feat: Add Execution Response Section component and related composables for file handling and error management
- 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.
This commit is contained in:
364
src/views/pages/ScenarioExecHistory.vue
Normal file
364
src/views/pages/ScenarioExecHistory.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user