971 lines
36 KiB
Vue
971 lines
36 KiB
Vue
<template>
|
|
<div class="dash-container">
|
|
<div class="dash-header">
|
|
<h1 class="dash-title">Dashboard</h1>
|
|
</div>
|
|
|
|
<div v-if="loading" class="flex justify-center">
|
|
<ProgressSpinner style="width: 50px; height: 50px; margin-top: 50px" strokeWidth="3" fill="transparent" />
|
|
</div>
|
|
|
|
<div v-else class="dash-execution">
|
|
<div class="filter-section flex justify-start gap-4 items-end">
|
|
<div class="filter-item">
|
|
<label class="filter-label">Date Range:</label>
|
|
<Calendar v-model="dateRange" selectionMode="range" placeholder="Date Range" />
|
|
</div>
|
|
<!-- Dropdown account -->
|
|
<div class="filter-item">
|
|
<label class="filter-label">Account:</label>
|
|
<Dropdown v-model="selectedAccount" :options="uniqueAccounts" optionLabel="name" optionValue="id" placeholder="Select an Account" class="w-full md:w-20rem" />
|
|
</div>
|
|
|
|
<!-- MultiSelect progetti -->
|
|
<div class="filter-item">
|
|
<label class="filter-label">Project:</label>
|
|
<MultiSelect v-model="selectedProjects" :options="filteredProjects" optionLabel="fe_name" optionValue="id" display="chip" placeholder="Select Projects" :maxSelectedLabels="3" class="w-full" style="min-width: 100px" />
|
|
</div>
|
|
|
|
<!-- MultiSelect applicazione -->
|
|
<!-- <div class="filter-item">
|
|
<label class="filter-label">Application:</label>
|
|
<MultiSelect
|
|
v-model="selectedApplications"
|
|
:options="appOptions"
|
|
optionLabel="fe_name"
|
|
optionValue="id"
|
|
display="chip"
|
|
placeholder="All Applications"
|
|
:maxSelectedLabels="3"
|
|
class="w-full"
|
|
style="min-width: 100px;"
|
|
/>
|
|
</div>
|
|
|
|
<div class="filter-item" style="position: relative; display: inline-block; width: 100%; min-width: 100px;">
|
|
<label class="filter-label">User:</label>
|
|
<MultiSelect
|
|
v-model="selectedUsers"
|
|
:options="userOptions"
|
|
optionLabel="username"
|
|
filter
|
|
optionValue="id"
|
|
display="chip"
|
|
placeholder="All Users"
|
|
:maxSelectedLabels="3"
|
|
class="w-full"
|
|
style="min-width: 100px;"
|
|
:disabled="loadingUsers"
|
|
/>
|
|
<div v-if="loadingUsers" class="user-loading-overlay">
|
|
<i class="pi pi-spin pi-spinner" style="font-size: 1.2em;"></i> Loading users...
|
|
</div>
|
|
</div> -->
|
|
|
|
<!-- MultiSelect scenarios -->
|
|
<div class="filter-item">
|
|
<label class="filter-label">Scenario:</label>
|
|
<MultiSelect v-model="selectedScenarios" :options="scenarioOptions" optionLabel="name" optionValue="id" display="chip" placeholder="Select Scenarios" :maxSelectedLabels="3" class="w-full" style="min-width: 100px" />
|
|
</div>
|
|
|
|
<!-- <div class=" apply-button-container">
|
|
<label class="filter-label"> </label>
|
|
<Button label="Apply" @click="onApplyFilters" class="p-button-purple apply-button" />
|
|
</div>
|
|
</div> -->
|
|
|
|
<div class="apply-button-container">
|
|
<label class="filter-label"> </label>
|
|
<div class="button-group">
|
|
<Button label="Clear" @click="onClearFilters" class="p-button-outlined p-button-secondary clear-button" icon="pi pi-times" />
|
|
<Button label="Apply" @click="onApplyFilters" class="p-button-purple apply-button" icon="pi pi-search" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="showTables">
|
|
<!-- Tabella Aggregations -->
|
|
<div class="p-my-3 mb-8">
|
|
<DataTable
|
|
:value="dashboardResponse"
|
|
class="p-datatable-sm"
|
|
v-model:expandedRows="expandedRows"
|
|
@rowExpand="onRowExpand"
|
|
@rowCollapse="onRowCollapse"
|
|
:paginator="true"
|
|
:rows="10"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} records"
|
|
:rowsPerPageOptions="[10, 15, 20, 50, 100]"
|
|
dataKey="id"
|
|
:rowHover="true"
|
|
rowGroupMode="subheader"
|
|
:sortOrder="1"
|
|
filterDisplay="menu"
|
|
responsiveLayout="scroll"
|
|
>
|
|
<template #header>
|
|
<div class="execution-section flex justify-between items-center">
|
|
<h3 class="text-xl font-bold mt-6">Executions Stats</h3>
|
|
<Button label="Export" icon="pi pi-download" class="p-button-purple" @click="exportAggregations" />
|
|
</div>
|
|
</template>
|
|
|
|
<Column field="projectName" header="Project" sortable>
|
|
<template #body="slotProps">
|
|
<span>{{ slotProps.data.projectName }}</span>
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="scenarioInternalName" header="Scenario" sortable>
|
|
<template #body="slotProps">
|
|
<span>{{ slotProps.data.scenarioInternalName }}</span>
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="totalExecutions" header="Executions" sortable>
|
|
<template #body="slotProps">
|
|
<span>{{ slotProps.data.totalExecutions }}</span>
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="totalTokens" header="Tokens" sortable>
|
|
<template #body="slotProps">
|
|
<span>{{ slotProps.data.totalTokens }}</span>
|
|
</template>
|
|
</Column>
|
|
|
|
<!-- <Column field="totalChatInteractions" header="Chat interactions" sortable>
|
|
<template #body="slotProps">
|
|
<span>{{ slotProps.data.totalChatInteractions }}</span>
|
|
</template>
|
|
</Column> -->
|
|
</DataTable>
|
|
</div>
|
|
|
|
<!-- Tabella Execution List -->
|
|
<div class="mb-8">
|
|
<DataTable
|
|
:value="executions"
|
|
ref="executionTable"
|
|
:totalRecords="executions.length"
|
|
v-model:expandedRows="expandedRows"
|
|
@rowExpand="onRowExpand"
|
|
@rowCollapse="onRowCollapse"
|
|
:paginator="true"
|
|
:rows="10"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} records"
|
|
:rowsPerPageOptions="[10, 15, 20, 50, 100]"
|
|
dataKey="id"
|
|
:rowHover="true"
|
|
rowGroupMode="subheader"
|
|
:sortOrder="1"
|
|
filterDisplay="menu"
|
|
responsiveLayout="scroll"
|
|
>
|
|
<template #header>
|
|
<div class="execution-section flex justify-between items-center">
|
|
<h3 class="text-xl font-bold mt-6">Execution List</h3>
|
|
<Button label="Export" icon="pi pi-download" class="p-button-purple" @click="exportExecutions" />
|
|
</div>
|
|
</template>
|
|
|
|
<Column field="startDate" header="StartDate" sortable :exportFunction="(row) => moment(row.startDate || row.data?.startDate || row.data).format('DD-MM-YYYY HH:mm:ss')">
|
|
<template #body="slotProps">
|
|
{{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="executedByUsername" header="User" sortable :exportFunction="(row) => row.executedByUsername || ''">
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.executedByUsername }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="execSharedMap.user_input.selected_project"
|
|
header="Project"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
try {
|
|
return row.execSharedMap?.user_input?.selected_project || '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.execSharedMap?.user_input?.selected_project || '' }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="execSharedMap.user_input.selected_application"
|
|
header="App"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
try {
|
|
return row.execSharedMap?.user_input?.selected_application || '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.execSharedMap?.user_input?.selected_application || '' }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="scenario.name"
|
|
header="Scenario Name"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
try {
|
|
return row.scenario?.name || '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.scenario?.name || '' }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="scenario.category"
|
|
header="Scenario Type"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
try {
|
|
return row.scenario?.category || '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
<div class="flex items-center gap-2">
|
|
{{ slotProps.data.scenario?.category || '' }}
|
|
<i class="pi pi-info-circle text-violet-600 cursor-pointer" v-tooltip="'Tipo di scenario: ' + (slotProps.data.scenario?.category || '')"></i>
|
|
</div>
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="scenario.aiModel.model"
|
|
header="Model"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
try {
|
|
return row.scenario?.aiModel?.model || '';
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.scenario?.aiModel?.model || '' }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="usedTokens" header="Token" sortable :exportFunction="(row) => row.usedTokens || ''">
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.usedTokens || '' }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column field="rating" header="Rating" sortable :exportFunction="(row) => row.rating || ''">
|
|
<template #body="slotProps">
|
|
<Rating :modelValue="slotProps.data.rating" :stars="5" :readonly="true" />
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="elapsed"
|
|
header="Elapsed"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
if (!row.endDate || !row.startDate) return '';
|
|
const elapsed = (new Date(row.endDate) - new Date(row.startDate)) / 1000;
|
|
return formatElapsed(elapsed);
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
{{ formatElapsed((new Date(slotProps.data.endDate) - new Date(slotProps.data.startDate)) / 1000) }}
|
|
</template>
|
|
</Column>
|
|
|
|
<Column
|
|
field="response"
|
|
header="Response"
|
|
sortable
|
|
:exportFunction="
|
|
(row) => {
|
|
if (row.latestStepStatus === 'ERROR') return 'N';
|
|
if (row.status === 'ERROR') return 'N';
|
|
return 'Y';
|
|
}
|
|
"
|
|
>
|
|
<template #body="slotProps">
|
|
{{ slotProps.data.latestStepStatus === 'ERROR' ? 'N' : 'Y' }}
|
|
</template>
|
|
</Column>
|
|
|
|
<template #empty>
|
|
<tr>
|
|
<td :colspan="12" class="text-center">No execution found</td>
|
|
</tr>
|
|
</template>
|
|
</DataTable>
|
|
</div>
|
|
|
|
<!-- Tabella Chat List -->
|
|
<!-- <div class="mb-8">
|
|
<DataTable
|
|
:value="paginatedChats"
|
|
ref="chatTable"
|
|
:paginator="true"
|
|
:rows="rowsPerPage"
|
|
:totalRecords="chats.length"
|
|
:first="(currentPageExecutions - 1) * rowsPerPage"
|
|
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
|
|
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} records"
|
|
:sortOrder="1" filterDisplay="menu"
|
|
tableStyle="min-width: 70rem"
|
|
@page="onPage"
|
|
@sort="onSort"
|
|
removableSort
|
|
responsiveLayout="scroll"
|
|
>
|
|
<template #header>
|
|
<div class="chat-section flex justify-between items-center mt-4">
|
|
<h3 class="text-xl font-bold mt-6">Chat List</h3>
|
|
<Button label="Export" icon="pi pi-download" class="p-button-purple" @click="exportChats" />
|
|
</div>
|
|
</template>
|
|
|
|
<Column field="utente" header="User" sortable></Column>
|
|
<Column field="chat" header="Chat" sortable></Column>
|
|
<Column field="currentMessage" header="Current Message" sortable></Column>
|
|
<Column field="scenarioChat" header="Scenario Chat" sortable></Column>
|
|
<Column field="scenario" header="Scenario Name" sortable></Column>
|
|
|
|
<template #empty>
|
|
<tr>
|
|
<td :colspan="5" class="text-center">No chat found</td>
|
|
</tr>
|
|
</template>
|
|
</DataTable>
|
|
</div> -->
|
|
</div>
|
|
|
|
<div v-if="loading_data" class="flex justify-center">
|
|
<ProgressSpinner style="width: 30px; height: 30px; margin: 30px" strokeWidth="6" fill="transparent" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { DashboardScenarioStore } from '@/stores/dashboard/DashboardScenarioStore';
|
|
import { UserPrefStore } from '@/stores/UserPrefStore';
|
|
import moment from 'moment';
|
|
import { Rating } from 'primevue';
|
|
import ProgressSpinner from 'primevue/progressspinner';
|
|
import { useToast } from 'primevue/usetoast';
|
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
|
|
const loading = ref(false);
|
|
const loading_data = ref(false);
|
|
const showTables = ref(false);
|
|
|
|
const dateRange = ref(null);
|
|
const selectedAccount = ref(null);
|
|
const selectedProjects = ref([]);
|
|
|
|
const executions = ref([]);
|
|
const chats = ref([]);
|
|
|
|
const currentPageExecutions = ref(1);
|
|
const currentPageChats = ref(1);
|
|
const rowsPerPage = ref(100);
|
|
|
|
const executionTable = ref(null);
|
|
const chatTable = ref(null);
|
|
const router = useRouter();
|
|
const toast = useToast();
|
|
const dashboardScenarioStore = DashboardScenarioStore();
|
|
console.log(dashboardScenarioStore);
|
|
const userPrefStore = UserPrefStore();
|
|
const filteredProjectOption = ref([]);
|
|
const projectOptions = ref([]);
|
|
const filteredProjectMulti = ref([]);
|
|
const selectedApplications = ref([]);
|
|
const selectedUsers = ref([]);
|
|
const appOptions = ref([]);
|
|
const userOptions = ref([]);
|
|
const selectedScenarios = ref([]);
|
|
const scenarioOptions = ref([]);
|
|
const filteredProjects = ref([]);
|
|
const dashboardResponse = ref(null);
|
|
const totalExecutions = ref(null);
|
|
const totalTokens = ref(null);
|
|
const loadingUsers = ref(false);
|
|
|
|
function formatElapsed(seconds) {
|
|
const mins = Math.floor(seconds / 60);
|
|
const secs = Math.floor(seconds % 60);
|
|
return mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
}
|
|
|
|
// Computed accounts unici da projectOptions
|
|
const uniqueAccounts = computed(() => {
|
|
const lstProjects = userPrefStore.user?.lstProjects || [];
|
|
const accounts = [...new Set(lstProjects.map((p) => p.account).filter(Boolean))];
|
|
console.log('Unique accounts:', accounts);
|
|
return accounts.map((acc) => ({ name: acc, id: acc })); // usa anche `id` se serve per la picklist
|
|
});
|
|
|
|
// Filter progetti per account selezionato
|
|
// const filteredProjects = computed(() => {
|
|
// if (!projectOptions.value) return []
|
|
// let filteredList = projectOptions.value
|
|
|
|
// if (selectedAccount.value) {
|
|
// filteredList = filteredList.filter(p => p.account === selectedAccount.value.name)
|
|
// }
|
|
|
|
// return filteredList;
|
|
// })
|
|
|
|
const onPage = (event) => {
|
|
currentPageExecutions.value = event.page + 1;
|
|
// currentPageChats.value = event.page + 1
|
|
console.log('Page changed:', event);
|
|
};
|
|
|
|
const exportExecutions = () => {
|
|
if (!executions.value.length) {
|
|
toast.add({
|
|
severity: 'warn',
|
|
summary: 'Warning',
|
|
detail: 'No data to export'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Converte i dati Proxy in oggetti normali per l'export
|
|
const dataForExport = executions.value.map((row) => ({
|
|
StartDate: moment(row.startDate).format('DD-MM-YYYY HH:mm:ss'),
|
|
User: row.executedByUsername || '',
|
|
Project: row.execSharedMap?.user_input?.selected_project || '',
|
|
App: row.execSharedMap?.user_input?.selected_application || '',
|
|
'Scenario Name': row.scenario?.name || '',
|
|
'Scenario Type': row.scenario?.category || '',
|
|
Model: row.scenario?.aiModel?.model || '',
|
|
Token: row.usedTokens || '',
|
|
Rating: row.rating || '',
|
|
Elapsed: row.endDate && row.startDate ? formatElapsed((new Date(row.endDate) - new Date(row.startDate)) / 1000) : '',
|
|
Response: row.latestStepStatus === 'ERROR' ? 'N' : 'Y'
|
|
}));
|
|
|
|
// Crea il CSV manualmente
|
|
const headers = Object.keys(dataForExport[0]);
|
|
const csvContent = [headers.join(';'), ...dataForExport.map((row) => headers.map((header) => `"${row[header]}"`).join(';'))].join('\n');
|
|
|
|
// Download del file
|
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
const link = document.createElement('a');
|
|
const url = URL.createObjectURL(blob);
|
|
link.setAttribute('href', url);
|
|
link.setAttribute('download', `Executions_${moment().format('YYYY-MM-DD_mm-ss')}.csv`);
|
|
link.style.visibility = 'hidden';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
};
|
|
|
|
const exportAggregations = () => {
|
|
if (!dashboardResponse.value || dashboardResponse.value.length === 0) {
|
|
toast.add({
|
|
severity: 'warn',
|
|
summary: 'Warning',
|
|
detail: 'No aggregation data to export'
|
|
});
|
|
return;
|
|
}
|
|
|
|
const dataForExport = dashboardResponse.value.map((item) => ({
|
|
'Project Name': item.projectName,
|
|
'Scenario Name': item.scenarioInternalName,
|
|
'Total Executions': item.totalExecutions,
|
|
'Total Tokens': item.totalTokens
|
|
}));
|
|
|
|
const headers = Object.keys(dataForExport[0]);
|
|
const csvContent = [headers.join(';'), ...dataForExport.map((row) => headers.map((header) => `"${row[header]}"`).join(';'))].join('\n');
|
|
|
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
const link = document.createElement('a');
|
|
const url = URL.createObjectURL(blob);
|
|
link.setAttribute('href', url);
|
|
link.setAttribute('download', `Aggregations_${moment().format('DD-MM-YYYY_HH-mm-ss')}.csv`);
|
|
link.style.visibility = 'hidden';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
};
|
|
|
|
// const exportChats = () => {
|
|
// if (chatTable.value) chatTable.value.exportCSV()
|
|
// }
|
|
|
|
// Caricamento dati con filtri
|
|
const loadData = async () => {
|
|
loading_data.value = true;
|
|
try {
|
|
const filterParams = {
|
|
// Date
|
|
dateFrom: dateRange.value?.[0] ? dateRange.value[0].toISOString().split('T')[0] : null,
|
|
dateTo: dateRange.value?.[1] ? dateRange.value[1].toISOString().split('T')[0] : null,
|
|
|
|
account: selectedAccount.value || null,
|
|
|
|
// Progetti
|
|
projectIds: selectedProjects.value.length ? selectedProjects.value.map((id) => String(id)) : [],
|
|
projectNames: selectedProjects.value.length
|
|
? selectedProjects.value
|
|
.map((id) => {
|
|
const project = projectOptions.value.find((p) => p.id === id);
|
|
return project ? project.fe_name : null;
|
|
})
|
|
.filter(Boolean)
|
|
: [],
|
|
|
|
// // Applicazioni
|
|
// applicationIds: selectedApplications.value.length
|
|
// ? selectedApplications.value.map(id => String(id))
|
|
// : [],
|
|
// applicationNames: selectedApplications.value.length
|
|
// ? selectedApplications.value
|
|
// .map(id => {
|
|
// const app = appOptions.value.find(a => a.id === id)
|
|
// return app ? app.fe_name : null
|
|
// })
|
|
// .filter(Boolean)
|
|
// : [],
|
|
|
|
// // Utenti
|
|
// userIds: selectedUsers.value.length
|
|
// ? selectedUsers.value.map(id => String(id))
|
|
// : [],
|
|
// userNames: selectedUsers.value.length
|
|
// ? selectedUsers.value
|
|
// .map(id => {
|
|
// const user = userOptions.value.find(u => u.id === id)
|
|
// return user ? user.username : null
|
|
// })
|
|
// .filter(Boolean)
|
|
// : [],
|
|
|
|
// Scenari
|
|
scenarioIds: selectedScenarios.value.length ? selectedScenarios.value.map((id) => String(id)) : [],
|
|
scenarioNames: selectedScenarios.value.length
|
|
? selectedScenarios.value
|
|
.map((id) => {
|
|
const scenario = scenarioOptions.value.find((s) => s.id === id);
|
|
return scenario ? scenario.name : null;
|
|
})
|
|
.filter(Boolean)
|
|
: []
|
|
};
|
|
|
|
console.log('Filter parameters:', filterParams);
|
|
|
|
const chatParams = {
|
|
dateFrom: dateRange.value?.[0] ? dateRange.value[0].toISOString().split('T')[0] : null,
|
|
dateTo: dateRange.value?.[1] ? dateRange.value[1].toISOString().split('T')[0] : null,
|
|
projectIds: selectedProjects.value.length ? selectedProjects.value.map((id) => String(id)) : [],
|
|
projectNames: selectedProjects.value.length
|
|
? selectedProjects.value
|
|
.map((id) => {
|
|
const project = projectOptions.value.find((p) => p.id === id);
|
|
return project ? project.fe_name : null;
|
|
})
|
|
.filter(Boolean)
|
|
: [],
|
|
scenarioIds: selectedScenarios.value.length ? selectedScenarios.value.map((id) => String(id)) : []
|
|
};
|
|
|
|
console.log('Chat parameters:', chatParams);
|
|
|
|
await dashboardScenarioStore.loadExecutions(filterParams);
|
|
await dashboardScenarioStore.loadExecutionsStats(filterParams);
|
|
executions.value = dashboardScenarioStore.executions;
|
|
console.log('Executions:', executions);
|
|
dashboardResponse.value = dashboardScenarioStore.dashboardResponse;
|
|
console.log('Execution Stats:', dashboardResponse);
|
|
} catch (error) {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Error while loading data'
|
|
});
|
|
console.error(error);
|
|
} finally {
|
|
loading_data.value = false;
|
|
}
|
|
};
|
|
|
|
const tableData = computed(() => {
|
|
const data = dashboardResponse?.value;
|
|
if (!data || (!data.totalExecutions && !data.totalTokens)) {
|
|
return [];
|
|
}
|
|
return [
|
|
{
|
|
project: data.project,
|
|
scenario: data.scenario,
|
|
totalExecutions: data.totalExecutions,
|
|
totalTokens: data.totalTokens,
|
|
totalChatInteractions: data.totalChatInteractions
|
|
}
|
|
];
|
|
});
|
|
|
|
// Validazione range data max 30 giorni
|
|
const validateDateRange = () => {
|
|
if (dateRange.value && dateRange.value[0] && dateRange.value[1]) {
|
|
const start = new Date(dateRange.value[0]);
|
|
const end = new Date(dateRange.value[1]);
|
|
const diffInDays = (end - start) / (1000 * 60 * 60 * 24);
|
|
|
|
if (diffInDays > 30) {
|
|
toast.add({
|
|
severity: 'warn',
|
|
summary: 'Range Too Wide!',
|
|
detail: 'You can select a maximum of 30 days',
|
|
life: 5000
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Applica filtro e carica dati
|
|
const onApplyFilters = async () => {
|
|
if (!validateDateRange()) return;
|
|
currentPageExecutions.value = 1;
|
|
showTables.value = false;
|
|
await loadData();
|
|
showTables.value = true;
|
|
};
|
|
|
|
// Funzione per cancellare filtri
|
|
const onClearFilters = () => {
|
|
dateRange.value = null;
|
|
selectedAccount.value = null;
|
|
selectedProjects.value = [];
|
|
selectedApplications.value = [];
|
|
selectedUsers.value = [];
|
|
selectedScenarios.value = [];
|
|
|
|
showTables.value = false;
|
|
};
|
|
|
|
// Al montaggio carica progetti
|
|
onMounted(async () => {
|
|
loading.value = true;
|
|
try {
|
|
const today = new Date();
|
|
const sevenDaysAgo = new Date();
|
|
sevenDaysAgo.setDate(today.getDate() - 7);
|
|
dateRange.value = [sevenDaysAgo, today];
|
|
await userPrefStore.fetchUserData();
|
|
const lstProjects = userPrefStore.user?.lstProjects || [];
|
|
console.log('Projects loaded:', lstProjects);
|
|
|
|
projectOptions.value = lstProjects.filter((p) => p.fe_name).map((p) => ({ ...p }));
|
|
selectedProjects.value = [];
|
|
|
|
// Popola appOptions in base alle applicazioni nei progetti
|
|
const applicationMap = new Map();
|
|
|
|
lstProjects.forEach((project) => {
|
|
if (Array.isArray(project.lstApplications)) {
|
|
project.lstApplications.forEach((app) => {
|
|
// Evita duplicati usando la mappa con ID come chiave
|
|
if (!applicationMap.has(app.id)) {
|
|
applicationMap.set(app.id, app);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
appOptions.value = Array.from(applicationMap.values());
|
|
|
|
// await dashboardScenarioStore.loadChats() //per caricare le chat
|
|
} catch (e) {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Error loading data',
|
|
life: 3000
|
|
});
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
});
|
|
|
|
// watch(selectedProjects, (newSelectedProjects) => {
|
|
// if (newSelectedProjects && newSelectedProjects.length > 0) {
|
|
// filterAppsByProjects(newSelectedProjects)
|
|
// filterUsersByProjects(newSelectedProjects)
|
|
// } else {
|
|
// // Nessun progetto selezionato, svuota le app e gli utenti
|
|
// appOptions.value = []
|
|
// userOptions.value = []
|
|
// }
|
|
// })
|
|
|
|
watch(selectedAccount, (newAccount) => {
|
|
if (newAccount) {
|
|
filteredProjects.value = userPrefStore.user?.lstProjects.filter((p) => p.account === newAccount);
|
|
const appMap = new Map();
|
|
console.log('Filtered projects:', filteredProjects.value);
|
|
|
|
filteredProjects.value.forEach((project, index) => {
|
|
if (Array.isArray(project.lstApplications)) {
|
|
project.lstApplications.forEach((app) => {
|
|
if (!appMap.has(app.id)) {
|
|
appMap.set(app.id, app);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
appOptions.value = Array.from(appMap.values());
|
|
loadScenariosByAccount(newAccount);
|
|
} else {
|
|
appOptions.value = [];
|
|
}
|
|
});
|
|
|
|
//watch per app e utenti selezionati
|
|
// watch(selectedApplications, (newApps) => {
|
|
// console.log('Applicazioni selezionate:', newApps)
|
|
// })
|
|
|
|
// watch(selectedUsers, (newUsers) => {
|
|
// if (newUsers && newUsers.length > 0) {
|
|
// console.log('Utenti selezionati:', newUsers)
|
|
// } else {
|
|
// console.log('Nessun utente selezionato')
|
|
// }
|
|
// })
|
|
|
|
watch(selectedScenarios, (newScenarios) => {
|
|
if (newScenarios && newScenarios.length > 0) {
|
|
console.log('Selected scenarios:', newScenarios);
|
|
} else {
|
|
console.log('No scenario selected');
|
|
}
|
|
});
|
|
|
|
//Nuova funzione
|
|
function filterAppsByProjects(projectIds) {
|
|
const lstProjects = userPrefStore.user?.lstProjects || [];
|
|
appOptions.value = lstProjects.filter((project) => projectIds.includes(project.id)).flatMap((project) => project.lstApplications || []);
|
|
console.log('App options:', appOptions.value);
|
|
}
|
|
|
|
//funzione User
|
|
async function filterUsersByProjects(projectIds) {
|
|
try {
|
|
loadingUsers.value = true;
|
|
if (!projectIds || projectIds.length === 0) {
|
|
userOptions.value = [];
|
|
loadingUsers.value = false;
|
|
return;
|
|
}
|
|
const projectIdsArr = projectIds.join(',');
|
|
|
|
const response = await dashboardScenarioStore.loadUsers(projectIdsArr);
|
|
console.log('Users:', response.data);
|
|
userOptions.value = response.data.map((user) => ({ ...user }));
|
|
} catch (error) {
|
|
console.error('Errore nel caricamento utenti:', error);
|
|
userOptions.value = [];
|
|
} finally {
|
|
loadingUsers.value = false;
|
|
}
|
|
}
|
|
console.log('Selected scenarios:', selectedScenarios);
|
|
//function per Scenarios
|
|
async function loadScenariosByAccount(selectedAccount) {
|
|
try {
|
|
await dashboardScenarioStore.loadScenarios(selectedAccount);
|
|
scenarioOptions.value = dashboardScenarioStore.allScenarios.map((scenario) => ({ ...scenario }));
|
|
|
|
console.log('Scenarios loaded:', scenarioOptions.value);
|
|
} catch (error) {
|
|
console.error('Errore nel caricamento scenari:', error);
|
|
scenarioOptions.value = [];
|
|
}
|
|
}
|
|
|
|
function filterProjectsByAccount(account) {
|
|
const lstProjects = userPrefStore.user?.lstProjects || [];
|
|
|
|
// Filtra i progetti associati all'account selezionato
|
|
const filteredProjectMulti = lstProjects.filter((p) => p.account === account.name || p.account === account);
|
|
console.log('Filtered projects:', filteredProjectMulti);
|
|
projectOptions.value = filteredProjectMulti.map((p) => ({ ...p }));
|
|
selectedProjects.value = projectOptions.value.map((p) => p.id);
|
|
|
|
// Reset delle applicazioni e utenti quando cambia l'account
|
|
appOptions.value = [];
|
|
userOptions.value = [];
|
|
selectedApplications.value = [];
|
|
selectedUsers.value = [];
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.dash-container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 0 20px;
|
|
}
|
|
.dash-header {
|
|
margin-bottom: 2rem;
|
|
padding-top: 1rem;
|
|
}
|
|
.dash-title {
|
|
font-size: 2rem;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
}
|
|
.dash-execution {
|
|
padding: 0;
|
|
}
|
|
|
|
.filter-section {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
grid-template-rows: auto auto auto;
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
padding-bottom: 1rem;
|
|
}
|
|
|
|
.filter-label {
|
|
font-weight: bold;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.uniform-input {
|
|
width: 100% !important;
|
|
height: 42px !important;
|
|
}
|
|
|
|
.execution-section,
|
|
.chat-section {
|
|
margin-top: 1rem;
|
|
margin-bottom: 30px;
|
|
}
|
|
.mt-4 {
|
|
margin-top: 20px;
|
|
}
|
|
.p-paginator-rpp-options {
|
|
display: none !important;
|
|
}
|
|
|
|
.filter-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: relative;
|
|
}
|
|
|
|
/* Posizionamento specifico per ogni elemento */
|
|
.filter-item:nth-child(1) {
|
|
grid-column: 1;
|
|
grid-row: 1;
|
|
}
|
|
|
|
.filter-item:nth-child(2) {
|
|
grid-column: 2;
|
|
grid-row: 1;
|
|
}
|
|
|
|
.filter-item:nth-child(3) {
|
|
grid-column: 1;
|
|
grid-row: 2;
|
|
}
|
|
|
|
.filter-item:nth-child(4) {
|
|
grid-column: 2;
|
|
grid-row: 2;
|
|
}
|
|
|
|
.apply-button-container {
|
|
grid-column: 1 / -1;
|
|
grid-row: 3;
|
|
justify-self: end;
|
|
align-self: end;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.filter-label {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.user-loading-overlay {
|
|
position: absolute;
|
|
bottom: -22px;
|
|
left: 0;
|
|
font-size: 0.85em;
|
|
color: gray;
|
|
display: flex;
|
|
align-items: center;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.clear-button {
|
|
background-color: transparent !important;
|
|
border: 1px solid #6b7280 !important;
|
|
color: #6b7280 !important;
|
|
}
|
|
|
|
.clear-button:hover {
|
|
background-color: #f3f4f6 !important;
|
|
border-color: #4b5563 !important;
|
|
color: #4b5563 !important;
|
|
}
|
|
</style>
|