chore: Improvements in scenarios (#12098)

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Sivin Varghese
2025-08-14 19:07:28 +05:30
committed by GitHub
parent b809cd2f15
commit 91d80004a6
3 changed files with 59 additions and 28 deletions

View File

@@ -52,10 +52,10 @@ const handleBreadcrumbClick = item => {
<template> <template>
<section <section
class="mt-4 px-10 flex flex-col w-full h-screen overflow-y-auto bg-n-background" class="px-6 flex flex-col w-full h-screen overflow-y-auto bg-n-background"
> >
<div class="max-w-[60rem] mx-auto flex flex-col w-full h-full mb-4"> <div class="max-w-[60rem] mx-auto flex flex-col w-full h-full mb-4">
<header class="mb-7 sticky top-0 z-10 bg-n-background"> <header class="mb-7 sticky top-0 bg-n-background pt-4 z-20">
<Breadcrumb :items="breadcrumbItems" @click="handleBreadcrumbClick" /> <Breadcrumb :items="breadcrumbItems" @click="handleBreadcrumbClick" />
</header> </header>
<main class="flex gap-16 w-full flex-1 pb-16"> <main class="flex gap-16 w-full flex-1 pb-16">

View File

@@ -1,7 +1,7 @@
<script setup> <script setup>
import { computed, h, reactive } from 'vue'; import { computed, h, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useToggle } from '@vueuse/core'; import { useToggle, useElementSize } from '@vueuse/core';
import { useVuelidate } from '@vuelidate/core'; import { useVuelidate } from '@vuelidate/core';
import { required, minLength } from '@vuelidate/validators'; import { required, minLength } from '@vuelidate/validators';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter'; import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
@@ -11,6 +11,7 @@ import TextArea from 'dashboard/components-next/textarea/TextArea.vue';
import Editor from 'dashboard/components-next/Editor/Editor.vue'; import Editor from 'dashboard/components-next/Editor/Editor.vue';
import CardLayout from 'dashboard/components-next/CardLayout.vue'; import CardLayout from 'dashboard/components-next/CardLayout.vue';
import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue'; import Checkbox from 'dashboard/components-next/checkbox/Checkbox.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
const props = defineProps({ const props = defineProps({
id: { id: {
@@ -31,7 +32,7 @@ const props = defineProps({
}, },
tools: { tools: {
type: Array, type: Array,
required: true, default: () => [],
}, },
selectable: { selectable: {
type: Boolean, type: Boolean,
@@ -60,7 +61,13 @@ const state = reactive({
instruction: '', instruction: '',
}); });
const instructionContentRef = ref();
const [isEditing, toggleEditing] = useToggle(); const [isEditing, toggleEditing] = useToggle();
const [isInstructionExpanded, toggleInstructionExpanded] = useToggle();
const { height: contentHeight } = useElementSize(instructionContentRef);
const needsOverlay = computed(() => contentHeight.value > 160);
const startEdit = () => { const startEdit = () => {
Object.assign(state, { Object.assign(state, {
@@ -111,7 +118,7 @@ const LINK_INSTRUCTION_CLASS =
const renderInstruction = instruction => () => const renderInstruction = instruction => () =>
h('p', { h('p', {
class: `text-sm text-n-slate-12 py-4 mb-0 [&_ol]:list-decimal ${LINK_INSTRUCTION_CLASS}`, class: `text-sm text-n-slate-12 py-4 mb-0 prose prose-sm min-w-0 break-words max-w-none ${LINK_INSTRUCTION_CLASS}`,
innerHTML: instruction, innerHTML: instruction,
}); });
</script> </script>
@@ -157,8 +164,38 @@ const renderInstruction = instruction => () =>
/> />
</div> </div>
</div> </div>
<component :is="renderInstruction(formatMessage(instruction, false))" />
<span class="text-sm text-n-slate-11 font-medium mb-1"> <div
class="relative overflow-hidden transition-all duration-300 ease-in-out group/expandable"
:class="{ 'cursor-pointer': needsOverlay }"
:style="{
maxHeight: isInstructionExpanded ? `${contentHeight}px` : '10rem',
}"
@click="needsOverlay ? toggleInstructionExpanded() : null"
>
<div ref="instructionContentRef">
<component
:is="renderInstruction(formatMessage(instruction, false))"
/>
</div>
<div
class="absolute bottom-0 w-full flex items-end justify-center text-xs text-n-slate-11 bg-gradient-to-t h-40 from-n-solid-2 via-n-solid-2 via-10% to-transparent transition-all duration-500 ease-in-out px-2 py-1 rounded pointer-events-none"
:class="{
'visible opacity-100': !isInstructionExpanded,
'invisible opacity-0': isInstructionExpanded || !needsOverlay,
}"
>
<Icon
icon="i-lucide-chevron-down"
class="text-n-slate-7 mb-4 size-4 group-hover/expandable:text-n-slate-11 transition-colors duration-200"
/>
</div>
</div>
<span
v-if="tools?.length"
class="text-sm text-n-slate-11 font-medium mb-1"
>
{{ t('CAPTAIN.ASSISTANTS.SCENARIOS.ADD.SUGGESTED.TOOLS_USED') }} {{ t('CAPTAIN.ASSISTANTS.SCENARIOS.ADD.SUGGESTED.TOOLS_USED') }}
{{ tools?.map(tool => `@${tool}`).join(', ') }} {{ tools?.map(tool => `@${tool}`).join(', ') }}
</span> </span>

View File

@@ -6,6 +6,7 @@ import { picoSearch } from '@scmmishra/pico-search';
import { useStore, useMapGetter } from 'dashboard/composables/store'; import { useStore, useMapGetter } from 'dashboard/composables/store';
import { useAlert } from 'dashboard/composables'; import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings'; import { useUISettings } from 'dashboard/composables/useUISettings';
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import Button from 'dashboard/components-next/button/Button.vue'; import Button from 'dashboard/components-next/button/Button.vue';
import Input from 'dashboard/components-next/input/Input.vue'; import Input from 'dashboard/components-next/input/Input.vue';
@@ -20,6 +21,7 @@ const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const store = useStore(); const store = useStore();
const { uiSettings, updateUISettings } = useUISettings(); const { uiSettings, updateUISettings } = useUISettings();
const { formatMessage } = useMessageFormatter();
const assistantId = route.params.assistantId; const assistantId = route.params.assistantId;
const uiFlags = useMapGetter('captainScenarios/getUIFlags'); const uiFlags = useMapGetter('captainScenarios/getUIFlags');
@@ -42,35 +44,25 @@ const breadcrumbItems = computed(() => {
]; ];
}); });
const TOOL_LINK_REGEX = /\[([^\]]+)]\(tool:\/\/.+?\)/g; const LINK_INSTRUCTION_CLASS =
'[&_a[href^="tool://"]]:text-n-iris-11 [&_a:not([href^="tool://"])]:text-n-slate-12 [&_a]:pointer-events-none [&_a]:cursor-default';
const renderInstruction = instruction => () => const renderInstruction = instruction => () =>
h('span', { h('span', {
class: 'text-sm text-n-slate-12 py-4', class: `text-sm text-n-slate-12 py-4 prose prose-sm min-w-0 break-words ${LINK_INSTRUCTION_CLASS}`,
innerHTML: instruction.replace( innerHTML: instruction,
TOOL_LINK_REGEX,
(_, title) =>
`<span class="text-n-iris-11 font-medium">@${title.replace(/^@/, '')}</span>`
),
}); });
// Suggested example scenarios for quick add // Suggested example scenarios for quick add
const scenariosExample = [ const scenariosExample = [
{ {
id: 1, id: 1,
title: 'Refund Order', title: 'Prospective Buyer',
description: 'User encountered a technical issue or error message.', description:
'Handle customers who are showing interest in purchasing a license',
instruction: instruction:
'Ask for steps to reproduce + browser/app version. Use [Known Issues](tool://known_issues) to check if its a known bug. File with [Create Bug Report](tool://bug_report_create) if new.', 'If someone is interested in purchasing a license, ask them for following:\n\n1. How many licenses are they willing to purchase?\n2. Are they migrating from another platform?\n. Once these details are collected, do the following steps\n1. add a private note to with the information you collected using [Add Private Note](tool://add_private_note)\n2. Add label "sales" to the contact using [Add Label to Conversation](tool://add_label_to_conversation)\n3. Reply saying "one of us will reach out soon" and provide an estimated timeline for the response and [Handoff to Human](tool://handoff)',
tools: ['create_bug_report', 'known_issues'], tools: ['add_private_note', 'add_label_to_conversation', 'handoff'],
},
{
id: 2,
title: 'Product Recommendation',
description: 'User is unsure which product or service to choose.',
instruction:
'Ask 23 clarifying questions. Use [Product Match](tool://product_match[user_needs]) and suggest 23 options with pros/cons. Link to compare page if available.',
tools: ['product_match[user_needs]'],
}, },
]; ];
@@ -248,7 +240,9 @@ onMounted(() => {
<span class="text-sm text-n-slate-11 mt-2"> <span class="text-sm text-n-slate-11 mt-2">
{{ item.description }} {{ item.description }}
</span> </span>
<component :is="renderInstruction(item.instruction)" /> <component
:is="renderInstruction(formatMessage(item.instruction, false))"
/>
<span class="text-sm text-n-slate-11 font-medium mb-1"> <span class="text-sm text-n-slate-11 font-medium mb-1">
{{ t('CAPTAIN.ASSISTANTS.SCENARIOS.ADD.SUGGESTED.TOOLS_USED') }} {{ t('CAPTAIN.ASSISTANTS.SCENARIOS.ADD.SUGGESTED.TOOLS_USED') }}
{{ item.tools?.map(tool => `@${tool}`).join(', ') }} {{ item.tools?.map(tool => `@${tool}`).join(', ') }}