before refactor
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
<template>
|
||||
<div v-if="parsedOuput != null">
|
||||
<h1>{{ parsedOuput.title }}</h1>
|
||||
<MdPreview class="editor" v-model="parsedOuput.description" language="en-US" />
|
||||
<CiaFlowCodeViewer :changes="parsedOuput.changes">
|
||||
</CiaFlowCodeViewer>
|
||||
<MarkdownViewer class="editor" v-model="parsedOuput.description" />
|
||||
<CiaFlowCodeViewer :changes="parsedOuput.changes"> </CiaFlowCodeViewer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import { defineProps, onMounted, ref, toRefs } from 'vue';
|
||||
import CiaFlowCodeViewer from './CiaFlowCodeViewer.vue';
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
|
||||
const parsedOuput =ref(null);
|
||||
const parsedOuput = ref(null);
|
||||
|
||||
//66f55e4b2894530b1c154f69
|
||||
const props = defineProps({
|
||||
@@ -30,85 +28,78 @@ onMounted(() => {
|
||||
var jsonParsed = JSON.parse(scenario_output.value.replace('```json', '').replace('```', ''));
|
||||
console.log(jsonParsed);
|
||||
parsedOuput.value = jsonParsed;
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@import '@vue-flow/core/dist/style.css';
|
||||
@import '@vue-flow/core/dist/theme-default.css';
|
||||
@import '@vue-flow/controls/dist/style.css';
|
||||
@import '@vue-flow/minimap/dist/style.css';
|
||||
|
||||
|
||||
|
||||
.basic-flow{
|
||||
.basic-flow {
|
||||
height: 90vh;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
.vue-flow__minimap {
|
||||
transform: scale(75%);
|
||||
transform-origin: bottom right;
|
||||
transform: scale(75%);
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.basic-flow.dark {
|
||||
background:#2d3748;
|
||||
color:#fffffb
|
||||
background: #2d3748;
|
||||
color: #fffffb;
|
||||
}
|
||||
|
||||
.basic-flow.dark .vue-flow__node {
|
||||
background:#4a5568;
|
||||
color:#fffffb
|
||||
background: #4a5568;
|
||||
color: #fffffb;
|
||||
}
|
||||
|
||||
.basic-flow.dark .vue-flow__node.selected {
|
||||
background:#333;
|
||||
box-shadow:0 0 0 2px #2563eb
|
||||
background: #333;
|
||||
box-shadow: 0 0 0 2px #2563eb;
|
||||
}
|
||||
|
||||
.basic-flow .vue-flow__controls {
|
||||
display:flex;
|
||||
flex-wrap:wrap;
|
||||
justify-content:center
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.basic-flow.dark .vue-flow__controls {
|
||||
border:1px solid #FFFFFB
|
||||
border: 1px solid #fffffb;
|
||||
}
|
||||
|
||||
.basic-flow .vue-flow__controls .vue-flow__controls-button {
|
||||
border:none;
|
||||
border-right:1px solid #eee
|
||||
border: none;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
|
||||
.basic-flow.dark .vue-flow__controls .vue-flow__controls-button:hover {
|
||||
background:#4d4d4d
|
||||
background: #4d4d4d;
|
||||
}
|
||||
|
||||
.basic-flow.dark .vue-flow__edge-textbg {
|
||||
fill:#292524
|
||||
fill: #292524;
|
||||
}
|
||||
|
||||
.basic-flow.dark .vue-flow__edge-text {
|
||||
fill:#fffffb
|
||||
fill: #fffffb;
|
||||
}
|
||||
|
||||
|
||||
.vue-flow__node-class-node {
|
||||
border:1px solid #dc07bc;
|
||||
padding:10px;
|
||||
border: 1px solid #dc07bc;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background:#f5f5f5;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
gap:10px;
|
||||
max-width:250px;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
max-width: 250px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<div v-for="(msg, index) in messages" :key="index" :class="['chat-message', msg.sender]">
|
||||
<div class="message-bubble">
|
||||
<div v-if="msg.sender === 'bot'">
|
||||
<MdPreview class="editor" theme="light" previewTheme="github" v-model="msg.text" language="en-US" :key="index" />
|
||||
<MarkdownViewer class="editor" theme="light" previewTheme="github" v-model="msg.text" :key="index" />
|
||||
</div>
|
||||
<p v-else>{{ msg.text }}</p>
|
||||
</div>
|
||||
@@ -19,15 +19,11 @@
|
||||
<!-- <p-button icon="pi pi-cog" @click="showSettings = !showSettings" class="p-button-normal" /> -->
|
||||
</p-inputGroup>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { marked } from 'marked';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import Button from 'primevue/button';
|
||||
import Card from 'primevue/card';
|
||||
import Checkbox from 'primevue/checkbox';
|
||||
@@ -35,6 +31,7 @@ import InputGroup from 'primevue/inputgroup';
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import ScrollPanel from 'primevue/scrollpanel';
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
|
||||
import { UserPrefStore } from '../stores/UserPrefStore.js';
|
||||
const userPrefStore = UserPrefStore();
|
||||
@@ -49,11 +46,11 @@ export default {
|
||||
'p-card': Card,
|
||||
'p-inputGroup': InputGroup,
|
||||
'p-inputGroupAddon': InputGroupAddon,
|
||||
MdPreview
|
||||
MarkdownViewer
|
||||
},
|
||||
props: {
|
||||
scenarioExecutionId: {
|
||||
type: String,
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
@@ -73,8 +70,7 @@ export default {
|
||||
previousMessagesLength: 0
|
||||
};
|
||||
},
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
mounted() {
|
||||
console.log('userPrefStore', userPrefStore);
|
||||
@@ -83,9 +79,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
async updateConversationId() {
|
||||
this.conversationId = this.scenarioExecutionId
|
||||
? `${userPrefStore.user.id}-${this.scenarioExecutionId}`
|
||||
: `${userPrefStore.user.id}-${userPrefStore.user.selectedProject.internal_name}`;
|
||||
this.conversationId = this.scenarioExecutionId ? `${userPrefStore.user.id}-${this.scenarioExecutionId}` : `${userPrefStore.user.id}-${userPrefStore.user.selectedProject.internal_name}`;
|
||||
await this.fetchChatHistory();
|
||||
if (this.scenarioExecutionId && this.messages.length === 0) {
|
||||
this.loadContext();
|
||||
@@ -98,7 +92,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.replace("/hermione", "")}/chatservice/get-history?conversationId=${this.conversationId}&lastN=100`, {
|
||||
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.replace('/hermione', '')}/chatservice/get-history?conversationId=${this.conversationId}&lastN=100`, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json', authorization: 'Bearer ' + this.$auth.token() }
|
||||
});
|
||||
@@ -143,7 +137,7 @@ export default {
|
||||
|
||||
try {
|
||||
this.waitingData = true;
|
||||
const response = await fetch(import.meta.env.VITE_BACKEND_URL.replace("/hermione", "")+'/chatservice/chat', {
|
||||
const response = await fetch(import.meta.env.VITE_BACKEND_URL.replace('/hermione', '') + '/chatservice/chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', authorization: this.authorization },
|
||||
body: JSON.stringify(payload)
|
||||
@@ -205,7 +199,7 @@ export default {
|
||||
|
||||
async clearHistory() {
|
||||
try {
|
||||
const response = await fetch( `${import.meta.env.VITE_BACKEND_URL.replace("/hermione", "")}/chatservice/delete-history?conversationId=${this.conversationId}`, {
|
||||
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.replace('/hermione', '')}/chatservice/delete-history?conversationId=${this.conversationId}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json', authorization: this.authorization }
|
||||
});
|
||||
@@ -221,7 +215,7 @@ export default {
|
||||
|
||||
async loadContext() {
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.replace("/hermione", "")}/chatservice/load-context-to-conversation?conversationId=${this.conversationId}&scenarioExecutionId=${this.scenarioExecutionId}`, {
|
||||
const response = await fetch(`${import.meta.env.VITE_BACKEND_URL.replace('/hermione', '')}/chatservice/load-context-to-conversation?conversationId=${this.conversationId}&scenarioExecutionId=${this.scenarioExecutionId}`, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json', authorization: this.authorization }
|
||||
});
|
||||
|
||||
@@ -1,84 +1,49 @@
|
||||
<template>
|
||||
<Tabs v-model:value="tabvalue" @update:value="tabUpdate">
|
||||
<TabList>
|
||||
<Tab v-for="(change, index) in changes" :value="index"> Change {{index +1}} </Tab>
|
||||
<!--<Tab value="1">Code Diff</Tab>-->
|
||||
<Tab value="class-description">Class RE</Tab>
|
||||
<Tab value="class-code">Actual Class Code</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel v-for="(change, index) in changes" :value="index">
|
||||
<div class="flex grid grid-cols-1 gap-4">
|
||||
<!--<h2>Change {{index}} description</h2>-->
|
||||
<div class="full-width">
|
||||
<MdPreview class="editor" v-model="change.change_description" language="en-US" />
|
||||
</div>
|
||||
<div>
|
||||
<Button clas="align-right" @click="toggleView"> {{ btn_toggle_label }}</Button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="full-width" v-if="!show_as_diff">
|
||||
<HighCode
|
||||
class="code"
|
||||
:codeValue="change.new_code"
|
||||
theme="light"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:codeLines="true"
|
||||
langName=""
|
||||
lang="java"
|
||||
fontSize="12px"
|
||||
></HighCode>
|
||||
</div>
|
||||
<div class="m-0" v-else>
|
||||
<CodeDiff
|
||||
:old-string="change.previous_code"
|
||||
:new-string="change.new_code"
|
||||
output-format="side-by-side"
|
||||
/>
|
||||
</div>
|
||||
<Tabs v-model:value="tabvalue" @update:value="tabUpdate">
|
||||
<TabList>
|
||||
<Tab v-for="(change, index) in changes" :value="index"> Change {{ index + 1 }} </Tab>
|
||||
<!--<Tab value="1">Code Diff</Tab>-->
|
||||
<Tab value="class-description">Class RE</Tab>
|
||||
<Tab value="class-code">Actual Class Code</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel v-for="(change, index) in changes" :value="index">
|
||||
<div class="flex grid grid-cols-1 gap-4">
|
||||
<!--<h2>Change {{index}} description</h2>-->
|
||||
<div class="full-width">
|
||||
<MarkdownViewer class="editor" v-model="change.change_description" />
|
||||
</div>
|
||||
<div>
|
||||
<Button clas="align-right" @click="toggleView"> {{ btn_toggle_label }}</Button>
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value="class-description">
|
||||
<p class="m-0" v-if="classLoaded">
|
||||
<MdPreview class="editor" v-model="classDetails.reDescription" language="en-US" />
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel value="class-code">
|
||||
|
||||
<p v-if="classLoaded" class="m-0">
|
||||
<div class="full-width" v-if="!show_as_diff">
|
||||
<HighCode class="code" :codeValue="change.new_code" theme="light" width="100%" height="100%" :codeLines="true" langName="" lang="java" fontSize="12px"></HighCode>
|
||||
</div>
|
||||
<div class="m-0" v-else>
|
||||
<CodeDiff :old-string="change.previous_code" :new-string="change.new_code" output-format="side-by-side" />
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
|
||||
<HighCode
|
||||
class="code"
|
||||
:codeValue="classDetails.code"
|
||||
theme="dark"
|
||||
width="100%"
|
||||
height="100%"
|
||||
:codeLines="true"
|
||||
fontSize="12px"
|
||||
langName=""
|
||||
lang="java"
|
||||
></HighCode>
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
|
||||
|
||||
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
|
||||
<TabPanel value="class-description">
|
||||
<p class="m-0" v-if="classLoaded">
|
||||
<MarkdownViewer class="editor" v-model="classDetails.reDescription" />
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
</TabPanel>
|
||||
<TabPanel value="class-code">
|
||||
<p v-if="classLoaded" class="m-0">
|
||||
<HighCode class="code" :codeValue="classDetails.code" theme="dark" width="100%" height="100%" :codeLines="true" fontSize="12px" langName="" lang="java"></HighCode>
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import Tab from 'primevue/tab';
|
||||
import TabList from 'primevue/tablist';
|
||||
import TabPanel from 'primevue/tabpanel';
|
||||
@@ -88,7 +53,7 @@ import { CodeDiff } from 'v-code-diff';
|
||||
import { defineProps, onMounted, ref, toRefs } from 'vue';
|
||||
import { HighCode } from 'vue-highlight-code';
|
||||
import 'vue-highlight-code/dist/style.css';
|
||||
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
|
||||
//66f55e4b2894530b1c154f69
|
||||
const props = defineProps({
|
||||
@@ -99,39 +64,34 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const { changes } = toRefs(props);
|
||||
const classDetails = ref(null);
|
||||
const classLoaded = ref(false);
|
||||
const classDetails = ref(null);
|
||||
const classLoaded = ref(false);
|
||||
const show_as_diff = ref(false);
|
||||
const btn_toggle_label = ref("Show Diff");
|
||||
const btn_toggle_label = ref('Show Diff');
|
||||
const tabvalue = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
|
||||
});
|
||||
onMounted(() => {});
|
||||
|
||||
function tabUpdate(value) {
|
||||
console.log(value);
|
||||
if ((value === 'class-description' || value ==='class-code') && classLoaded.value === false) {
|
||||
|
||||
console.log("Getting class details : ", changes.value[0].classname);
|
||||
axios.get("/source-module/getClassDetailedInfo?className=" + changes.value[0].classname ).then(resp => {
|
||||
classDetails.value = resp.data;
|
||||
classLoaded.value = true;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during the request:', error);
|
||||
});
|
||||
if ((value === 'class-description' || value === 'class-code') && classLoaded.value === false) {
|
||||
console.log('Getting class details : ', changes.value[0].classname);
|
||||
axios
|
||||
.get('/source-module/getClassDetailedInfo?className=' + changes.value[0].classname)
|
||||
.then((resp) => {
|
||||
classDetails.value = resp.data;
|
||||
classLoaded.value = true;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error during the request:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toggleView() {
|
||||
show_as_diff.value = !show_as_diff.value;
|
||||
btn_toggle_label.value = show_as_diff.value ? "Show Code" : "Show Diff";
|
||||
btn_toggle_label.value = show_as_diff.value ? 'Show Code' : 'Show Diff';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
</style>
|
||||
<style></style>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<TabPanels>
|
||||
<TabPanel value="0">
|
||||
<p class="m-0">
|
||||
<MdPreview class="editor" v-model="change.change_description" language="en-US" />
|
||||
<MarkdownViewer class="editor" v-model="change.change_description" />
|
||||
</p>
|
||||
</TabPanel>
|
||||
<TabPanel value="1">
|
||||
@@ -25,14 +25,14 @@
|
||||
</TabPanel>
|
||||
<TabPanel value="class-description">
|
||||
<p class="m-0" v-if="classLoaded">
|
||||
<MdPreview class="editor" v-model="classDetails.reDescription" language="en-US" />
|
||||
<MarkdownViewer class="editor" v-model="classDetails.reDescription" />
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel value="class-code">
|
||||
|
||||
<p v-if="classLoaded" class="m-0">
|
||||
|
||||
<p v-if="classLoaded" class="m-0">
|
||||
|
||||
<HighCode
|
||||
class="code"
|
||||
@@ -46,18 +46,16 @@
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import Tab from 'primevue/tab';
|
||||
import TabList from 'primevue/tablist';
|
||||
import TabPanel from 'primevue/tabpanel';
|
||||
@@ -67,6 +65,7 @@ import { CodeDiff } from 'v-code-diff';
|
||||
import { defineProps, onMounted, ref, toRefs } from 'vue';
|
||||
import { HighCode } from 'vue-highlight-code';
|
||||
import 'vue-highlight-code/dist/style.css';
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
|
||||
|
||||
//66f55e4b2894530b1c154f69
|
||||
@@ -82,7 +81,7 @@ const classDetails = ref(null);
|
||||
const classLoaded = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
69
src/components/ExecutionInputTable.vue
Normal file
69
src/components/ExecutionInputTable.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
inputs: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
scenario: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['download-file']);
|
||||
|
||||
const filteredInputs = computed(() => {
|
||||
const filtered = {};
|
||||
for (const [key, value] of Object.entries(props.inputs)) {
|
||||
if (!(key.includes('input_multiselect') && key.endsWith('_id'))) {
|
||||
filtered[key] = value;
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
});
|
||||
|
||||
const handleDownload = (filePath) => {
|
||||
emit('download-file', filePath);
|
||||
};
|
||||
|
||||
const getDisplayLabel = (index) => {
|
||||
if (index === 'MultiFileUpload') {
|
||||
return 'Files Uploaded';
|
||||
} else if (index === 'SingleFileUpload') {
|
||||
return 'Parameter';
|
||||
} else if (index.includes('input_multiselect') && index.endsWith('_name')) {
|
||||
return props.scenario.inputs && Array.isArray(props.scenario.inputs) ? props.scenario.inputs.find((i) => i.name === index.replace('_name', ''))?.label : null;
|
||||
} else {
|
||||
return index.replace(/_/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="box p-4 border rounded-md shadow-sm" style="background-color: white">
|
||||
<table class="table-auto w-full border-collapse border border-gray-300">
|
||||
<tbody>
|
||||
<tr v-for="(input, index) in filteredInputs" :key="index">
|
||||
<th class="border border-gray-300 px-4 py-2" :class="{ 'bg-gray-500 text-white': index === 'SingleFileUpload' }">
|
||||
{{ getDisplayLabel(index) }}
|
||||
</th>
|
||||
<td class="border border-gray-300 px-4 py-2">
|
||||
<div v-if="index === 'MultiFileUpload'">
|
||||
{{ filteredInputs.SingleFileUpload.replace(/\\/g, '/').split('/').pop() }}
|
||||
<Button icon="pi pi-download" class="p-button-text p-button-sm" label="Download" @click="handleDownload(inputs['SingleFileUpload'])" />
|
||||
</div>
|
||||
<div v-else-if="index !== 'SingleFileUpload'">{{ input }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.box {
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
86
src/components/ExecutionTimer.vue
Normal file
86
src/components/ExecutionTimer.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup>
|
||||
import moment from 'moment';
|
||||
import { onBeforeUnmount, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const startTime = ref(null);
|
||||
const timerInterval = ref(null);
|
||||
const elapsedTime = ref('00:00');
|
||||
|
||||
const startTimer = () => {
|
||||
startTime.value = Date.now();
|
||||
timerInterval.value = setInterval(() => {
|
||||
const elapsed = moment.duration(Date.now() - startTime.value);
|
||||
elapsedTime.value = moment.utc(elapsed.asMilliseconds()).format('mm:ss');
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const stopTimer = () => {
|
||||
if (timerInterval.value) {
|
||||
clearInterval(timerInterval.value);
|
||||
timerInterval.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const resetTimer = () => {
|
||||
stopTimer();
|
||||
startTime.value = null;
|
||||
elapsedTime.value = '00:00';
|
||||
};
|
||||
|
||||
// Watch for loading changes
|
||||
watch(
|
||||
() => props.isLoading,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
startTimer();
|
||||
} else {
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Cleanup on unmount
|
||||
onBeforeUnmount(() => {
|
||||
stopTimer();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
startTimer,
|
||||
stopTimer,
|
||||
resetTimer
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="isLoading" class="flex flex-col items-center">
|
||||
<div class="flex justify-center mt-4">
|
||||
<jellyfish-loader :loading="isLoading" scale="1" color="#A100FF" />
|
||||
</div>
|
||||
<div v-if="message && message.includes('/')">
|
||||
<span>{{ message }}</span>
|
||||
</div>
|
||||
<div v-else>Starting execution...</div>
|
||||
<div class="flex justify-center" style="margin-bottom: 30px">
|
||||
<p>Time elapsed: </p>
|
||||
<div class="timer">{{ elapsedTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.timer {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
1045
src/components/MarkdownViewer.vue
Normal file
1045
src/components/MarkdownViewer.vue
Normal file
File diff suppressed because it is too large
Load Diff
142
src/components/OldExecutionResponsePanel.vue
Normal file
142
src/components/OldExecutionResponsePanel.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<script setup>
|
||||
import ChangeImpactOutputViewer from '@/components/ChangeImpactOutputViewer.vue';
|
||||
import JsonEditorVue from 'json-editor-vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
|
||||
const props = defineProps({
|
||||
scenario: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
execScenario: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
scenarioOutput: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
executionId: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fileType: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
fileContent: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
rating: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
canUpdateRating: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['download-file']);
|
||||
|
||||
const debug_modal = ref(false);
|
||||
const localExecScenario = ref({});
|
||||
const localScenarioOutput = computed(() => props.scenarioOutput);
|
||||
|
||||
const openDebug = () => {
|
||||
localExecScenario.value = props.execScenario;
|
||||
debug_modal.value = true;
|
||||
};
|
||||
|
||||
const handleDownload = () => {
|
||||
emit('download-file', props.scenarioOutput);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Panel class="mt-6">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">Workflow response</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #icons>
|
||||
<div class="flex justify-end">
|
||||
<Button severity="secondary" rounded @click="openDebug" v-tooltip.left="'View code'">
|
||||
<i class="pi pi-code"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="execScenario.latestStepStatus == 'ERROR'" class="card flex flex-col gap-4 w-full">
|
||||
<div v-if="execScenario.latestStepOutput">
|
||||
<p class="text-red-500 font-bold">Error: {{ execScenario.latestStepOutput }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="text-red-500 font-bold">Error: Execution failed.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="execScenario.latestStepStatus != 'ERROR'" class="card flex flex-col gap-4 w-full">
|
||||
<div v-if="scenario.outputType == 'ciaOutput'">
|
||||
<ChangeImpactOutputViewer :scenario_output="scenarioOutput" />
|
||||
</div>
|
||||
<div v-else-if="isLoading">
|
||||
<div class="flex justify-center mt-4">
|
||||
<jellyfish-loader :loading="isLoading" scale="1" color="#A100FF" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="fileType == 'FILE' && execScenario.execSharedMap.status != null && execScenario.execSharedMap.status === 'DONE'">
|
||||
<ul class="file-list">
|
||||
<li class="file-item">
|
||||
sf_document-{{ executionId }}
|
||||
<Button icon="pi pi-download" class="p-button-text p-button-sm" label="Download" @click="handleDownload" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="fileType == 'MARKDOWN'">
|
||||
<div v-html="fileContent" class="markdown-content"></div>
|
||||
</div>
|
||||
<div v-else-if="fileType == 'JSON'">
|
||||
<pre>{{ fileContent }}</pre>
|
||||
</div>
|
||||
<div v-else>
|
||||
<MarkdownViewer class="editor" :modelValue="localScenarioOutput" background-color="transparent" padding="20px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<Dialog v-model:visible="debug_modal" maximizable modal :header="scenario.name" :style="{ width: '75%' }" :breakpoints="{ '1199px': '75vw', '575px': '90vw' }">
|
||||
<div class="flex">
|
||||
<div class="card flex flex-col gap-4 w-full">
|
||||
<JsonEditorVue v-model="localExecScenario" />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.editor ol {
|
||||
list-style-type: decimal !important;
|
||||
}
|
||||
|
||||
.editor ul {
|
||||
list-style-type: disc !important;
|
||||
}
|
||||
|
||||
/* Removed pre and .markdown-content styles - handled by MarkdownViewer component */
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
215
src/components/ScenarioFileUpload.vue
Normal file
215
src/components/ScenarioFileUpload.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<script setup>
|
||||
import { FileUploadStore } from '@/stores/FileUploadStore';
|
||||
import { useAuth } from '@websanova/vue-auth/src/v3.js';
|
||||
import { usePrimeVue } from 'primevue/config';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
inputName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
tooltipText: {
|
||||
type: String,
|
||||
default: 'Upload files'
|
||||
},
|
||||
uploadUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isMultiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
acceptedFormats: {
|
||||
type: String,
|
||||
default: '.docx'
|
||||
},
|
||||
folderName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
uploadedFiles: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['upload', 'remove', 'update:uploadedFiles']);
|
||||
|
||||
const fileUploadStore = FileUploadStore();
|
||||
const toast = useToast();
|
||||
const auth = useAuth();
|
||||
const $primevue = usePrimeVue();
|
||||
|
||||
const localUploadedFiles = ref(props.uploadedFiles);
|
||||
|
||||
// Sync uploaded files with parent
|
||||
watch(
|
||||
localUploadedFiles,
|
||||
(newFiles) => {
|
||||
emit('update:uploadedFiles', newFiles);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.uploadedFiles,
|
||||
(newFiles) => {
|
||||
localUploadedFiles.value = newFiles;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const onBeforeSend = (event) => {
|
||||
const { xhr } = event;
|
||||
const token = auth.token();
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
|
||||
};
|
||||
|
||||
const handleUpload = (event) => {
|
||||
const { xhr } = event;
|
||||
|
||||
if (xhr.status === 200) {
|
||||
const uploadedFileName = event.files && event.files.length > 0 ? event.files[0].name : 'UnknownFile';
|
||||
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Success',
|
||||
detail: 'File uploaded successfully!',
|
||||
life: 3000
|
||||
});
|
||||
|
||||
emit('upload', {
|
||||
fileName: uploadedFileName,
|
||||
response: xhr.response,
|
||||
files: event.files,
|
||||
inputName: props.inputName
|
||||
});
|
||||
} else {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: `Failed to upload file. Status: ${xhr.status}`,
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = async (event, removeUploadedFileCallback) => {
|
||||
const { file, index } = event;
|
||||
|
||||
try {
|
||||
const response = await fileUploadStore.deleteFile(file.name, props.folderName);
|
||||
|
||||
if (response.status === 200) {
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Success',
|
||||
detail: 'File removed successfully!',
|
||||
life: 3000
|
||||
});
|
||||
|
||||
removeUploadedFileCallback(index);
|
||||
emit('remove', { fileName: file.name, inputName: props.inputName });
|
||||
} else {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: `Failed to remove file. Status: ${response.statusText}`,
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: `Error while removing file: ${error.message}`,
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
const k = 1024;
|
||||
const sizes = $primevue.config.locale.fileSizeTypes;
|
||||
|
||||
if (bytes === 0) {
|
||||
return `0 ${sizes[0]}`;
|
||||
}
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
const truncatedSize = Math.trunc(bytes / Math.pow(k, i));
|
||||
|
||||
return `${truncatedSize} ${sizes[i]}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label :for="inputName">
|
||||
<b>{{ label }}</b>
|
||||
<i class="pi pi-info-circle text-violet-600 cursor-pointer" v-tooltip="tooltipText"></i>
|
||||
</label>
|
||||
<div>
|
||||
<FileUpload
|
||||
:name="inputName"
|
||||
:customUpload="false"
|
||||
:url="uploadUrl"
|
||||
@upload="handleUpload"
|
||||
:multiple="isMultiple"
|
||||
:accept="acceptedFormats"
|
||||
auto
|
||||
:showUploadButton="false"
|
||||
:showCancelButton="false"
|
||||
:maxFileSize="52428800"
|
||||
:invalidFileSizeMessage="'Invalid file size, file size should be smaller than 20 MB'"
|
||||
v-model:files="localUploadedFiles"
|
||||
@before-send="onBeforeSend"
|
||||
>
|
||||
<template #content="{ uploadedFiles, removeUploadedFileCallback }">
|
||||
<div class="pt-4">
|
||||
<div v-if="uploadedFiles.length > 0">
|
||||
<table class="table-auto w-full border-collapse border border-gray-200">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border border-gray-300 p-2">Name</th>
|
||||
<th class="border border-gray-300 p-2">Dimension</th>
|
||||
<th class="border border-gray-300 p-2">Status</th>
|
||||
<th class="border border-gray-300 p-2">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(file, index) in uploadedFiles" :key="file.name + file.size" class="hover:bg-gray-50">
|
||||
<td class="border border-gray-300 p-2">{{ file.name }}</td>
|
||||
<td class="border border-gray-300 p-2">{{ formatSize(file.size) }}</td>
|
||||
<td class="border border-gray-300 p-2">
|
||||
<Badge value="UPLOADED" severity="success" />
|
||||
</td>
|
||||
<td class="border border-gray-300 p-2">
|
||||
<Button label="Remove" @click="handleRemove({ file, index }, removeUploadedFileCallback)" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
<div class="!border !border-violet-600 !rounded-full !w-24 !h-24 flex items-center justify-center">
|
||||
<i class="pi pi-cloud-upload !text-4xl !-violet-600"></i>
|
||||
</div>
|
||||
<p class="mt-2 mb-2 text-m">Drag and drop files here to upload.</p>
|
||||
</div>
|
||||
</template>
|
||||
</FileUpload>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<Tabs value="class-code" @update:value="tabUpdate">
|
||||
|
||||
<TabList>
|
||||
|
||||
<TabList>
|
||||
<Tab value="class-code">Class Code</Tab>
|
||||
<Tab value="class-description">Class RE</Tab>
|
||||
<Tab v-if="methods != null" value="method-list">Method List</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
|
||||
|
||||
|
||||
<TabPanel value="class-code">
|
||||
<p v-if="classLoaded" class="m-0">
|
||||
|
||||
|
||||
|
||||
<TabPanel value="class-code">
|
||||
<p v-if="classLoaded" class="m-0">
|
||||
|
||||
<HighCode
|
||||
class="code"
|
||||
@@ -25,16 +25,16 @@
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
|
||||
|
||||
|
||||
</TabPanel>
|
||||
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel value="class-description">
|
||||
<div class="flex justify-end">
|
||||
<Button label="Execute RE Class"
|
||||
@click="openToastRE"
|
||||
<Button label="Execute RE Class"
|
||||
@click="openToastRE"
|
||||
v-tooltip.left="'Execute reverse engeenering for the class selected'"
|
||||
:disabled="loadingStore.re_loading">
|
||||
|
||||
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
</div>
|
||||
<div v-if="!loadingStore.re_loading">
|
||||
|
||||
|
||||
|
||||
<p class="m-0" v-if="classLoaded">
|
||||
<MdPreview v-if="classDetails.reDescription != null" class="editor" v-model="classDetails.reDescription" language="en-US" />
|
||||
<MarkdownViewer v-if="classDetails.reDescription != null" class="editor" v-model="classDetails.reDescription" />
|
||||
<p v-else> No Description available for this class</p>
|
||||
</p>
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-9">
|
||||
<div v-if="!loadingMethod && selectedMethodDetails != null && selectedMethodDetails.reDescription == null" class="card flow-codeviewer">
|
||||
<div v-if="!loadingMethod && selectedMethodDetails != null && selectedMethodDetails.reDescription == null" class="card flow-codeviewer">
|
||||
<h5>Method Code ( No reverse engineering available )</h5>
|
||||
<HighCode
|
||||
class="code"
|
||||
@@ -76,9 +76,9 @@
|
||||
></HighCode>
|
||||
|
||||
</div>
|
||||
<div v-if="!loadingMethod && selectedMethodDetails != null && selectedMethodDetails.reDescription != null" class="card flow-codeviewer">
|
||||
<div v-if="!loadingMethod && selectedMethodDetails != null && selectedMethodDetails.reDescription != null" class="card flow-codeviewer">
|
||||
<h5>Method Explaination</h5>
|
||||
<MdPreview class="editor" v-model="selectedMethodDetails.reDescription" language="en-US" />
|
||||
<MarkdownViewer class="editor" v-model="selectedMethodDetails.reDescription" />
|
||||
|
||||
|
||||
</div>
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<Skeleton v-else width="100%" height="10rem"></Skeleton>
|
||||
|
||||
</TabPanel>
|
||||
@@ -96,19 +96,19 @@
|
||||
<!-- Dialog per selezionare lo scenario -->
|
||||
<Dialog v-model:visible="showScenarioDialog" header="Select a Scenario" :closable="false" :modal="true" style="width: 400px;">
|
||||
<div>
|
||||
<Dropdown
|
||||
v-model="selectedScenario"
|
||||
:options="scenario_store.scenariosForRE"
|
||||
optionLabel="name"
|
||||
placeholder="Select a Scenario"
|
||||
class="w-full"
|
||||
<Dropdown
|
||||
v-model="selectedScenario"
|
||||
:options="scenario_store.scenariosForRE"
|
||||
optionLabel="name"
|
||||
placeholder="Select a Scenario"
|
||||
class="w-full"
|
||||
/> </div>
|
||||
<div class="flex justify-end mt-3">
|
||||
<Button label="Cancel" severity="secondary" @click="showScenarioDialog = false" class="mr-2" />
|
||||
<Button label="Execute" severity="primary" :disabled="!selectedScenario" @click="executeScenario" />
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -118,8 +118,6 @@ import { LoadingStore } from '@/stores/LoadingStore';
|
||||
import { ScenarioStore } from '@/stores/ScenarioStore';
|
||||
import { UserPrefStore } from '@/stores/UserPrefStore.js';
|
||||
import axios from 'axios';
|
||||
import { MdPreview } from 'md-editor-v3';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import Tab from 'primevue/tab';
|
||||
import TabList from 'primevue/tablist';
|
||||
import TabPanel from 'primevue/tabpanel';
|
||||
@@ -131,6 +129,7 @@ import { defineProps, onMounted, reactive, ref, toRefs } from 'vue';
|
||||
import { HighCode } from 'vue-highlight-code';
|
||||
import 'vue-highlight-code/dist/style.css';
|
||||
import { JellyfishLoader } from "vue3-spinner";
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
import { useLayout } from './useLayout';
|
||||
|
||||
|
||||
@@ -180,7 +179,7 @@ onMounted(() => {
|
||||
loadClassDetails();
|
||||
console.log("class details: ", classDetails.value);
|
||||
console.log("class name: ", className.value);
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -214,14 +213,14 @@ const executeScenario = () => {
|
||||
function checkExtension() {
|
||||
// Ottieni la parte dopo il punto
|
||||
const extension = userPrefStore.getSelFile.split('.').pop();
|
||||
|
||||
|
||||
// Controlla se è "java"
|
||||
if (extension === 'java' || extension === 'jsp') {
|
||||
commonRevRequest.applicationType = extension;
|
||||
} else {
|
||||
commonRevRequest.applicationType = 'GENERIC';
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// Logica per eseguire l'RE
|
||||
@@ -260,7 +259,7 @@ const doREClass = () => {
|
||||
|
||||
const doREClass = () => {
|
||||
|
||||
|
||||
|
||||
|
||||
commonRevRequest.fullClassQualifiedName = className.value;
|
||||
commonRevRequest.applicationName = userPrefStore.getSelApp.internal_name;
|
||||
@@ -278,8 +277,8 @@ const doREClass = () => {
|
||||
life: 3000 // Durata della notifica in millisecondi
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
}*/
|
||||
// Function to start polling
|
||||
@@ -289,7 +288,7 @@ const doREClass = () => {
|
||||
loadingStore.re_loading = true;
|
||||
pollingInterval = setInterval(() => pollREBackendAPI(processId), 5000);
|
||||
console.log("Polling started.");
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Function to stop polling
|
||||
@@ -324,7 +323,7 @@ axios.get('/java-re-module/getProgressRevSingleClass/'+processId).then(response
|
||||
}
|
||||
|
||||
//stopPolling();
|
||||
|
||||
|
||||
/*if (response.data.status == 'OK' || response.data.status == 'ERROR') {
|
||||
console.log("Condition met, stopping polling.");
|
||||
stopPolling();
|
||||
@@ -370,7 +369,7 @@ function loadClassDetails() {
|
||||
if (classDetails.value.methods != null) {
|
||||
methods.value = createMethodList();
|
||||
}
|
||||
|
||||
|
||||
classLoaded.value = true;
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
227
src/components/WorkflowResponsePanel.vue
Normal file
227
src/components/WorkflowResponsePanel.vue
Normal file
@@ -0,0 +1,227 @@
|
||||
<script setup>
|
||||
import ChangeImpactOutputViewer from '@/components/ChangeImpactOutputViewer.vue';
|
||||
import { ScenarioService } from '@/service/ScenarioService';
|
||||
import { ScenarioExecutionStore } from '@/stores/ScenarioExecutionStore';
|
||||
import JsonEditorVue from 'json-editor-vue';
|
||||
import { marked } from 'marked';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { computed, ref } from 'vue';
|
||||
import MarkdownViewer from './MarkdownViewer.vue';
|
||||
|
||||
const props = defineProps({
|
||||
scenario: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
scenarioOutput: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
execId: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
errorMessage: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
erroredExecution: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const scenarioExecutionStore = ScenarioExecutionStore();
|
||||
const toast = useToast();
|
||||
|
||||
const rating = ref(0);
|
||||
const debug_modal = ref(false);
|
||||
const exec_scenario = ref({});
|
||||
const fileContent = ref('');
|
||||
const fileType = ref('');
|
||||
|
||||
const localScenarioOutput = computed(() => props.scenarioOutput);
|
||||
|
||||
const openDebug = async () => {
|
||||
try {
|
||||
const resp = await scenarioExecutionStore.getScenarioExecution(props.execId);
|
||||
exec_scenario.value = resp;
|
||||
debug_modal.value = true;
|
||||
} catch (error) {
|
||||
console.error('Error opening debug:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateRating = async (newRating) => {
|
||||
try {
|
||||
const response = await ScenarioService.updateScenarioExecRating(props.execId, newRating.value);
|
||||
|
||||
if (response.data === 'OK') {
|
||||
rating.value = newRating.value;
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Success',
|
||||
detail: 'Rating updated with success.',
|
||||
life: 3000
|
||||
});
|
||||
} else {
|
||||
console.error('Error during rating update', response.data);
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'Error updating rating. Try later.',
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during backend call:', error);
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Error',
|
||||
detail: 'Error updating rating.',
|
||||
life: 3000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const downloadFile = () => {
|
||||
try {
|
||||
const base64String = props.scenarioOutput;
|
||||
const byteCharacters = atob(base64String);
|
||||
const byteNumbers = Array.from(byteCharacters, (char) => char.charCodeAt(0));
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
const blob = new Blob([byteArray]);
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'sf_document-' + props.execId + '.docx';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error('Error during file download:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const showFileContent = (base64String, type) => {
|
||||
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);
|
||||
}
|
||||
|
||||
const textContent = new TextDecoder().decode(bytes);
|
||||
|
||||
if (type === 'MARKDOWN') {
|
||||
fileContent.value = marked(textContent);
|
||||
} else if (type === 'JSON') {
|
||||
const jsonObject = JSON.parse(textContent);
|
||||
fileContent.value = JSON.stringify(jsonObject, null, 2);
|
||||
} else {
|
||||
fileContent.value = 'Unsupported file type.';
|
||||
}
|
||||
} catch (error) {
|
||||
fileContent.value = 'Error while decoding or parsing file.';
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
showFileContent,
|
||||
fileType
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Panel class="mt-6">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">Workflow Response</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #icons>
|
||||
<div class="flex justify-end">
|
||||
<div class="flex">
|
||||
<Rating :modelValue="rating" :stars="5" @change="updateRating($event)" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button severity="secondary" rounded @click="openDebug" v-tooltip.left="'View execution info'">
|
||||
<i class="pi pi-code"></i>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="erroredExecution" class="card flex flex-col gap-4 w-full">
|
||||
<div v-if="errorMessage">
|
||||
<p class="text-red-500 font-bold">Error: {{ errorMessage }}</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="text-red-500 font-bold">Error: Execution failed.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="card flex flex-col gap-4 w-full">
|
||||
<div v-if="scenario.outputType == 'ciaOutput'">
|
||||
<ChangeImpactOutputViewer :scenario_output="scenarioOutput" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="scenario.outputType == 'file'">
|
||||
<Button icon="pi pi-download" label="Download File" class="p-button-primary" @click="downloadFile" />
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div v-if="fileType == 'FILE'">
|
||||
<ul>
|
||||
<li class="file-item">
|
||||
sf_document-{{ execId }}
|
||||
<Button icon="pi pi-download" class="p-button-text p-button-sm" label="Download" @click="downloadFile()" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-else-if="fileType == 'MARKDOWN'">
|
||||
<div v-html="fileContent" class="markdown-content"></div>
|
||||
</div>
|
||||
<div v-else-if="fileType == 'JSON'">
|
||||
<pre>{{ fileContent }}</pre>
|
||||
</div>
|
||||
<div v-else>
|
||||
<MarkdownViewer class="editor" :modelValue="localScenarioOutput" background-color="white" padding="20px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<Dialog v-model:visible="debug_modal" maximizable modal :header="scenario.name" :style="{ width: '75%' }" :breakpoints="{ '1199px': '75vw', '575px': '90vw' }">
|
||||
<div class="flex">
|
||||
<div class="card flex flex-col gap-4 w-full">
|
||||
<JsonEditorVue v-model="exec_scenario" />
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.editor ol {
|
||||
list-style-type: decimal !important;
|
||||
}
|
||||
|
||||
.editor ul {
|
||||
list-style-type: disc !important;
|
||||
}
|
||||
|
||||
/* Removed pre and .markdown-content styles - handled by MarkdownViewer component */
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user