Linear integration

This commit is contained in:
Pranav
2025-05-12 21:42:34 -07:00
parent bd571f5a7c
commit 123765b676
11 changed files with 266 additions and 25 deletions
@@ -27,7 +27,7 @@ const props = defineProps({
},
conversationInboxType: {
type: String,
required: true,
default: '',
},
assistants: {
type: Array,
@@ -102,7 +102,7 @@ watch(
ref="chatContainer"
class="flex-1 flex px-4 py-4 overflow-y-auto items-start"
>
<div v-if="messages.length" class="space-y-6 flex-1 flex flex-col">
<div v-if="messages.length" class="space-y-6 flex-1 flex flex-col w-full">
<template
v-for="item in groupedMessages"
:key="item.type === 'message' ? item.message.id : 'thinking-group'"
@@ -17,7 +17,7 @@ const props = defineProps({
},
conversationInboxType: {
type: String,
required: true,
default: '',
},
});
@@ -46,7 +46,7 @@ const useCopilotResponse = () => {
<template>
<div class="flex flex-row gap-2">
<div class="flex flex-col gap-1 text-n-slate-12">
<div class="flex flex-col gap-1 text-n-slate-12 w-full">
<div class="font-medium text-n-slate-12">{{ $t('CAPTAIN.NAME') }}</div>
<span v-if="hasEmptyMessageContent" class="text-n-ruby-11">
{{ $t('CAPTAIN.COPILOT.EMPTY_MESSAGE') }}
@@ -54,11 +54,11 @@ const useCopilotResponse = () => {
<div
v-else
v-dompurify-html="messageContent"
class="prose-sm break-words text-n-slate-11"
class="prose-sm break-words text-n-slate-11 [&_a]:underline [&_a]:text-n-slate-11 [&_a]:hover:text-n-slate-12"
/>
<div class="flex flex-row mt-1">
<Button
v-if="!hasEmptyMessageContent"
v-if="!hasEmptyMessageContent && false"
:label="$t('CAPTAIN.COPILOT.USE')"
faded
sm
@@ -1,24 +1,57 @@
<script setup>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import Icon from '../icon/Icon.vue';
const emit = defineEmits(['useSuggestion']);
const { t } = useI18n();
const route = useRoute();
const promptOptions = [
{
label: 'CAPTAIN.COPILOT.PROMPTS.SUMMARIZE.LABEL',
prompt: 'CAPTAIN.COPILOT.PROMPTS.SUMMARIZE.CONTENT',
},
{
label: 'CAPTAIN.COPILOT.PROMPTS.SUGGEST.LABEL',
prompt: 'CAPTAIN.COPILOT.PROMPTS.SUGGEST.CONTENT',
},
{
label: 'CAPTAIN.COPILOT.PROMPTS.RATE.LABEL',
prompt: 'CAPTAIN.COPILOT.PROMPTS.RATE.CONTENT',
},
];
const routePromptMap = {
conversations: [
{
label: 'CAPTAIN.COPILOT.PROMPTS.SUMMARIZE.LABEL',
prompt: 'CAPTAIN.COPILOT.PROMPTS.SUMMARIZE.CONTENT',
},
{
label: 'CAPTAIN.COPILOT.PROMPTS.SUGGEST.LABEL',
prompt: 'CAPTAIN.COPILOT.PROMPTS.SUGGEST.CONTENT',
},
{
label: 'CAPTAIN.COPILOT.PROMPTS.RATE.LABEL',
prompt: 'CAPTAIN.COPILOT.PROMPTS.RATE.CONTENT',
},
],
dashboard: [
{
label: 'High priority conversations',
prompt: 'Get me a summary of all high priority open conversations',
},
{
label: 'List contacts',
prompt: 'Show me the list of contacts',
},
{
label: 'Summarize articles',
prompt: 'List articles that are in draft',
},
],
};
const getCurrentRoute = () => {
const path = route.path;
if (path.includes('/conversations')) return 'conversations';
if (path.includes('/dashboard')) return 'dashboard';
if (path.includes('/contacts')) return 'contacts';
if (path.includes('/articles')) return 'articles';
return 'dashboard';
};
const promptOptions = computed(() => {
const currentRoute = getCurrentRoute();
return routePromptMap[currentRoute] || routePromptMap.conversations;
});
const handleSuggestion = opt => {
emit('useSuggestion', t(opt.prompt));
@@ -5,7 +5,7 @@ import Copilot from 'dashboard/components-next/copilot/Copilot.vue';
import CopilotActionCableConnector from 'dashboard/helpers/CopilotActionCableConnector';
import { useMapGetter } from 'dashboard/composables/store';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { emitter } from 'shared/helpers/mitt';
const store = useStore();
const currentUser = useMapGetter('getCurrentUser');
const assistants = useMapGetter('captainAssistants/getRecords');
@@ -96,6 +96,13 @@ const initializeWebSocket = () => {
content: data.response.response,
});
isCaptainTyping.value = false;
} else if (data.type === 'ui_event') {
if (data.event === 'ui:linear_ticket_create') {
emitter.emit('ui:linear_ticket_create');
setTimeout(() => {
emitter.emit('ui:linear_ticket_create_data', data.response);
}, 100);
}
} else {
messages.value.push({
id: new Date().getTime(),
@@ -10,6 +10,7 @@ import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
import SearchableDropdown from './SearchableDropdown.vue';
import { LINEAR_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import Button from 'dashboard/components-next/button/Button.vue';
import { useEmitter } from 'dashboard/composables/emitter';
const props = defineProps({
conversationId: {
@@ -204,6 +205,12 @@ const createIssue = async () => {
}
};
useEmitter('ui:linear_ticket_create_data', data => {
formState.title = data.title;
formState.description = data.description;
formState.priority = data.priority;
});
onMounted(getTeams);
</script>
@@ -10,6 +10,7 @@ import { useTrack } from 'dashboard/composables';
import { LINEAR_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
import Button from 'dashboard/components-next/button/Button.vue';
import { useEmitter } from 'dashboard/composables/emitter';
const props = defineProps({
conversationId: {
@@ -94,6 +95,10 @@ watch(
}
);
useEmitter('ui:linear_ticket_create', () => {
shouldShowPopup.value = true;
});
onMounted(() => {
loadLinkedIssue();
});