- Implemented ExecutionResponseSection.vue to display execution results and handle file downloads. - Created useBase64Decoder.js for base64 decoding utilities. - Developed useChatToggle.js for managing chat panel state. - Added useErrorHandler.js for standardized error handling with toast notifications. - Introduced useFileDownload.js for various file download operations. - Created useFileProcessing.js for processing files, including zip extraction and content display. - Implemented usePolling.js for polling backend API for execution status. - Added useScenarioRating.js for managing scenario execution ratings. - Developed CSV export utilities in csvExport.js for generating and downloading CSV files. - Created formDataProcessor.js for processing and validating multiselect form data. - Implemented inputComponents.js for mapping input types to PrimeVue components. - Enhanced ScenarioExecHistory.vue to integrate new components and functionalities.
365 lines
10 KiB
Vue
365 lines
10 KiB
Vue
<script setup>
|
|
import ExecutionChatSection from '@/components/ExecutionChatSection.vue';
|
|
import ExecutionInputSection from '@/components/ExecutionInputSection.vue';
|
|
import ExecutionResponseSection from '@/components/ExecutionResponseSection.vue';
|
|
import { useChatToggle } from '@/composables/useChatToggle';
|
|
import { useFileDownload } from '@/composables/useFileDownload';
|
|
import { useFileProcessing } from '@/composables/useFileProcessing';
|
|
import { ScenarioService } from '@/service/ScenarioService.js';
|
|
import { LoadingStore } from '@/stores/LoadingStore.js';
|
|
import { ScenarioExecutionStore } from '@/stores/ScenarioExecutionStore.js';
|
|
import ProgressSpinner from 'primevue/progressspinner';
|
|
import { onMounted, ref } from 'vue';
|
|
|
|
// ============= Stores and Services =============
|
|
const loadingStore = LoadingStore();
|
|
const scenarioExecutionStore = ScenarioExecutionStore();
|
|
|
|
// ============= Composables =============
|
|
const { extractFiles, showFileContent } = useFileProcessing();
|
|
const { downloadFile, downloadCodegenieFile } = useFileDownload();
|
|
const { chatEnabled, enableChat, disableChat } = useChatToggle();
|
|
|
|
// ============= Reactive State =============
|
|
const scenario = ref({});
|
|
const exec_scenario = ref({});
|
|
const scenario_output = ref(null);
|
|
const inputs = ref(null);
|
|
const execution_id = ref(null);
|
|
const rating = ref(null);
|
|
const loading = ref(false);
|
|
const data_loaded = ref(false);
|
|
|
|
// ============= Lifecycle Hooks =============
|
|
onMounted(() => {
|
|
const execution = scenarioExecutionStore.getSelectedExecScenario;
|
|
|
|
if (execution) {
|
|
execution_id.value = execution.id;
|
|
} else {
|
|
const url = window.location.href;
|
|
execution_id.value = new URL(url).searchParams.get('id');
|
|
}
|
|
|
|
retrieveScenarioExec(execution_id.value);
|
|
});
|
|
|
|
// ============= Data Fetching Methods =============
|
|
const retrieveScenarioExec = async (id) => {
|
|
loading.value = true;
|
|
|
|
try {
|
|
const response = await ScenarioService.getScenarioExecutionById(id);
|
|
|
|
scenario.value = response.data.scenario;
|
|
exec_scenario.value = response.data;
|
|
data_loaded.value = true;
|
|
rating.value = response.data.rating;
|
|
scenario_output.value = response.data.execSharedMap.scenario_output;
|
|
inputs.value = response.data.scenarioExecutionInput.inputs;
|
|
|
|
// Handle file processing for MultiFileUpload scenarios
|
|
await handleFileProcessing();
|
|
} catch (error) {
|
|
console.error('Error retrieving scenario execution:', error);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
// ============= File Processing Methods =============
|
|
const handleFileProcessing = async () => {
|
|
if (inputs.value['MultiFileUpload'] && scenario.value.steps?.[0]?.attributes?.['codegenie_output_type']) {
|
|
try {
|
|
// Extract input files
|
|
await extractFiles(inputs.value['MultiFileUpload'], 'input');
|
|
|
|
const outputType = scenario.value.steps[0].attributes['codegenie_output_type'];
|
|
|
|
// Show file content for MARKDOWN or JSON types
|
|
if (outputType === 'MARKDOWN' || outputType === 'JSON') {
|
|
showFileContent(scenario_output.value, outputType);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error processing files:', error);
|
|
}
|
|
}
|
|
};
|
|
|
|
// ============= Download Methods =============
|
|
const handleDownloadFile = async (filePath) => {
|
|
await downloadFile(filePath, execution_id.value);
|
|
};
|
|
|
|
const handleDownloadCodegenieFile = (base64String) => {
|
|
downloadCodegenieFile(base64String, execution_id.value);
|
|
};
|
|
|
|
// ============= Rating Methods =============
|
|
const handleRatingUpdate = (newRating) => {
|
|
rating.value = newRating;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="scenario-history-container">
|
|
<!-- Loading Spinner -->
|
|
<div v-if="loading" class="loading-section">
|
|
<ProgressSpinner style="width: 50px; height: 50px" strokeWidth="3" fill="transparent" />
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div v-if="data_loaded">
|
|
<!-- Enhanced Header Section -->
|
|
<div class="header-section">
|
|
<div class="header-content">
|
|
<div class="header-icon">
|
|
<i class="pi pi-history" style="font-size: 1.36rem"></i>
|
|
</div>
|
|
<div class="header-text">
|
|
<h1 class="page-title">{{ scenario.name }}</h1>
|
|
<p class="page-subtitle">Execution History</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Execution Input Section -->
|
|
<div class="input-section">
|
|
<ExecutionInputSection :execution-id="execution_id" :inputs="inputs" :scenario="scenario" :exec-scenario="exec_scenario" :rating="rating" :show-rating="true" @download-file="handleDownloadFile" @rating-updated="handleRatingUpdate" />
|
|
</div>
|
|
|
|
<!-- Chat Toggle Button -->
|
|
<div v-if="scenario.chatEnabled && exec_scenario.latestStepStatus !== 'ERROR'" class="chat-toggle-section">
|
|
<div class="toggle-card">
|
|
<div v-if="!chatEnabled" class="button-group">
|
|
<Button label="Open Chat" @click="enableChat" size="large" iconPos="right" icon="pi pi-comments" severity="help" />
|
|
</div>
|
|
<div v-else class="button-group">
|
|
<Button label="Close Chat" @click="disableChat" size="large" iconPos="left" icon="pi pi-times" severity="help" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Workflow Response Section -->
|
|
<div v-if="!chatEnabled" class="response-section">
|
|
<ExecutionResponseSection
|
|
:scenario="scenario"
|
|
:exec-scenario="exec_scenario"
|
|
:scenario-output="scenario_output"
|
|
:execution-id="execution_id"
|
|
:is-loading="loadingStore.exectuion_loading && loadingStore.getExecIdLoading === execution_id"
|
|
mode="history"
|
|
@download-file="handleDownloadCodegenieFile"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Enhanced Chat Section -->
|
|
<div v-if="chatEnabled" class="chat-section">
|
|
<ExecutionChatSection :execution-id="execution_id" :scenario-name="scenario.name" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* Container */
|
|
.scenario-history-container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
/* Loading Section */
|
|
.loading-section {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 400px;
|
|
animation: fadeIn 0.5s ease-out;
|
|
}
|
|
|
|
/* Enhanced Header Section */
|
|
.header-section {
|
|
margin-bottom: 2rem;
|
|
animation: fadeInDown 0.5s ease-out;
|
|
}
|
|
|
|
.header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1.2rem;
|
|
padding: 1.36rem;
|
|
background: linear-gradient(135deg, #a100ff 0%, #7b00cc 100%);
|
|
border-radius: 16px;
|
|
box-shadow: 0 10px 30px rgba(161, 0, 255, 0.3);
|
|
color: white;
|
|
}
|
|
|
|
.header-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 48px;
|
|
height: 48px;
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border-radius: 50%;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.header-text {
|
|
flex: 1;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 1.8rem !important;
|
|
font-weight: 700 !important;
|
|
margin: 0 0 0.5rem 0 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
.page-subtitle {
|
|
font-size: 1.1rem;
|
|
margin: 0;
|
|
opacity: 0.95;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* Input Section */
|
|
.input-section {
|
|
margin-bottom: 2rem;
|
|
animation: fadeIn 0.5s ease-out;
|
|
}
|
|
|
|
.input-section :deep(.card),
|
|
.input-section :deep(.p-panel) {
|
|
background: white;
|
|
border-radius: 16px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.input-section :deep(.p-panel-header) {
|
|
background: linear-gradient(135deg, #a100ff15 0%, #7b00cc15 100%);
|
|
border-bottom: 2px solid #a100ff;
|
|
padding: 1.5rem 2rem;
|
|
}
|
|
|
|
/* Chat Toggle Section */
|
|
.chat-toggle-section {
|
|
margin-bottom: 2rem;
|
|
animation: fadeIn 0.5s ease-out;
|
|
}
|
|
|
|
.toggle-card {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: 1.5rem;
|
|
background: white;
|
|
border-radius: 16px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 1rem;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
|
|
.button-group :deep(button) {
|
|
min-width: 200px;
|
|
font-weight: 600;
|
|
justify-content: center;
|
|
}
|
|
|
|
/* Response Section */
|
|
.response-section {
|
|
margin-top: 2rem;
|
|
animation: fadeInUp 0.5s ease-out;
|
|
}
|
|
|
|
.response-section :deep(.card),
|
|
.response-section :deep(.p-panel) {
|
|
background: white;
|
|
border-radius: 16px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.response-section :deep(.p-panel-header) {
|
|
background: linear-gradient(135deg, #a100ff15 0%, #7b00cc15 100%);
|
|
border-bottom: 2px solid #a100ff;
|
|
padding: 1.5rem 2rem;
|
|
}
|
|
|
|
/* Chat Section */
|
|
.chat-section {
|
|
margin-top: 2rem;
|
|
animation: fadeInUp 0.5s ease-out;
|
|
}
|
|
|
|
/* Animations */
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes fadeInDown {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Responsive Design */
|
|
@media (max-width: 768px) {
|
|
.scenario-history-container {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.header-content {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.scenario-title {
|
|
font-size: 1.4rem !important;
|
|
}
|
|
|
|
.scenario-description {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.button-group {
|
|
width: 100%;
|
|
}
|
|
|
|
.button-group button {
|
|
flex: 1;
|
|
min-width: auto;
|
|
}
|
|
}
|
|
</style>
|