Skip to content

ThreadsList Component ​

The ThreadsList component provides a comprehensive interface for displaying and managing AI chat threads in your Qelos application. It features a responsive grid layout, thread actions, and seamless integration with the Qelos AI SDK.

Features ​

  • Thread Display: Shows threads in a responsive card grid with title and metadata
  • Thread Actions: Create new threads, select existing threads, and delete threads with confirmation
  • Responsive Design: Mobile-friendly layout that adapts to different screen sizes
  • SDK Integration: Uses the Qelos SDK for all thread operations
  • Loading States: Proper loading indicators and empty state handling
  • Event System: Emits events for thread lifecycle actions

Usage ​

Basic Usage ​

vue
<template>
  <ThreadsList
    v-model:selected="selectedThreadId"
    title="Chat Threads"
    :show-header="true"
    :allow-create="true"
    :allow-delete="true"
    integration="your-integration-id"
    :limit="20"
    :auto-load="true"
    @create-thread="handleCreateThread"
    @select-thread="handleSelectThread"
    @thread-deleted="handleThreadDeleted"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import ThreadsList from '@/modules/pre-designed/components/ThreadsList.vue'

const selectedThreadId = ref<string>('')

function handleCreateThread(thread) {
  // Handle thread creation
  console.log('Thread created:', thread)
}

function handleSelectThread(thread) {
  // Navigate to selected thread or open in chat component
  console.log('Thread selected:', thread._id)
  // The selectedThreadId is automatically updated by v-model
}

function handleThreadDeleted(threadId) {
  // Handle thread deletion completion
  console.log('Thread deleted:', threadId)
  // If the deleted thread was selected, clear the selection
  if (selectedThreadId.value === threadId) {
    selectedThreadId.value = ''
  }
}
</script>

Integration with AiChat Component ​

vue
<template>
  <div class="chat-interface">
    <ThreadsList
      v-if="!selectedThreadId"
      integration="customer-support-integration"
      @select-thread="selectThread"
      @create-thread="handleNewThread"
    />
    <AiChat
      v-else
      :thread-id="selectedThreadId"
      url="/api/ai/customer-support-integration/chat-completion/[threadId]"
      @back="selectedThreadId = null"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import ThreadsList from '@/modules/pre-designed/components/ThreadsList.vue'
import AiChat from '@/modules/pre-designed/components/AiChat.vue'

const selectedThreadId = ref<string>()

function selectThread(thread) {
  selectedThreadId.value = thread._id
}

function handleNewThread(thread) {
  if (thread) {
    selectedThreadId.value = thread._id
  }
}
</script>

Props ​

PropTypeDefaultDescription
titlestring'Threads'Header title displayed above the threads list
show-headerbooleantrueWhether to show the header with title and create button
allow-createbooleantrueWhether to show the "Create New Thread" button
allow-deletebooleantrueWhether to show delete buttons on thread cards
integrationstringundefinedIntegration ID to filter threads by (optional)
limitnumber20Number of threads to load
auto-loadbooleantrueWhether to automatically load threads on component mount
selected (v-model)string''The ID of the currently selected thread. Use v-model:selected to bind to this value.

Slots ​

Header Slot ​

You can customize the header section using the header slot. This slot provides access to the title and createThread function, allowing you to create a completely custom header layout.

vue
<template>
  <ThreadsList integration="customer-support" :show-header="false">
    <template #header="{ title, createThread }">
      <div class="custom-header">
        <el-icon class="header-icon"><ChatDotRound /></el-icon>
        <h2>{{ title }}</h2>
        <el-button 
          type="success" 
          :icon="Plus"
          @click="createThread"
          class="custom-create-btn"
        >
          Start New Conversation
        </el-button>
      </div>
    </template>
  </ThreadsList>
</template>

<script setup lang="ts">
import { ChatDotRound, Plus } from '@element-plus/icons-vue'
</script>

<style scoped>
.custom-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-radius: 8px;
  margin-bottom: 16px;
}

.header-icon {
  font-size: 24px;
}

.custom-create-btn {
  margin-left: auto;
}
</style>

Slot Props:

  • title: string - The title prop value passed to the component
  • createThread: () => Promise<void> - Function to create a new thread

Note: When using the header slot, you may want to set :show-header="false" to hide the default header.

Events ​

EventPayloadDescription
create-threadthread?: IThreadEmitted when the "Create New Thread" button is clicked. Includes the created thread if integration is provided.
select-threadthread: IThreadEmitted when a thread card is clicked
thread-deletedthreadId: stringEmitted after a thread is successfully deleted

SDK Integration ​

The component uses the Qelos AI SDK for thread operations:

typescript
// List threads
const threads = await sdk.ai.threads.list({
  integration: 'integration-id',
  limit: 20,
  sort: '-updated'
});

// Create thread
const thread = await sdk.ai.threads.create({
  integration: 'integration-id',
  title: 'New Thread'
});

// Delete thread
await sdk.ai.threads.delete('thread-id');

Styling ​

The component uses scoped CSS with Element Plus design tokens:

CSS Custom Properties ​

You can customize the appearance using CSS variables:

css
.threads-list {
  --threads-grid-min-width: 300px;
  --threads-gap: 16px;
}

Component Structure ​

html
<div class="threads-list">
  <div class="threads-list-header">
    <h3>{{ title }}</h3>
    <el-button class="create-thread-btn">Create New Thread</el-button>
  </div>
  
  <div class="threads-content">
    <div class="threads-grid">
      <!-- Thread cards -->
    </div>
  </div>
</div>

CSS Classes for Custom Styling ​

The component exposes specific CSS classes that you can target from outside the component:

.create-thread-btn ​

This class is applied to the "Create New Thread" button in the default header. You can use it to customize the button's appearance:

css
/* Global styles in your main CSS file */
.threads-list .create-thread-btn {
  background: linear-gradient(45deg, #ff6b6b, #ee5a24);
  border: none;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  box-shadow: 0 4px 15px rgba(238, 90, 36, 0.3);
}

.threads-list .create-thread-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(238, 90, 36, 0.4);
}

.threads-list-header ​

This class is applied to the default header container when not using the header slot:

css
.threads-list .threads-list-header {
  background: #f8f9fa;
  padding: 24px;
  border-radius: 12px;
  border: 1px solid #e9ecef;
  margin-bottom: 20px;
}

Advanced Usage ​

Programmatic Control ​

vue
<template>
  <div>
    <ThreadsList ref="threadsListRef" />
    <el-button @click="refreshThreads">Refresh</el-button>
    <el-button @click="loadCustomPage">Load Page 2</el-button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import ThreadsList from '@/modules/pre-designed/components/ThreadsList.vue'

const threadsListRef = ref()

function refreshThreads() {
  threadsListRef.value?.refresh()
}

async function loadCustomPage() {
  // Access internal loadThreads method
  await threadsListRef.value?.loadThreads()
}
</script>

Custom Integration Filtering ​

vue
<template>
  <ThreadsList
    integration="customer-support-integration"
    :limit="10"
    :allow-create="false"
    title="Customer Support History"
    @select-thread="viewThread"
  />
</template>

Conditional Header ​

vue
<template>
  <ThreadsList
    :show-header="isAdmin"
    :allow-create="isAdmin"
    :allow-delete="isAdmin"
    integration="user-integration"
  />
</template>

<script setup lang="ts">
import { computed } from 'vue'

const isAdmin = computed(() => userRole.value === 'admin')
</script>

TypeScript Interfaces ​

IThread ​

typescript
interface IThread {
  _id?: string;
  integration: string;
  title?: string;
  messages: IMessage[];
  messageSummaries: IMessageSummary[];
  created?: Date;
  updated?: Date;
  user?: string;
  workspace?: string;
}

IMessage ​

typescript
interface IMessage {
  role: string;
  content: string;
  timestamp?: Date;
  tool_calls?: any[];
  name?: string;
  tool_call_id?: string;
  function_call?: any;
  message_id?: string;
}

Accessibility ​

The component follows accessibility best practices:

  • Semantic HTML structure
  • Keyboard navigation support
  • Screen reader friendly labels
  • ARIA attributes where appropriate
  • Focus management for interactive elements

Performance Considerations ​

  • Lazy Loading: Threads are loaded on demand with pagination
  • Efficient Rendering: Uses Vue's reactivity system for optimal updates
  • Memory Management: Proper cleanup of API calls and event listeners
  • Responsive Images: Thread previews are truncated to prevent layout shifts

Troubleshooting ​

Common Issues ​

  1. Threads not loading: Check that the integration ID is correct and the user has permissions
  2. Empty state showing: Verify that threads exist for the given integration
  3. Delete not working: Ensure the user has delete permissions for the integration

Debug Mode ​

Enable debug logging to troubleshoot issues:

vue
<script setup lang="ts">
// Check browser console for detailed logs
</script>

API Reference ​

Exposed Methods ​

MethodParametersReturnsDescription
loadThreadspage?: numberPromise<void>Loads threads from the API
refresh-Promise<void>Refreshes the current thread list

Dependencies ​

  • Vue 3 Composition API
  • Element Plus UI components
  • Qelos AI SDK
  • TypeScript for type safety

Build SaaS Products Without Limits.