diff --git a/app/javascript/dashboard/components-next/captain/PageLayout.vue b/app/javascript/dashboard/components-next/captain/PageLayout.vue index 817608f62..6c0d37d15 100644 --- a/app/javascript/dashboard/components-next/captain/PageLayout.vue +++ b/app/javascript/dashboard/components-next/captain/PageLayout.vue @@ -182,7 +182,8 @@ const handleCreateAssistant = () => { -
+
+
+import { computed } from 'vue'; +import { useMapGetter } from 'dashboard/composables/store'; + +const currentUser = useMapGetter('getCurrentUser'); + +const firstName = computed(() => { + const name = currentUser.value?.name?.trim(); + return name ? name.split(' ')[0] : 'there'; +}); + +// The summary is generated by the model from the assistant's stats using the +// captain_overview_summary.liquid prompt (lib/integrations/openai/openai_prompts). +// DUMMY: stands in for an LLM-generated summary. In production this markdown +// string would come back from the model after we feed it the assistant's +// stats; numbers are emphasised with **bold** so we can highlight them. +const welcomeMarkdown = computed( + () => + `Hey ${firstName.value}, your assistant handled **1,248 conversations** this month and saved your team **612 hours** of work. Auto-resolution climbed **4.1%** while keeping handoffs low. Knowledge coverage is running strong as well.\n\nI'd keep an eye out for credits, you seem to have exhaused nearly **80%** of it.` +); + +// Split the markdown on **bold** runs so we can render emphasised numbers as +// brand-highlighted spans with Tailwind (instead of styling raw HTML). +const segments = computed(() => { + const parts = []; + const regex = /\*\*(.+?)\*\*/g; + let lastIndex = 0; + let match = regex.exec(welcomeMarkdown.value); + while (match) { + if (match.index > lastIndex) { + parts.push({ text: welcomeMarkdown.value.slice(lastIndex, match.index) }); + } + parts.push({ text: match[1], bold: true }); + lastIndex = regex.lastIndex; + match = regex.exec(welcomeMarkdown.value); + } + if (lastIndex < welcomeMarkdown.value.length) { + parts.push({ text: welcomeMarkdown.value.slice(lastIndex) }); + } + return parts.map((part, index) => ({ ...part, key: index })); +}); + + + diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index a0a96b09a..510829900 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -394,15 +394,14 @@ "HEADER_KNOW_MORE": "Know more", "OVERVIEW": { "HEADER": "Overview", + "WELCOME": { + "LABEL": "Captain summary" + }, "INBOX_BANNER": { "TEXT": "This assistant isn't connected to any inbox yet, so it won't respond to conversations.", "ACTION": "Connect inbox", "DISMISS": "Dismiss" }, - "PERFORMANCE": { - "TITLE": "Performance", - "SUBTITLE": "How this assistant is doing" - }, "RANGES": { "DAYS": "{count}d" }, diff --git a/app/javascript/dashboard/routes/dashboard/captain/assistants/overview/Index.vue b/app/javascript/dashboard/routes/dashboard/captain/assistants/overview/Index.vue index 960d407ea..c01f9b1a9 100644 --- a/app/javascript/dashboard/routes/dashboard/captain/assistants/overview/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/captain/assistants/overview/Index.vue @@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'; import { FEATURE_FLAGS } from 'dashboard/featureFlags'; import PageLayout from 'dashboard/components-next/captain/PageLayout.vue'; +import WelcomeCard from 'dashboard/components-next/captain/pageComponents/overview/WelcomeCard.vue'; import MetricCard from 'dashboard/components-next/captain/pageComponents/overview/MetricCard.vue'; import KnowledgeCard from 'dashboard/components-next/captain/pageComponents/overview/KnowledgeCard.vue'; import ResponseQualityCard from 'dashboard/components-next/captain/pageComponents/overview/ResponseQualityCard.vue'; @@ -80,38 +81,29 @@ const metrics = computed(() => [ :show-know-more="false" :feature-flag="FEATURE_FLAGS.CAPTAIN" > +