Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display GenAI analysis in the case page in the Web UI #5301

Merged
merged 6 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/dispatch/static/dispatch/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ declare module '@vue/runtime-core' {
DefaultLayout: typeof import('./src/components/layouts/DefaultLayout.vue')['default']
DMenu: typeof import('./src/components/DMenu.vue')['default']
DTooltip: typeof import('./src/components/DTooltip.vue')['default']
GenaiAnalysisDisplay: typeof import('./src/components/GenaiAnalysisDisplay.vue')['default']
IconPickerInput: typeof import('./src/components/IconPickerInput.vue')['default']
InfoWidget: typeof import('./src/components/InfoWidget.vue')['default']
Loading: typeof import('./src/components/Loading.vue')['default']
Expand Down
6 changes: 6 additions & 0 deletions src/dispatch/static/dispatch/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/dispatch/static/dispatch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"d3-force": "^3.0.0",
"date-fns": "^2.30.0",
"date-fns-tz": "^1.3.8",
"dompurify": "^3.1.7",
"dotenv": "^16.3.1",
"font-awesome": "^4.7.0",
"happy-dom": "^12.10.3",
Expand Down
37 changes: 22 additions & 15 deletions src/dispatch/static/dispatch/src/case/Page.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<template>
<div>
<PageHeader
:case-id="caseDetails.id"
:case-description="caseDetails.description"
:case-genai-analysis="caseDetails.genai_analysis"
:case-id="caseDetails.id"
:case-name="caseDetails.name"
:case-visibility="caseDetails.visibility"
:case-status="caseDetails.status"
:case-title="caseDetails.title"
:case-updated-at="caseDetails.updated_at"
:case-visibility="caseDetails.visibility"
:is-drawer-open="isDrawerOpen"
:active-tab="activeTab"
@toggle-drawer="toggleDrawer"
Expand All @@ -27,6 +28,11 @@
class="pl-8 pb-6"
@update:model-value="handleDescriptionUpdate"
/>
<GenaiAnalysisDisplay
v-if="activeTab !== 'signals' && activeTab !== 'graph'"
:analysis="caseDetails.genai_analysis"
class="pl-8 pb-6"
/>
<CaseStatusSelectGroup
v-if="activeTab !== 'signals' && activeTab !== 'graph'"
v-model="caseDetails"
Expand All @@ -43,55 +49,56 @@
</template>

<script setup lang="ts">
import { debounce } from "lodash"
import { ref, watch } from "vue"
import { useStore } from "vuex"
import { useRoute } from "vue-router"

import { debounce } from "lodash"
import { useSavingState } from "@/composables/useSavingState"
import { useStore } from "vuex"

import CaseApi from "@/case/api"
import CaseAttributesDrawer from "@/case/CaseAttributesDrawer.vue"
import PageHeader from "@/case//PageHeader.vue"
import CaseStatusSelectGroup from "@/case/CaseStatusSelectGroup.vue"
import CaseTabs from "@/case/CaseTabs.vue"
import PageHeader from "@/case/PageHeader.vue"
import RichEditor from "@/components/RichEditor.vue"
import CaseStatusSelectGroup from "@/case/CaseStatusSelectGroup.vue"
import { useSavingState } from "@/composables/useSavingState"
import GenaiAnalysisDisplay from "@/components/GenaiAnalysisDisplay.vue"

const route = useRoute()
const store = useStore()

const caseDefaults = {
status: "New",
assignee: null,
case_priority: null,
case_severity: null,
case_type: null,
closed_at: null,
conversation: null,
description: "",
documents: [],
duplicates: [],
escalated_at: null,
participants: [],
events: [],
genai_analysis: null,
groups: [],
id: null,
incidents: [],
name: null,
participants: [],
project: null,
related: [],
reporter: null,
reported_at: null,
resolution_reason: "",
reporter: null,
resolution: "",
title: "",
resolution_reason: "",
signal_instances: [],
status: "New",
storage: null,
tags: [],
ticket: null,
title: "",
triage_at: null,
updated_at: null,
visibility: "",
conversation: null,
workflow_instances: null,
}

Expand Down Expand Up @@ -173,7 +180,7 @@ watch(

<style scoped>
.container {
max-width: 1920px; /* for example */
max-width: 1920px;
padding-left: 1rem;
padding-right: 1rem;
margin-left: auto;
Expand Down
9 changes: 5 additions & 4 deletions src/dispatch/static/dispatch/src/case/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ const getDefaultSelectedState = () => {
case_priority: null,
case_severity: null,
case_type: null,
dedicated_channel: true,
closed_at: null,
conversation: null,
dedicated_channel: true,
description: null,
documents: [],
duplicates: [],
escalated_at: null,
events: [],
genai_analysis: null,
groups: [],
id: null,
incidents: [],
Expand All @@ -30,10 +32,10 @@ const getDefaultSelectedState = () => {
participant: null,
project: null,
related: [],
reporter: null,
reported_at: null,
resolution_reason: null,
reporter: null,
resolution: null,
resolution_reason: null,
saving: false,
signals: [],
status: null,
Expand All @@ -44,7 +46,6 @@ const getDefaultSelectedState = () => {
triage_at: null,
updated_at: null,
visibility: null,
conversation: null,
workflow_instances: null,
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<template>
<div>
<h2>GenAI Analysis</h2>
<div v-if="analysis && Object.keys(analysis).length > 0">
<div v-for="(value, key) in analysis" :key="key" class="analysis-item">
<h3>
<b>{{ key }}</b>
</h3>
<div v-if="isObject(value)">
<!-- Render each sub-value with formatText to handle links and code formatting -->
<span

Check warning on line 11 in src/dispatch/static/dispatch/src/components/GenaiAnalysisDisplay.vue

View workflow job for this annotation

GitHub Actions / build

Require self-closing on HTML elements (<span>)
v-for="(subValue, subKey) in value"
:key="subKey"
v-html="formatText(subValue)"
mvilanova marked this conversation as resolved.
Show resolved Hide resolved
></span>
</div>
<span v-else v-html="formatText(value)"></span>

Check warning on line 17 in src/dispatch/static/dispatch/src/components/GenaiAnalysisDisplay.vue

View workflow job for this annotation

GitHub Actions / build

Require self-closing on HTML elements (<span>)
</div>
</div>
<div v-else>
<p>A GenAI analysis does not exist for this case.</p>
</div>
</div>
</template>

<script setup lang="ts">
import { defineProps } from "vue"
import DOMPurify from "dompurify"

// Define the `analysis` prop
defineProps({
analysis: {
type: Object,
required: false,
default: null,
},
})

// Helper function to check if a value is an object
function isObject(value) {
return typeof value === "object" && value !== null
}

// Function to format text, converting <URL|text> format to clickable links and wrapping `code` with <code> tags
function formatText(text) {
if (typeof text !== "string") return text

// Convert <URL|text> format to clickable links
let formattedText = text.replace(/<([^|]+)\|([^>]+)>/g, '<a href="$1" target="_blank">$2</a>')

// Convert `code` format to <code>code</code> for inline code styling
formattedText = formattedText.replace(/`([^`]+)`/g, "<code>$1</code>")

// Sanitize the formatted text to avoid XSS
return DOMPurify.sanitize(formattedText)
}
</script>

<style scoped>
h2 {
margin-bottom: 1rem;
font-size: 1.5em;
}

.analysis-item h3 {
margin-top: 1em;
}

a {
color: #1a73e8;
text-decoration: underline;
}

code {
font-family: monospace;
background-color: #f5f5f5;
padding: 0.2em 0.4em;
border-radius: 4px;
}
</style>
Loading