This document provides a comprehensive guide to all Vue 3 components in the PilotDeck frontend.
Audience: Developers working on the frontend or integrating new features.
- Component Overview
- Timeline Components
- Project Components
- Layout Components
- Utility Components
- Usage Examples
App.vue
└── TheHeader.vue
└── Router View
├── LoginPage.vue
└── ProjectsPage.vue
├── TheFilters.vue
├── ProjectCard.vue (multiple)
├── ProjectDetailModal.vue
│ ├── AgentTimeline.vue
│ │ └── TimelineItem.vue (multiple)
│ │ └── DataViewer.vue (recursive)
├── ProjectFormModal.vue
├── StatsModal.vue
├── OpsModal.vue
└── Toast.vue
Purpose: Main container for displaying Agent runs and events in chronological order.
Props:
interface Props {
runs: AgentRun[] // Array of agent runs
events: AgentEvent[] // Array of agent events
loading?: boolean // Loading state (default: false)
}Features:
- Merges and sorts runs + events by timestamp (newest first)
- Search bar: searches title, summary, agent ID, status
- Type filter: All / Runs / Events
- Level filter: All / Info+ / Warn+ / Error Only
- Pagination: shows 50 items, "Load More" button for remaining
- Empty state, loading spinner
Example:
<AgentTimeline
:runs="agentStore.runs"
:events="agentStore.events"
:loading="loadingTimeline"
/>Internal State:
searchQuery: Search input textfilterType: 'all' | 'run' | 'event'filterLevel: 'all' | 'info' | 'warn' | 'error'displayLimit: Number of items to show (increments by 50)
Computed:
mergedTimeline: Combined & sorted timelinefilteredTimeline: After applying search & filtersvisibleTimeline: First N items based on displayLimithasMore: Whether more items existremainingCount: Number of hidden items
Purpose: Displays a single timeline item (Agent Run or Event) with expandable details.
Props:
interface Props {
item: TimelineItemData // Run or Event data
initiallyExpanded?: boolean // Default expansion state (default: false)
}Item Types:
- Status badge (running/completed/failed/cancelled)
- Agent ID
- Duration (calculated from startedAt/finishedAt)
- Summary text
- Expandable metrics (DataViewer)
- Links (if any)
- Level badge (debug/info/warn/error) with color coding
- Type badge (note/action/result/error/milestone)
- Title and message
- Expandable data (DataViewer)
Features:
- Click header to toggle expansion
- Copy JSON button (copies entire item as JSON)
- Smooth expand/collapse animation
- Marker icon (R for Run, E for Event)
Example:
<TimelineItem
:item="timelineItem"
:initially-expanded="false"
/>Internal State:
isExpanded: Expansion toggle state
Computed:
isRun: Whether item is an AgentRunstatusClass: CSS class based on status/leveltypeLabel: Human-readable type labelitemTitle: Derived from title/summaryformattedTime: Formatted timestampduration: Duration string (for runs)
Purpose: Recursive JSON data viewer with syntax highlighting and smart formatting.
Props:
interface Props {
data: any // JSON data to display
label?: string // Optional key label
depth?: number // Current recursion depth (default: 0)
maxDepth?: number // Maximum recursion depth (default: 3)
showCopy?: boolean // Show copy button at root (default: true)
}Features:
-
Primitive Types:
- String: Green with quotes, truncated if > 100 chars (expandable)
- Number: Blue
- Boolean: Purple (true/false)
- Null/Undefined: Gray italic
-
Complex Types:
- Object:
{ key: value }with expand/collapse - Array:
[items]with expand/collapse - Collapsed preview shows first few keys/items
- Item count badge
- Object:
-
Interactions:
- Click primitive value to copy
- Click header to toggle expand/collapse
- Copy JSON button at root level
- Recursion depth limit prevents performance issues
Example:
<DataViewer
:data="{ foo: 'bar', nested: { a: 1 } }"
:depth="0"
:max-depth="3"
/>Internal State:
expanded: Expansion state for complex typesstringExpanded: Expansion state for long stringscopied: Copy feedback state
Computed:
isPrimitive: Whether data is primitive typeisArray: Whether data is arraytype: Data type ('string' | 'number' | 'boolean' | 'null' | 'undefined')formattedValue: Formatted primitive valuedisplayString: Truncated or full stringpreviewContent: Collapsed preview textitemCount: Number of items in object/array
Purpose: Display project summary in grid or list view.
Props:
interface Props {
project: Project
viewMode: 'card' | 'list'
}Emits:
click: Project clicked (opens detail modal)edit: Edit button clickeddelete: Delete button clicked
Features:
- Status badge (color-coded)
- Priority indicator
- Progress bar (0-100%)
- Cost vs Budget display
- Tags display
- GitHub/Workspace links (if present)
- Hover actions (Edit, Delete)
Purpose: Full-screen modal showing project details and agent timeline.
Props:
interface Props {
project: Project
}Emits:
close: Close modaledit: Edit button clickeddelete: Delete confirmation
Tabs:
-
项目详情 (Details):
- Description, notes
- Status, priority, progress
- Category, tags
- Budget vs Actual cost
- Created/Updated timestamps
-
Agent 时间线 (Timeline):
- AgentTimeline component
- Loads runs/events on tab switch
Features:
- Tab navigation
- Auto-loads timeline when tab is clicked
- Error handling with retry button
- Edit/Delete actions in footer
Purpose: Create or edit project form.
Props:
interface Props {
project?: Project // If provided, edit mode; else create mode
}Emits:
close: Close modalsubmit: Form submitted with data
Form Fields:
- Name (required)
- Description (textarea)
- Notes (textarea)
- Status (select)
- Priority (select)
- Progress (slider 0-100)
- Cost Total (number)
- Revenue Total (number)
- GitHub URL (text)
- Workspace Path (text)
- Tags (comma-separated)
Validation:
- Name is required
- Progress clamped to 0-100
- Numbers validated
Purpose: Application header with navigation and utilities.
Features:
- Logo and app name
- Theme toggle button (dark/light)
- Operations button (opens OpsModal)
- User menu (logout)
State:
- Uses
useTheme()composable for theme management - Uses
useAuthStore()for user info
Purpose: Filter bar for projects page.
Emits:
update:status: Status filter changedupdate:priority: Priority filter changedupdate:category: Category filter changedupdate:search: Search query changed
Features:
- Status dropdown (all/planning/in-progress/paused/completed/cancelled)
- Priority dropdown (all/low/medium/high/urgent)
- Category input
- Search input
- Clear filters button
Purpose: Toast notification system.
Props:
interface Props {
message: string
type?: 'success' | 'error' | 'info'
duration?: number // Auto-hide duration (ms)
}Features:
- Auto-hide after duration
- Different colors per type
- Slide-in animation
Purpose: Display aggregated project statistics.
Features:
- Total projects count
- By status breakdown
- By priority breakdown
- Financial summary (cost/revenue/profit)
Purpose: Admin operations dashboard.
Features:
- Backup database (download)
- Restore database (upload)
- Deploy trigger (if configured)
- View deploy logs
- Requires admin token in headers
<script setup lang="ts">
import { ref } from 'vue'
import ProjectDetailModal from '@/components/ProjectDetailModal.vue'
import type { Project } from '@/api/types'
const selectedProject = ref<Project | null>(null)
function openProjectDetail(project: Project) {
selectedProject.value = project
}
function closeModal() {
selectedProject.value = null
}
</script>
<template>
<div>
<button @click="openProjectDetail(someProject)">
View Details
</button>
<ProjectDetailModal
v-if="selectedProject"
:project="selectedProject"
@close="closeModal"
@edit="handleEdit"
@delete="handleDelete"
/>
</div>
</template><script setup lang="ts">
import DataViewer from '@/components/timeline/DataViewer.vue'
const complexData = {
user: {
name: 'John Doe',
email: 'john@example.com',
roles: ['admin', 'developer']
},
metrics: {
requests: 12345,
errors: 42
}
}
</script>
<template>
<div class="custom-viewer">
<h3>System Metrics</h3>
<DataViewer
:data="complexData"
:max-depth="2"
/>
</div>
</template><script setup lang="ts">
import { computed } from 'vue'
import { useAgentStore } from '@/stores/agent'
import AgentTimeline from '@/components/timeline/AgentTimeline.vue'
const agentStore = useAgentStore()
// Filter only failed runs and error events
const criticalRuns = computed(() =>
agentStore.runs.filter(r => r.status === 'failed')
)
const criticalEvents = computed(() =>
agentStore.events.filter(e => e.level === 'error')
)
</script>
<template>
<div>
<h2>Critical Issues</h2>
<AgentTimeline
:runs="criticalRuns"
:events="criticalEvents"
/>
</div>
</template>Always import types from @/api/types.ts:
import type { Project, AgentRun, AgentEvent } from '@/api/types'Use TypeScript interfaces for props:
interface Props {
required: string
optional?: number
}
const props = withDefaults(defineProps<Props>(), {
optional: 0
})Always define emits with types:
const emit = defineEmits<{
submit: [data: FormData]
cancel: []
}>()Use design tokens from tokens.css:
.my-component {
background: var(--card-bg);
color: var(--text-primary);
border-radius: var(--border-radius-sm);
box-shadow: var(--shadow-md);
}Components automatically support dark/light themes via CSS variables. No JavaScript theme logic needed in components (handled by [data-theme] attribute on root).
- Component renders correctly in light theme
- Component renders correctly in dark theme
- All props work as expected
- All emits fire correctly
- Error states display properly
- Loading states display properly
- Responsive on mobile/tablet/desktop
- Keyboard navigation works (Tab, Enter, Escape)
- No TypeScript errors
- No console errors/warnings
- ProjectTimeline.vue - Gantt-style project timeline
- AgentInsights.vue - Agent performance analytics dashboard
- TokenUsageChart.vue - Token usage visualization
- NotificationCenter.vue - Persistent notification system
- CommandPalette.vue - Keyboard command palette
- BulkProjectEditor.vue - Multi-select bulk edit interface
Last Updated: 2026-02-11
Version: 1.0 (Timeline System Launch)