-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Change {{ index + 1 }}
+
+ Class RE
+ Actual Class Code
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/src/components/CiaSingleImpactView.vue b/src/components/CiaSingleImpactView.vue
index 98f0d9c..d6ee9f0 100644
--- a/src/components/CiaSingleImpactView.vue
+++ b/src/components/CiaSingleImpactView.vue
@@ -9,7 +9,7 @@
-
+
@@ -25,14 +25,14 @@
-
+
-
-
+
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ExecutionInputSection.vue b/src/components/ExecutionInputSection.vue
new file mode 100644
index 0000000..b0b2cd0
--- /dev/null
+++ b/src/components/ExecutionInputSection.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+ Execution Input for ID {{ executionId }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ExecutionInputTable.vue b/src/components/ExecutionInputTable.vue
new file mode 100644
index 0000000..2d2e23d
--- /dev/null
+++ b/src/components/ExecutionInputTable.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+ | Field |
+ Value |
+
+
+
+
+ |
+ {{ getDisplayLabel(index) }}
+ |
+
+
+
+ {{ filteredInputs.SingleFileUpload.replace(/\\/g, '/').split('/').pop() }}
+
+
+
+
+ {{ input.replace(/\\/g, '/').split('/').pop() }}
+
+
+
+ {{ input }}
+ |
+
+
+
+
+
+
+
diff --git a/src/components/ExecutionResponsePanel.vue b/src/components/ExecutionResponsePanel.vue
new file mode 100644
index 0000000..9786d0e
--- /dev/null
+++ b/src/components/ExecutionResponsePanel.vue
@@ -0,0 +1,263 @@
+
+
+
+
+
+
+ Workflow Response
+
+
+
+
+
+
+
+
+
+
Error: {{ errorText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ sf_document-{{ executionId }}
+
+
+
+
+
+
+
+
+
+
+
{{ displayFileContent }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ExecutionResponseSection.vue b/src/components/ExecutionResponseSection.vue
new file mode 100644
index 0000000..cbee78b
--- /dev/null
+++ b/src/components/ExecutionResponseSection.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
diff --git a/src/components/ExecutionTimer.vue b/src/components/ExecutionTimer.vue
new file mode 100644
index 0000000..9bc33be
--- /dev/null
+++ b/src/components/ExecutionTimer.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+ {{ message }}
+
+
Starting execution...
+
+
+
+
+
Time elapsed:
+
{{ elapsedTime }}
+
+
+
+
+
diff --git a/src/components/MarkdownViewer.vue b/src/components/MarkdownViewer.vue
new file mode 100644
index 0000000..ea499bc
--- /dev/null
+++ b/src/components/MarkdownViewer.vue
@@ -0,0 +1,1121 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/OldExecutionResponsePanel.vue b/src/components/OldExecutionResponsePanel.vue
new file mode 100644
index 0000000..0511f11
--- /dev/null
+++ b/src/components/OldExecutionResponsePanel.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+ Workflow response
+
+
+
+
+
+
+
+
+
+
+
Error: {{ execScenario.latestStepOutput }}
+
+
+
Error: Execution failed.
+
+
+
+
+
+
+
+
+
+
+
+ -
+ sf_document-{{ executionId }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ScenarioFileUpload.vue b/src/components/ScenarioFileUpload.vue
new file mode 100644
index 0000000..814a31e
--- /dev/null
+++ b/src/components/ScenarioFileUpload.vue
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Name |
+ Dimension |
+ Status |
+ Actions |
+
+
+
+
+ | {{ file.name }} |
+ {{ formatSize(file.size) }} |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
Drag and drop files here to upload.
+
+
+
+
+
+
diff --git a/src/components/SingleClassViewer.vue b/src/components/SingleClassViewer.vue
index 97c54c7..2804f1a 100644
--- a/src/components/SingleClassViewer.vue
+++ b/src/components/SingleClassViewer.vue
@@ -1,17 +1,17 @@
-
-
+
+
Class Code
Class RE
Method List
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
@@ -44,9 +44,9 @@
-
+
Method Code ( No reverse engineering available )
-
+
Method Explaination
-
+
@@ -86,7 +86,7 @@
-
+
@@ -96,19 +96,19 @@
-
+
+
+
+
+
+
+ Workflow Response
+
+
+
+
+
+
+
+
+
+
Error: {{ errorMessage }}
+
+
+
Error: Execution failed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ sf_document-{{ execId }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/composables/useBase64Decoder.js b/src/composables/useBase64Decoder.js
new file mode 100644
index 0000000..f4ea674
--- /dev/null
+++ b/src/composables/useBase64Decoder.js
@@ -0,0 +1,57 @@
+/**
+ * Composable for base64 decoding operations
+ * @returns {Object} Base64 utilities
+ */
+export function useBase64Decoder() {
+ /**
+ * Decode base64 string to Uint8Array
+ */
+ const decodeToBytes = (base64String) => {
+ try {
+ 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);
+ }
+
+ return bytes;
+ } catch (error) {
+ console.error('Error decoding base64 to bytes:', error);
+ throw new Error('Failed to decode base64 string');
+ }
+ };
+
+ /**
+ * Decode base64 string to text
+ */
+ const decodeToText = (base64String) => {
+ try {
+ const bytes = decodeToBytes(base64String);
+ return new TextDecoder().decode(bytes);
+ } catch (error) {
+ console.error('Error decoding base64 to text:', error);
+ throw new Error('Failed to decode base64 to text');
+ }
+ };
+
+ /**
+ * Decode base64 string to Blob
+ */
+ const decodeToBlob = (base64String, mimeType = 'application/octet-stream') => {
+ try {
+ const bytes = decodeToBytes(base64String);
+ return new Blob([bytes], { type: mimeType });
+ } catch (error) {
+ console.error('Error decoding base64 to blob:', error);
+ throw new Error('Failed to decode base64 to blob');
+ }
+ };
+
+ return {
+ decodeToBytes,
+ decodeToText,
+ decodeToBlob
+ };
+}
diff --git a/src/composables/useChatToggle.js b/src/composables/useChatToggle.js
new file mode 100644
index 0000000..70e0cf9
--- /dev/null
+++ b/src/composables/useChatToggle.js
@@ -0,0 +1,37 @@
+import { ref } from 'vue';
+
+/**
+ * Composable for managing chat panel toggle state
+ * @returns {Object} Chat toggle utilities and state
+ */
+export function useChatToggle() {
+ const chatEnabled = ref(false);
+
+ /**
+ * Enable chat panel
+ */
+ const enableChat = () => {
+ chatEnabled.value = true;
+ };
+
+ /**
+ * Disable chat panel
+ */
+ const disableChat = () => {
+ chatEnabled.value = false;
+ };
+
+ /**
+ * Toggle chat panel
+ */
+ const toggleChat = () => {
+ chatEnabled.value = !chatEnabled.value;
+ };
+
+ return {
+ chatEnabled,
+ enableChat,
+ disableChat,
+ toggleChat
+ };
+}
diff --git a/src/composables/useErrorHandler.js b/src/composables/useErrorHandler.js
new file mode 100644
index 0000000..1852a25
--- /dev/null
+++ b/src/composables/useErrorHandler.js
@@ -0,0 +1,74 @@
+import { useToast } from 'primevue/usetoast';
+
+/**
+ * Composable for standardized error handling with toast notifications
+ * @returns {Object} Error handling utilities
+ */
+export function useErrorHandler() {
+ const toast = useToast();
+
+ /**
+ * Show success toast
+ */
+ const showSuccess = (summary, detail, life = 3000) => {
+ toast.add({
+ severity: 'success',
+ summary,
+ detail,
+ life
+ });
+ };
+
+ /**
+ * Show error toast
+ */
+ const showError = (summary, detail, life = 3000) => {
+ toast.add({
+ severity: 'error',
+ summary,
+ detail,
+ life
+ });
+ };
+
+ /**
+ * Show warning toast
+ */
+ const showWarning = (summary, detail, life = 3000) => {
+ toast.add({
+ severity: 'warn',
+ summary,
+ detail,
+ life
+ });
+ };
+
+ /**
+ * Show info toast
+ */
+ const showInfo = (summary, detail, life = 3000) => {
+ toast.add({
+ severity: 'info',
+ summary,
+ detail,
+ life
+ });
+ };
+
+ /**
+ * Handle error with logging and toast
+ */
+ const handleError = (error, userMessage = 'An error occurred', consoleMessage = null) => {
+ console.error(consoleMessage || userMessage, error);
+ showError('Error', userMessage);
+ };
+
+ return {
+ toast,
+ showSuccess,
+ showError,
+ showWarning,
+ showInfo,
+ handleError
+ };
+}
diff --git a/src/composables/useFileDownload.js b/src/composables/useFileDownload.js
new file mode 100644
index 0000000..17af765
--- /dev/null
+++ b/src/composables/useFileDownload.js
@@ -0,0 +1,141 @@
+import { ScenarioExecutionStore } from '@/stores/ScenarioExecutionStore';
+import { ref } from 'vue';
+import { useBase64Decoder } from './useBase64Decoder';
+import { useErrorHandler } from './useErrorHandler';
+
+/**
+ * Composable for file download operations
+ * @returns {Object} Download utilities and state
+ */
+export function useFileDownload() {
+ const scenarioExecutionStore = ScenarioExecutionStore();
+ const { decodeToBlob } = useBase64Decoder();
+ const { showError } = useErrorHandler();
+
+ const isDownloading = ref(false);
+ const baseUploadDir = '/mnt/hermione_storage/hermione/file_input_scenarios/';
+
+ /**
+ * Download file from server (via store)
+ */
+ const downloadFile = async (filePath, executionId) => {
+ try {
+ isDownloading.value = true;
+
+ // Normalize path by removing base directory if present
+ let relativePath = filePath;
+ if (filePath.startsWith(baseUploadDir)) {
+ relativePath = filePath.substring(baseUploadDir.length);
+ }
+
+ await scenarioExecutionStore.downloadFile(relativePath, executionId);
+ } catch (error) {
+ console.error('Error downloading file:', error);
+ showError('Error', 'Error downloading file. Please try again.');
+ throw error;
+ } finally {
+ isDownloading.value = false;
+ }
+ };
+
+ /**
+ * Download file from base64 string
+ */
+ const downloadBase64File = (base64String, fileName, mimeType = 'application/octet-stream') => {
+ try {
+ isDownloading.value = true;
+
+ const blob = decodeToBlob(base64String, mimeType);
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = fileName;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ } catch (error) {
+ console.error('Error downloading base64 file:', error);
+ showError('Error', 'Error downloading file. Please try again.');
+ throw error;
+ } finally {
+ isDownloading.value = false;
+ }
+ };
+
+ /**
+ * Download CodeGenie document (DOCX format)
+ */
+ const downloadCodegenieFile = (base64String, executionId) => {
+ try {
+ const fileName = `sf_document-${executionId}.docx`;
+ const mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
+ downloadBase64File(base64String, fileName, mimeType);
+ } catch (error) {
+ console.error('Error downloading CodeGenie file:', error);
+ throw error;
+ }
+ };
+
+ /**
+ * Download zip file from JSZip object
+ */
+ const downloadZipFile = async (zipData, fileName) => {
+ try {
+ isDownloading.value = true;
+
+ const blob = await zipData.generateAsync({ type: 'blob' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = fileName;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ } catch (error) {
+ console.error('Error downloading zip file:', error);
+ showError('Error', 'Error downloading zip file. Please try again.');
+ throw error;
+ } finally {
+ isDownloading.value = false;
+ }
+ };
+
+ /**
+ * Download CSV file
+ */
+ const downloadCsvFile = (csvContent, fileName) => {
+ try {
+ isDownloading.value = true;
+
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = fileName;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ } catch (error) {
+ console.error('Error downloading CSV file:', error);
+ showError('Error', 'Error downloading CSV file. Please try again.');
+ throw error;
+ } finally {
+ isDownloading.value = false;
+ }
+ };
+
+ return {
+ isDownloading,
+ downloadFile,
+ downloadBase64File,
+ downloadCodegenieFile,
+ downloadZipFile,
+ downloadCsvFile
+ };
+}
diff --git a/src/composables/useFileProcessing.js b/src/composables/useFileProcessing.js
new file mode 100644
index 0000000..300e734
--- /dev/null
+++ b/src/composables/useFileProcessing.js
@@ -0,0 +1,135 @@
+import JSZip from 'jszip';
+import { marked } from 'marked';
+import { ref } from 'vue';
+import { useBase64Decoder } from './useBase64Decoder';
+
+/**
+ * Composable for file processing operations (zip extraction, base64 decoding, markdown/JSON parsing)
+ * @returns {Object} File processing utilities and state
+ */
+export function useFileProcessing() {
+ const { decodeToBytes, decodeToText } = useBase64Decoder();
+
+ const fileContent = ref('');
+ const fileType = ref('');
+ const fileNames = ref([]);
+ const fileNamesOutput = ref([]);
+ const zipInput = ref(null);
+ const zipOutput = ref(null);
+
+ /**
+ * Extract files from base64 encoded zip
+ */
+ const extractFiles = async (base64String, type = 'input', zipRef = null) => {
+ try {
+ const bytes = decodeToBytes(base64String);
+ const zipData = await JSZip.loadAsync(bytes);
+
+ // Store zip reference if provided
+ if (zipRef) {
+ if (type === 'input') {
+ zipInput.value = zipData;
+ } else {
+ zipOutput.value = zipData;
+ }
+ }
+
+ // Extract file names based on type
+ if (type === 'input') {
+ fileNames.value = getFileNamesInput(zipData);
+ } else {
+ fileNamesOutput.value = getFileNames(zipData);
+ }
+
+ return zipData;
+ } catch (error) {
+ console.error('Error extracting zip:', error);
+ if (type === 'input') {
+ fileNames.value = [];
+ } else {
+ fileNamesOutput.value = [];
+ }
+ throw error;
+ }
+ };
+
+ /**
+ * Show file content from base64 string (supports MARKDOWN, JSON)
+ */
+ const showFileContent = (base64String, type) => {
+ try {
+ const textContent = decodeToText(base64String);
+
+ if (type === 'MARKDOWN') {
+ fileContent.value = marked(textContent);
+ fileType.value = 'MARKDOWN';
+ } else if (type === 'JSON') {
+ const jsonObject = JSON.parse(textContent);
+ fileContent.value = JSON.stringify(jsonObject, null, 2);
+ fileType.value = 'JSON';
+ } else {
+ fileContent.value = 'File type not supported.';
+ fileType.value = type;
+ }
+
+ return fileContent.value;
+ } catch (error) {
+ fileContent.value = 'Error while parsing the file.';
+ console.error('Error showing file content:', error);
+ throw error;
+ }
+ };
+
+ /**
+ * Get file names from zip (full paths)
+ */
+ const getFileNamesInput = (zipData) => {
+ const files = [];
+ zipData.forEach((relativePath, file) => {
+ if (!file.dir) {
+ files.push(relativePath);
+ }
+ });
+ return files;
+ };
+
+ /**
+ * Get file names from zip (file names only)
+ */
+ const getFileNames = (zipData) => {
+ const files = [];
+ zipData.forEach((relativePath, file) => {
+ if (!file.dir) {
+ const fileName = relativePath.split('/').pop();
+ files.push(fileName);
+ }
+ });
+ return files;
+ };
+
+ /**
+ * Reset all file processing state
+ */
+ const reset = () => {
+ fileContent.value = '';
+ fileType.value = '';
+ fileNames.value = [];
+ fileNamesOutput.value = [];
+ zipInput.value = null;
+ zipOutput.value = null;
+ };
+
+ return {
+ fileContent,
+ fileType,
+ fileNames,
+ fileNamesOutput,
+ zipInput,
+ zipOutput,
+ extractFiles,
+ showFileContent,
+ getFileNames,
+ getFileNamesInput,
+ reset
+ };
+}
diff --git a/src/composables/usePolling.js b/src/composables/usePolling.js
new file mode 100644
index 0000000..60f7315
--- /dev/null
+++ b/src/composables/usePolling.js
@@ -0,0 +1,87 @@
+import axios from 'axios';
+import { onBeforeUnmount, ref } from 'vue';
+
+/**
+ * Composable for polling backend API for execution status
+ * @returns {Object} Polling utilities and state
+ */
+export function usePolling() {
+ const pollingInterval = ref(null);
+ const isPolling = ref(false);
+ const pollingData = ref(null);
+
+ /**
+ * Start polling with custom callback
+ */
+ const startPolling = (callback, interval = 2000) => {
+ if (pollingInterval.value) {
+ stopPolling();
+ }
+
+ isPolling.value = true;
+ pollingInterval.value = setInterval(callback, interval);
+ };
+
+ /**
+ * Stop polling
+ */
+ const stopPolling = () => {
+ if (pollingInterval.value) {
+ clearInterval(pollingInterval.value);
+ pollingInterval.value = null;
+ isPolling.value = false;
+ }
+ };
+
+ /**
+ * Poll backend API for execution status
+ */
+ const pollBackendAPI = async (executionId, onSuccess, onError, onComplete) => {
+ try {
+ const response = await axios.get('/execution?id=' + executionId);
+ pollingData.value = response.data;
+
+ const status = response.data.latestStepStatus;
+
+ if (status === 'OK') {
+ stopPolling();
+ if (onSuccess) onSuccess(response.data);
+ } else if (status === 'ERROR') {
+ stopPolling();
+ if (onError) onError(response.data);
+ }
+
+ if (onComplete) onComplete(response.data);
+ } catch (error) {
+ console.error('Error polling backend:', error);
+ stopPolling();
+ if (onError) onError(error);
+ }
+ };
+
+ /**
+ * Start polling for execution status with callbacks
+ */
+ const startExecutionPolling = (executionId, callbacks = {}, interval = 2000) => {
+ const { onSuccess, onError, onComplete } = callbacks;
+
+ startPolling(() => {
+ pollBackendAPI(executionId, onSuccess, onError, onComplete);
+ }, interval);
+ };
+
+ // Cleanup on component unmount
+ onBeforeUnmount(() => {
+ stopPolling();
+ });
+
+ return {
+ pollingInterval,
+ isPolling,
+ pollingData,
+ startPolling,
+ stopPolling,
+ pollBackendAPI,
+ startExecutionPolling
+ };
+}
diff --git a/src/composables/useScenarioRating.js b/src/composables/useScenarioRating.js
new file mode 100644
index 0000000..df21519
--- /dev/null
+++ b/src/composables/useScenarioRating.js
@@ -0,0 +1,70 @@
+import { ScenarioService } from '@/service/ScenarioService';
+import { UserPrefStore } from '@/stores/UserPrefStore';
+import { computed, ref } from 'vue';
+import { useErrorHandler } from './useErrorHandler';
+
+/**
+ * Composable for scenario execution rating functionality
+ * @param {Ref|number} executionId - Execution ID (can be ref or number)
+ * @param {Ref|Object} execution - Execution object (optional, for permission check)
+ * @returns {Object} Rating utilities and state
+ */
+export function useScenarioRating(executionId, execution = null) {
+ const userPrefStore = UserPrefStore();
+ const { showSuccess, showError } = useErrorHandler();
+
+ const rating = ref(null);
+ const isUpdating = ref(false);
+
+ /**
+ * Check if current user can update rating
+ */
+ const canUpdate = computed(() => {
+ if (!execution || !execution.value) return false;
+ return execution.value.executedByUsername === userPrefStore.getUser.username;
+ });
+
+ /**
+ * Update scenario execution rating
+ */
+ const updateRating = async (newRating) => {
+ isUpdating.value = true;
+
+ try {
+ const execId = typeof executionId === 'object' ? executionId.value : executionId;
+ const ratingValue = typeof newRating === 'object' ? newRating.value : newRating;
+
+ const response = await ScenarioService.updateScenarioExecRating(execId, ratingValue);
+
+ if (response.data === 'OK') {
+ rating.value = ratingValue;
+ showSuccess('Success', 'Rating updated with success.');
+ return true;
+ } else {
+ showError('Error', 'Error updating rating. Try later.');
+ return false;
+ }
+ } catch (error) {
+ console.error('Error while updating rating:', error);
+ showError('Error', 'Error updating rating.');
+ return false;
+ } finally {
+ isUpdating.value = false;
+ }
+ };
+
+ /**
+ * Initialize rating from execution data
+ */
+ const initializeRating = (initialRating) => {
+ rating.value = initialRating;
+ };
+
+ return {
+ rating,
+ isUpdating,
+ canUpdate,
+ updateRating,
+ initializeRating
+ };
+}
diff --git a/src/layout/AppMenu.vue b/src/layout/AppMenu.vue
index 7fa6fe7..96d318a 100644
--- a/src/layout/AppMenu.vue
+++ b/src/layout/AppMenu.vue
@@ -9,18 +9,23 @@ const userPrefStore = UserPrefStore();
const route = useRouter();
const model = ref([
-
{
label: 'Scenarios',
items: [
- { label: 'Available Scenarios', icon: 'pi pi-fw pi-id-card', to: '/home' },
- { label: 'Execution List', icon: 'pi pi-fw pi-list', command: () => {
- route.push({path: '/executions/all'});
- } },
- ] },
+ { label: 'Available Scenarios', icon: 'pi pi-fw pi-id-card', to: '/home' },
+ {
+ label: 'Execution List',
+ icon: 'pi pi-fw pi-list',
+ command: () => {
+ route.push({ path: '/executions/all' });
+ }
+ }
+ ]
+ },
{
label: '',
- items: [] } ,
+ items: []
+ },
{
label: 'Canvas',
items: [{ label: 'New Canvas', icon: 'pi pi-fw pi-pencil', to: '/mdcanvas' }]
@@ -29,20 +34,19 @@ const model = ref([
label: 'Chat',
items: [{ label: 'Chat', icon: 'pi pi-fw pi-comments', to: '/chat' }]
}
-
]);
onMounted(() => {
- if(userPrefStore.user.role === 'ADMIN'){
- model.value[0].items.push({
- label: 'Dashboard',
- icon: 'pi pi-fw pi-chart-bar',
- command: () => {
- route.push({ path: '/dashboard' });
- }
- });
- }
- });
+ if (userPrefStore.user.role === 'ADMIN') {
+ model.value[0].items.push({
+ label: 'Dashboard',
+ icon: 'pi pi-fw pi-chart-bar',
+ command: () => {
+ route.push({ path: '/dashboard' });
+ }
+ });
+ }
+});
// Funzione per aggiornare la sezione "Your Applications" in base a selectedApp
function updateApplicationsMenu() {
@@ -95,8 +99,6 @@ function updateApplicationsMenu() {
model.value[1].items.push(createScenarioItem(scenarios[0]));
}
});
-
-
//Aggiungi "Rev Eng Code" alla fine della lista
model.value[1].items.push({
label: 'Application Code',
@@ -148,13 +150,193 @@ watch(() => userPrefStore.getSelApp, updateApplicationsMenu, { immediate: true }
-
+
-
+
diff --git a/src/layout/AppProfileMenu.vue b/src/layout/AppProfileMenu.vue
index 5b7facb..a30a2e3 100644
--- a/src/layout/AppProfileMenu.vue
+++ b/src/layout/AppProfileMenu.vue
@@ -2,7 +2,7 @@
Welcome
-
+
{{ user.name + " " + user.surname }}
@@ -15,10 +15,10 @@
+
+
diff --git a/src/layout/AppTopbar.vue b/src/layout/AppTopbar.vue
index a028994..2ce0459 100644
--- a/src/layout/AppTopbar.vue
+++ b/src/layout/AppTopbar.vue
@@ -3,7 +3,6 @@ import { useLayout } from '@/layout/composables/layout';
import { useAuth } from '@websanova/vue-auth/src/v3.js';
import { useRouter } from 'vue-router';
-
import { LoadingStore } from '../stores/LoadingStore.js';
import { ScenarioExecutionStore } from '../stores/ScenarioExecutionStore.js';
import { ScenarioStore } from '../stores/ScenarioStore.js';
@@ -11,45 +10,42 @@ import { UserPrefStore } from '../stores/UserPrefStore.js';
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
-import { JellyfishLoader, RiseLoader } from "vue3-spinner";
+import { JellyfishLoader, RiseLoader } from 'vue3-spinner';
import AppProfileMenu from './AppProfileMenu.vue';
-const auth = useAuth();
+const auth = useAuth();
const route = useRoute();
-
-const router = useRouter();
+const router = useRouter();
const props = defineProps(['page']);
const userPrefStore = UserPrefStore();
const scenario_store = ScenarioStore();
const scenario_execution_store = ScenarioExecutionStore();
-const loadingStore = LoadingStore()
+const loadingStore = LoadingStore();
const selectedApp = ref(userPrefStore.getSelApp);
const { onMenuToggle, toggleDarkMode, isDarkTheme } = useLayout();
const isDropdownDisabled = computed(() => {
- return route.path === '/projects' || /^\/scenario\/exec\/.+/.test(route.path);
+ return route.path === '/projects' || /^\/scenario\/exec\/.+/.test(route.path);
});
async function updateApplication() {
await userPrefStore.setSelectedApp(selectedApp.value);
- scenario_store.fetchApplicationScenarios();
+ scenario_store.fetchApplicationScenarios();
scenario_execution_store.fetchScenariosExecution();
-
}
function redirectProject() {
router.push('/projects'); // Specifica il percorso per la pagina "Projects"
}
-function appUpdated() {
+function appUpdated() {
selectedApp.value = userPrefStore.getSelApp;
}
watch(() => userPrefStore.getSelApp, appUpdated, { immediate: true });
-
@@ -61,14 +57,14 @@ watch(() => userPrefStore.getSelApp, appUpdated, { immediate: true });
fill-rule="evenodd"
clip-rule="evenodd"
d="M27.017 30.3135C27.0057 30.5602 27 30.8085 27 31.0581C27 39.9267 34.1894 47.1161 43.0581 47.1161C51.9267 47.1161 59.1161 39.9267 59.1161 31.0581C59.1161 30.8026 59.1102 30.5485 59.0984 30.2959C60.699 30.0511 62.2954 29.7696 63.8864 29.4515L64.0532 29.4181C64.0949 29.9593 64.1161 30.5062 64.1161 31.0581C64.1161 42.6881 54.6881 52.1161 43.0581 52.1161C31.428 52.1161 22 42.6881 22 31.0581C22 30.514 22.0206 29.9747 22.0612 29.441L22.1136 29.4515C23.7428 29.7773 25.3777 30.0646 27.017 30.3135ZM52.4613 18.0397C49.8183 16.1273 46.5698 15 43.0581 15C39.54 15 36.2862 16.1313 33.6406 18.05C31.4938 17.834 29.3526 17.5435 27.221 17.1786C31.0806 12.7781 36.7449 10 43.0581 10C49.3629 10 55.0207 12.7708 58.8799 17.1612C56.7487 17.5285 54.6078 17.8214 52.4613 18.0397ZM68.9854 28.4316C69.0719 29.2954 69.1161 30.1716 69.1161 31.0581C69.1161 45.4495 57.4495 57.1161 43.0581 57.1161C28.6666 57.1161 17 45.4495 17 31.0581C17 30.1793 17.0435 29.3108 17.1284 28.4544L12.2051 27.4697C12.0696 28.6471 12 29.8444 12 31.0581C12 48.211 25.9052 62.1161 43.0581 62.1161C60.211 62.1161 74.1161 48.211 74.1161 31.0581C74.1161 29.8366 74.0456 28.6317 73.9085 27.447L68.9854 28.4316ZM69.6705 15.0372L64.3929 16.0927C59.6785 9.38418 51.8803 5 43.0581 5C34.2269 5 26.4218 9.39306 21.7089 16.1131L16.4331 15.0579C21.867 6.03506 31.7578 0 43.0581 0C54.3497 0 64.234 6.02581 69.6705 15.0372Z"
- fill="var(--primary-color)"
+ fill="white"
/>
-
+
@@ -85,15 +81,13 @@ watch(() => userPrefStore.getSelApp, appUpdated, { immediate: true });
Project:
@@ -116,42 +107,26 @@ watch(() => userPrefStore.getSelApp, appUpdated, { immediate: true });
-
-
+
Application:
-
-
-
+
-
@@ -164,12 +139,59 @@ watch(() => userPrefStore.getSelApp, appUpdated, { immediate: true });
align-items: center;
justify-content: left;
margin-right: 1rem;
- font-size: 1.2rem;
+ font-size: 1rem;
min-width: 100px;
-
}
+
+.topbar-project :deep(.p-button) {
+ background: rgba(255, 255, 255, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ color: white;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+}
+
+.topbar-project :deep(.p-button:hover) {
+ background: rgba(255, 255, 255, 0.3);
+ border-color: rgba(255, 255, 255, 0.5);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(255, 255, 255, 0.2);
+}
+
.menu-list {
width: 200px;
margin-right: 1rem;
}
-
\ No newline at end of file
+
+.menu-list :deep(.p-dropdown) {
+ background: rgba(255, 255, 255, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ color: white;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+}
+
+.menu-list :deep(.p-dropdown:hover) {
+ background: rgba(255, 255, 255, 0.3);
+ border-color: rgba(255, 255, 255, 0.5);
+}
+
+.menu-list :deep(.p-dropdown-label) {
+ color: white;
+}
+
+.menu-list :deep(.p-dropdown-trigger) {
+ color: white;
+}
+
+.menu-list :deep(.p-dropdown:not(.p-disabled).p-focus) {
+ border-color: rgba(255, 255, 255, 0.5);
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.15);
+}
+
+.user-menu-button i {
+ color: #ffffff !important;
+}
+
diff --git a/src/main.js b/src/main.js
index 3c00cba..a88e95b 100644
--- a/src/main.js
+++ b/src/main.js
@@ -53,8 +53,8 @@ var auth = createAuth({
}
});
-axios.defaults.baseURL = import.meta.env.VITE_BACKEND_URL;
-//axios.defaults.baseURL = 'http://localhost:8081';
+//axios.defaults.baseURL = import.meta.env.VITE_BACKEND_URL;
+axios.defaults.baseURL = 'http://localhost:8081';
console.log(import.meta.env.VITE_BACKEND_URL);
@@ -63,17 +63,25 @@ const pinia = createPinia();
const preset = definePreset(Nora, {
semantic: {
primary: {
- 50: '{violet.50}',
- 100: '{violet.100}',
- 200: '{violet.200}',
- 300: '{violet.300}',
- 400: '{violet.400}',
- 500: '{violet.500}',
- 600: '{violet.600}',
- 700: '{violet.700}',
- 800: '{violet.800}',
- 900: '{violet.900}',
- 950: '{violet.950}'
+ 50: '#F5E6FF',
+ 100: '#E6CCFF',
+ 200: '#CC99FF',
+ 300: '#B366FF',
+ 400: '#9933FF',
+ 500: '#A100FF',
+ 600: '#8800D9',
+ 700: '#7B00CC',
+ 800: '#6600A6',
+ 900: '#4D007F',
+ 950: '#330052'
+ },
+ borderRadius: {
+ none: '0',
+ xs: '2px',
+ sm: '4px',
+ md: '8px',
+ lg: '12px',
+ xl: '16px'
},
colorScheme: {
light: {
diff --git a/src/router/index.js b/src/router/index.js
index e99d3ae..6962722 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -38,7 +38,7 @@ const router = createRouter({
{
path: 'exec-history',
name: 'scenario-exec-history',
- component: () => import('@/views/pages/OldScenarioExec.vue')
+ component: () => import('@/views/pages/ScenarioExecHistory.vue')
},
{
path: '/dashboard',
@@ -76,6 +76,11 @@ const router = createRouter({
path: '/chat',
name: 'chat',
component: () => import('@/views/pages/chat/ChatPage.vue')
+ },
+ {
+ path: '/markdown-demo',
+ name: 'markdown-demo',
+ component: () => import('@/views/pages/MarkdownDemo.vue')
}
]
}
diff --git a/src/service/ScenarioService.js b/src/service/ScenarioService.js
index 867c0ab..86390d6 100644
--- a/src/service/ScenarioService.js
+++ b/src/service/ScenarioService.js
@@ -35,6 +35,11 @@ export const ScenarioService = {
return axios.get(`/scenarios/${id}`);
},
+ // Get full scenario execution details by ID
+ getScenarioExecutionById(id) {
+ return axios.get('/execution?id=' + id);
+ },
+
// Nuovo metodo per eseguire uno scenario in modo asincrono
executeScenarioAsync(data) {
return axios.post('/scenarios/execute-async', data);
diff --git a/src/utils/csvExport.js b/src/utils/csvExport.js
new file mode 100644
index 0000000..391e061
--- /dev/null
+++ b/src/utils/csvExport.js
@@ -0,0 +1,127 @@
+/**
+ * CSV export utilities
+ * Consolidates CSV generation and download logic
+ */
+
+/**
+ * Convert array of objects to CSV string
+ * @param {Array} data - Array of objects to convert
+ * @param {Array} columns - Column definitions with field and header
+ * @returns {string} CSV string
+ */
+export function convertToCSV(data, columns) {
+ if (!data || data.length === 0) {
+ return '';
+ }
+
+ // Create header row
+ const header = columns.map((col) => col.header || col.field).join(',');
+
+ // Create data rows
+ const rows = data.map((item) => {
+ return columns
+ .map((col) => {
+ let value = item[col.field];
+
+ // Handle null/undefined
+ if (value === null || value === undefined) {
+ value = '';
+ }
+
+ // Handle objects and arrays
+ if (typeof value === 'object') {
+ value = JSON.stringify(value);
+ }
+
+ // Escape quotes and wrap in quotes if contains comma or quotes
+ value = String(value).replace(/"/g, '""');
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
+ value = `"${value}"`;
+ }
+
+ return value;
+ })
+ .join(',');
+ });
+
+ return [header, ...rows].join('\n');
+}
+
+/**
+ * Export data to CSV file
+ * @param {Array} data - Data to export
+ * @param {Array} columns - Column definitions
+ * @param {string} fileName - Output file name
+ * @returns {boolean} Success status
+ */
+export function exportToCSV(data, columns, fileName = 'export.csv') {
+ try {
+ const csv = convertToCSV(data, columns);
+
+ if (!csv) {
+ console.warn('No data to export');
+ return false;
+ }
+
+ // Create blob and download
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = fileName;
+
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+
+ return true;
+ } catch (error) {
+ console.error('Error exporting CSV:', error);
+ return false;
+ }
+}
+
+/**
+ * Format date for CSV export
+ * @param {Date|string} date - Date to format
+ * @param {boolean} includeTime - Include time in output
+ * @returns {string} Formatted date string
+ */
+export function formatDateForCSV(date, includeTime = true) {
+ if (!date) return '';
+
+ const d = new Date(date);
+ if (isNaN(d.getTime())) return '';
+
+ const year = d.getFullYear();
+ const month = String(d.getMonth() + 1).padStart(2, '0');
+ const day = String(d.getDate()).padStart(2, '0');
+
+ if (!includeTime) {
+ return `${year}-${month}-${day}`;
+ }
+
+ const hours = String(d.getHours()).padStart(2, '0');
+ const minutes = String(d.getMinutes()).padStart(2, '0');
+ const seconds = String(d.getSeconds()).padStart(2, '0');
+
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+}
+
+/**
+ * Sanitize data for CSV export
+ * @param {*} value - Value to sanitize
+ * @returns {string} Sanitized value
+ */
+export function sanitizeForCSV(value) {
+ if (value === null || value === undefined) {
+ return '';
+ }
+
+ if (typeof value === 'object') {
+ return JSON.stringify(value);
+ }
+
+ return String(value);
+}
diff --git a/src/utils/formDataProcessor.js b/src/utils/formDataProcessor.js
new file mode 100644
index 0000000..f5ad915
--- /dev/null
+++ b/src/utils/formDataProcessor.js
@@ -0,0 +1,71 @@
+/**
+ * Process form data for multiselect inputs
+ * Converts multiselect arrays into _id and _name JSON strings
+ * Consolidates processFormData logic from multiple files
+ * @param {Object} formData - Form data object
+ * @param {Array} multiselectFields - Array of multiselect field names
+ * @returns {Object} Processed form data
+ */
+export function processFormData(formData, multiselectFields = []) {
+ const processed = { ...formData };
+
+ multiselectFields.forEach((field) => {
+ if (Array.isArray(processed[field]) && processed[field].length > 0) {
+ // Convert array to _id and _name format
+ const ids = processed[field].map((item) => (typeof item === 'object' ? item.id : item));
+ const names = processed[field].map((item) => (typeof item === 'object' ? item.name : item));
+
+ processed[`${field}_id`] = JSON.stringify(ids);
+ processed[`${field}_name`] = JSON.stringify(names);
+
+ // Remove original array field
+ delete processed[field];
+ }
+ });
+
+ return processed;
+}
+
+/**
+ * Parse multiselect data from _id and _name format
+ * Reverse operation of processFormData
+ * @param {Object} data - Data object with _id and _name fields
+ * @param {Array} multiselectFields - Array of multiselect field names
+ * @returns {Object} Parsed data with arrays
+ */
+export function parseMultiselectData(data, multiselectFields = []) {
+ const parsed = { ...data };
+
+ multiselectFields.forEach((field) => {
+ const idField = `${field}_id`;
+ const nameField = `${field}_name`;
+
+ if (parsed[idField] && parsed[nameField]) {
+ try {
+ const ids = JSON.parse(parsed[idField]);
+ const names = JSON.parse(parsed[nameField]);
+
+ parsed[field] = ids.map((id, index) => ({
+ id,
+ name: names[index]
+ }));
+
+ delete parsed[idField];
+ delete parsed[nameField];
+ } catch (error) {
+ console.error(`Error parsing multiselect field ${field}:`, error);
+ }
+ }
+ });
+
+ return parsed;
+}
+
+/**
+ * Validate multiselect field value
+ * @param {*} value - Value to validate
+ * @returns {boolean} True if valid multiselect value
+ */
+export function isValidMultiselectValue(value) {
+ return Array.isArray(value) && value.length > 0;
+}
diff --git a/src/utils/inputComponents.js b/src/utils/inputComponents.js
new file mode 100644
index 0000000..ac74dd3
--- /dev/null
+++ b/src/utils/inputComponents.js
@@ -0,0 +1,38 @@
+import InputText from 'primevue/inputtext';
+import MultiSelect from 'primevue/multiselect';
+import Select from 'primevue/select';
+import Textarea from 'primevue/textarea';
+
+/**
+ * Map input type to PrimeVue component
+ * Consolidates getInputComponent logic from multiple files
+ * @param {string} type - Input type (text, textarea, select, multiselect)
+ * @returns {Component} PrimeVue component
+ */
+export function getInputComponent(type) {
+ const components = {
+ text: InputText,
+ textarea: Textarea,
+ select: Select,
+ multiselect: MultiSelect
+ };
+
+ return components[type] || InputText;
+}
+
+/**
+ * Get all available input types
+ * @returns {Array} Array of input type names
+ */
+export function getInputTypes() {
+ return ['text', 'textarea', 'select', 'multiselect'];
+}
+
+/**
+ * Check if input type is valid
+ * @param {string} type - Input type to check
+ * @returns {boolean} True if valid
+ */
+export function isValidInputType(type) {
+ return getInputTypes().includes(type);
+}
diff --git a/src/views/pages/DashExecution.vue b/src/views/pages/DashExecution.vue
index 8d95e87..7836846 100644
--- a/src/views/pages/DashExecution.vue
+++ b/src/views/pages/DashExecution.vue
@@ -1,387 +1,3 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Executions Stats
-
-
-
-
-
-
- {{ slotProps.data.projectName }}
-
-
-
-
-
- {{ slotProps.data.scenarioInternalName }}
-
-
-
-
-
- {{ slotProps.data.totalExecutions }}
-
-
-
-
-
- {{ slotProps.data.totalTokens }}
-
-
-
-
-
-
-
-
-
-
-
-
-
Execution List
-
-
-
-
-
-
- {{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }}
-
-
-
-
-
- {{ slotProps.data.executedByUsername }}
-
-
-
-
-
- {{ slotProps.data.execSharedMap?.user_input?.selected_project || '' }}
-
-
-
-
-
- {{ slotProps.data.execSharedMap?.user_input?.selected_application || '' }}
-
-
-
-
-
- {{ slotProps.data.scenario?.name || '' }}
-
-
-
-
-
-
- {{ slotProps.data.scenario?.category || '' }}
-
-
-
-
-
-
-
- {{ slotProps.data.scenario?.aiModel?.model || '' }}
-
-
-
-
-
- {{ slotProps.data.usedTokens || '' }}
-
-
-
-
-
-
-
-
-
-
-
- {{ formatElapsed((new Date(slotProps.data.endDate) - new Date(slotProps.data.startDate)) / 1000) }}
-
-
-
-
-
- {{ slotProps.data.latestStepStatus === 'ERROR' ? 'N' : 'Y' }}
-
-
-
-
-
- | No execution found |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ slotProps.data.projectName }}
+
+
+
+
+
+ {{ slotProps.data.scenarioInternalName }}
+
+
+
+
+
+ {{ slotProps.data.totalExecutions }}
+
+
+
+
+
+ {{ slotProps.data.totalTokens }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }}
+
+
+
+
+
+ {{ slotProps.data.executedByUsername }}
+
+
+
+
+
+ {{ slotProps.data.execSharedMap?.user_input?.selected_project || '' }}
+
+
+
+
+
+ {{ slotProps.data.execSharedMap?.user_input?.selected_application || '' }}
+
+
+
+
+
+ {{ slotProps.data.scenario?.name || '' }}
+
+
+
+
+
+
+ {{ slotProps.data.scenario?.category || '' }}
+
+
+
+
+
+
+
+ {{ slotProps.data.scenario?.aiModel?.model || '' }}
+
+
+
+
+
+ {{ slotProps.data.usedTokens || '' }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatElapsed((new Date(slotProps.data.endDate) - new Date(slotProps.data.startDate)) / 1000) }}
+
+
+
+
+
+ {{ slotProps.data.latestStepStatus === 'ERROR' ? 'N' : 'Y' }}
+
+
+
+
+
+ | No execution found |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/pages/MarkdownDemo.vue b/src/views/pages/MarkdownDemo.vue
new file mode 100644
index 0000000..ae6e59f
--- /dev/null
+++ b/src/views/pages/MarkdownDemo.vue
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
diff --git a/src/views/pages/OldScenarioExec.vue b/src/views/pages/OldScenarioExec.vue.backup
similarity index 100%
rename from src/views/pages/OldScenarioExec.vue
rename to src/views/pages/OldScenarioExec.vue.backup
diff --git a/src/views/pages/ProjectList.vue b/src/views/pages/ProjectList.vue
index 34b087d..9939a1c 100644
--- a/src/views/pages/ProjectList.vue
+++ b/src/views/pages/ProjectList.vue
@@ -78,9 +78,9 @@ import ProgressSpinner from 'primevue/progressspinner';
import { computed, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { LoadingStore } from '../../stores/LoadingStore.js';
-import { UserPrefStore } from '../../stores/UserPrefStore.js';
-import { ScenarioStore } from '../../stores/ScenarioStore.js';
import { ScenarioExecutionStore } from '../../stores/ScenarioExecutionStore.js';
+import { ScenarioStore } from '../../stores/ScenarioStore.js';
+import { UserPrefStore } from '../../stores/UserPrefStore.js';
const loadingStore = LoadingStore()
@@ -116,7 +116,7 @@ const scenario_execution_store = ScenarioExecutionStore();
return data.search
.toLowerCase()
.split(" ")
- .every((v) => item.name.toLowerCase().includes(v));
+ .every((v) => item.fe_name.toLowerCase().includes(v));
});
} else {
return data.projects;
diff --git a/src/views/pages/ScenarioExec.vue b/src/views/pages/ScenarioExec.vue
index 322375e..b60a391 100644
--- a/src/views/pages/ScenarioExec.vue
+++ b/src/views/pages/ScenarioExec.vue
@@ -1,914 +1,261 @@
-
-
-
- {{ scenario.name }}
-
-
-
-
- {{ scenario.description }}
-
-
-
-
-
-
-
-
-
-
-
-
-
onUpload(event, 'SingleFileUpload')"
- :multiple="false"
- :accept="acceptedFormats"
- auto
- :showUploadButton="false"
- :showCancelButton="false"
- :maxFileSize="52428800"
- :invalidFileSizeMessage="'Invalid file size, file size should be smaller than 20 MB'"
- v-model:files="uploadedFiles"
- @before-send="onBeforeSend"
- >
-
-
-
-
-
-
-
- | Name |
- Dimension |
- Status |
- Actions |
-
-
-
-
- | {{ file.name }} |
- {{ formatSize(file.size) }} |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Drag and drop files here to upload.
-
-
-
-
-
-
-
-
-
-
onUpload(event, 'MultiFileUpload')"
- :multiple="true"
- accept=".msg,.txt,.docx"
- auto
- :showUploadButton="false"
- :showCancelButton="false"
- :maxFileSize="52428800"
- v-model:files="uploadedFiles"
- @before-send="onBeforeSend"
- >
- >
-
-
-
-
-
-
-
- | Name |
- Dimension |
- Status |
- Actions |
-
-
-
-
- | {{ file.name }} |
- {{ formatSize(file.size) }} |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Drag and drop files here to upload.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ scenario_response_message }}
-
-
Starting execution...
-
-
Time elapsed:
-
00:00
-
-
-
-
-
-
- Workflow Response
-
-
-
-
-
-
-
-
-
Error: {{ error_message }}
-
-
-
Error: Execution failed.
-
-
-
-
-
-
-
-
-
-
-
-
- -
- sf_document-{{ exec_id }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Chat with WizardAI
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/pages/ScenarioExec.vue.backup b/src/views/pages/ScenarioExec.vue.backup
new file mode 100644
index 0000000..322375e
--- /dev/null
+++ b/src/views/pages/ScenarioExec.vue.backup
@@ -0,0 +1,997 @@
+
+
+
+ {{ scenario.name }}
+
+
+
+
+ {{ scenario.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
onUpload(event, 'SingleFileUpload')"
+ :multiple="false"
+ :accept="acceptedFormats"
+ auto
+ :showUploadButton="false"
+ :showCancelButton="false"
+ :maxFileSize="52428800"
+ :invalidFileSizeMessage="'Invalid file size, file size should be smaller than 20 MB'"
+ v-model:files="uploadedFiles"
+ @before-send="onBeforeSend"
+ >
+
+
+
+
+
+
+
+ | Name |
+ Dimension |
+ Status |
+ Actions |
+
+
+
+
+ | {{ file.name }} |
+ {{ formatSize(file.size) }} |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Drag and drop files here to upload.
+
+
+
+
+
+
+
+
+
+
onUpload(event, 'MultiFileUpload')"
+ :multiple="true"
+ accept=".msg,.txt,.docx"
+ auto
+ :showUploadButton="false"
+ :showCancelButton="false"
+ :maxFileSize="52428800"
+ v-model:files="uploadedFiles"
+ @before-send="onBeforeSend"
+ >
+ >
+
+
+
+
+
+
+
+ | Name |
+ Dimension |
+ Status |
+ Actions |
+
+
+
+
+ | {{ file.name }} |
+ {{ formatSize(file.size) }} |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Drag and drop files here to upload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ scenario_response_message }}
+
+
Starting execution...
+
+
Time elapsed:
+
00:00
+
+
+
+
+
+
+ Workflow Response
+
+
+
+
+
+
+
+
+
Error: {{ error_message }}
+
+
+
Error: Execution failed.
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ sf_document-{{ exec_id }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Chat with WizardAI
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/pages/ScenarioExecHistory.vue b/src/views/pages/ScenarioExecHistory.vue
new file mode 100644
index 0000000..3f4ee38
--- /dev/null
+++ b/src/views/pages/ScenarioExecHistory.vue
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/pages/ScenarioExecList.vue b/src/views/pages/ScenarioExecList.vue
index e5df9d2..552b06f 100644
--- a/src/views/pages/ScenarioExecList.vue
+++ b/src/views/pages/ScenarioExecList.vue
@@ -1,173 +1,4 @@
-
-
-
-
-
Executions List
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ slotProps.data.scenario?.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ slotProps.data.execSharedMap?.user_input?.selected_application }}
-
-
-
-
-
-
-
-
-
-
-
- {{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }}
-
-
-
-
-
-
-
- {{ slotProps.data.scenario?.aiModel?.model }}
-
-
-
-
-
-
-
-
- {{ slotProps.data.executedByUsername || 'N/A' }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- | No execution found |
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ slotProps.data.scenario?.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ slotProps.data.execSharedMap?.user_input?.selected_application }}
+
+
+
+
+
+
+
+
+
+
+ {{ moment(slotProps.data.startDate).format('DD-MM-YYYY HH:mm:ss') }}
+
+
+
+
+
+
+
+ {{ slotProps.data.scenario?.aiModel?.model }}
+
+
+
+
+
+
+
+
+ {{ slotProps.data.executedByUsername || 'N/A' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
No executions found
+
Try adjusting your filters or execute a new scenario
+
+
+
+
+
+
+
+
+
\ No newline at end of file
+
diff --git a/src/views/pages/ScenarioList.vue b/src/views/pages/ScenarioList.vue
index 8a453a7..39ad85e 100644
--- a/src/views/pages/ScenarioList.vue
+++ b/src/views/pages/ScenarioList.vue
@@ -1,139 +1,226 @@
-
-
-
Available Scenarios
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ item.name }}
-
{{ item.description }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ item.name }}
-
{{ item.description }}
-
-
-
- {{ item.visible }}
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.description }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
+/* Filter Container */
+.filter-container {
+ display: flex;
+ align-items: center;
+}
+
+.type-filter :deep(.p-button) {
+ border-radius: 8px;
+ transition: all 0.3s ease;
+}
+
+.type-filter :deep(.p-button:hover) {
+ transform: translateY(-2px);
+}
+
+/* Layout Switch */
+.layout-switch :deep(.p-button) {
+ border-radius: 8px;
+ width: 3rem;
+ height: 3rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.layout-switch :deep(.p-button i) {
+ font-size: 1.2rem;
+}
+
+/* List View */
+.list-view {
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.list-item {
+ background: #f8f9fa;
+ border-radius: 12px;
+ border: 1px solid #e2e8f0;
+ transition: all 0.3s ease;
+ overflow: hidden;
+}
+
+.list-item:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 6px 20px rgba(161, 0, 255, 0.15);
+ border-color: #a100ff;
+}
+
+.list-item-content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1.5rem;
+ gap: 2rem;
+}
+
+.item-info {
+ flex: 1;
+}
+
+.item-header {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-bottom: 0.75rem;
+}
+
+.item-icon {
+ color: #a100ff;
+ font-size: 1.3rem;
+}
+
+.item-title {
+ font-size: 1.2rem;
+ font-weight: 600;
+ color: #2d3748;
+ margin: 0;
+}
+
+.draft-badge {
+ padding: 0.25rem 0.75rem;
+ background: #a100ff;
+ color: white;
+ border-radius: 12px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+}
+
+.item-description {
+ color: #64748b;
+ font-size: 0.95rem;
+ line-height: 1.6;
+ margin: 0;
+}
+
+.item-actions {
+ display: flex;
+ align-items: center;
+}
+
+.load-button :deep(.p-button) {
+ min-width: 150px;
+}
+
+/* Grid View */
+.grid-view {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: 1.5rem;
+ padding: 1.5rem;
+}
+
+.grid-item-card {
+ background: #f8f9fa;
+ border-radius: 12px;
+ border: 1px solid #e2e8f0;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ transition: all 0.3s ease;
+ overflow: hidden;
+}
+
+.grid-item-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 24px rgba(161, 0, 255, 0.2);
+ border-color: #a100ff;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1.25rem;
+ background: linear-gradient(135deg, #a100ff15 0%, #7b00cc15 100%);
+ border-bottom: 1px solid #e2e8f0;
+}
+
+.card-header-title {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ flex: 1;
+ min-width: 0;
+}
+
+.card-icon {
+ color: #a100ff;
+ font-size: 1.3rem;
+ flex-shrink: 0;
+}
+
+.draft-badge-small {
+ padding: 0.25rem 0.5rem;
+ background: #a100ff;
+ color: white;
+ border-radius: 8px;
+ font-size: 0.7rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ flex-shrink: 0;
+}
+
+.card-content {
+ flex: 1;
+ padding: 1.25rem;
+}
+
+.card-title {
+ font-size: 1.05rem;
+ font-weight: 600;
+ color: #2d3748;
+ margin: 0;
+ line-height: 1.3;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.card-description {
+ color: #64748b;
+ font-size: 0.9rem;
+ line-height: 1.5;
+ margin: 0;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.card-footer {
+ padding: 1rem 1.25rem;
+ border-top: 1px solid #e2e8f0;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.card-button {
+ width: 100%;
+ justify-content: center;
+}
+
+/* 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);
+ }
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ .scenario-list-container {
+ padding: 1rem;
+ }
+
+ .header-content {
+ flex-direction: column;
+ text-align: center;
+ padding: 1.5rem;
+ }
+
+ .page-title {
+ font-size: 1.4rem !important;
+ }
+
+ .page-subtitle {
+ font-size: 1rem;
+ }
+
+ .dataview-header {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .search-container {
+ min-width: 100%;
+ }
+
+ .list-item-content {
+ flex-direction: column;
+ gap: 1rem;
+ }
+
+ .item-actions {
+ width: 100%;
+ }
+
+ .load-button {
+ width: 100%;
+ }
+
+ .grid-view {
+ grid-template-columns: 1fr;
+ }
+}
+
+/* DataView Override Styles */
+.custom-dataview :deep(.p-dataview-content) {
+ background: transparent;
+}
+
+.custom-dataview :deep(.p-paginator) {
+ background: white;
+ border-top: 2px solid #e2e8f0;
+ padding: 1rem 1.5rem;
+}
+
+.custom-dataview :deep(.p-paginator .p-paginator-pages .p-paginator-page.p-highlight) {
+ background: #a100ff;
+ border-color: #a100ff;
+}
+
+.custom-dataview :deep(.p-paginator .p-paginator-pages .p-paginator-page:hover) {
+ background: #a100ff15;
+ border-color: #a100ff;
+}
+
diff --git a/src/views/pages/chat/ChatPage.vue b/src/views/pages/chat/ChatPage.vue
index 5788082..ebabcee 100644
--- a/src/views/pages/chat/ChatPage.vue
+++ b/src/views/pages/chat/ChatPage.vue
@@ -1,44 +1,150 @@
-
-
-
-
- Chat with WizardAI
-
-
-
-
-
- Contextualized on
-
-
- Project: {{ userPrefStore.user.selectedProject.fe_name }}
-
-
- Application: {{ userPrefStore.user.selectedApplication.fe_name}}
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+