Files
nexus/frontend/src/components/dashboard/v2/TaskStrip.vue
T
reviewer 6cedd8410f
CI - Build & Test / Backend (.NET) (push) Failing after 23s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 3s
refactor(frontend): deduplicate CSS keyframes, unify types, extract format utils, add UI states, trim mock data
- Remove duplicate @keyframes pulse-* from 3 component files (already in nexus-tokens.css)
- Rename AgentDetail → AgentDetailData in dashboard types to avoid collision with types/agent.ts
- Extract shared formatNumber/initials/formatTime to utils/format.ts
- Simplify FlowBoard: use agentStore modal/selection getters instead of duplicating local state
- Add error banner + empty state to IrisChat; add loading skeleton + error/empty states to TaskStrip
- Remove 105-line unused mockAgents array from useFlowLayout
- Reduce operations store fallbacks from hardcoded preview data to minimal safe defaults
- Update operations store tests to match lean fallback structure
- Net: -73 lines, cleaner imports, fewer magic strings
2026-06-12 17:02:50 +02:00

227 lines
5.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* TaskStrip — Untere Leiste im V2 Dashboard Stage
*
* Props:
* tasks TaskItem[]
*/
import type { TaskItem } from './types'
defineProps<{
tasks: TaskItem[]
loading?: boolean
error?: string | null
}>()
</script>
<template>
<div class="taskstrip v2-scroll">
<!-- Loading skeleton -->
<template v-if="loading">
<div v-for="n in 3" :key="'sk-' + n" class="taskcard skeleton" />
</template>
<!-- Error -->
<div v-else-if="error" class="task-error">
<span class="error-icon"></span> {{ error }}
</div>
<!-- Empty -->
<div v-else-if="!tasks.length" class="task-empty">
No active tasks
</div>
<!-- Tasks -->
<div
v-for="task in tasks"
:key="task.id"
class="taskcard"
:class="`task-${task.status}`"
>
<!-- Priority Badge -->
<span class="prio-badge" :class="`prio-${task.priority}`">
{{ task.priority === 'high' ? 'P0' : task.priority === 'medium' ? 'P1' : 'P2' }}
</span>
<!-- Title -->
<div class="task-title">{{ task.title }}</div>
<!-- Agent -->
<div class="task-agent">{{ task.agent }}</div>
<!-- Progress Bar -->
<div class="task-progress">
<div class="bar-track">
<div
class="bar-fill"
:style="{ width: task.progress + '%' }"
></div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.taskstrip {
display: flex;
flex-direction: row;
gap: 10px;
padding: 0 16px 14px;
overflow-x: auto;
min-height: 0;
flex: 0 0 auto;
}
/* ── Task Card ────────────────────────────────────── */
.taskcard {
min-width: 196px;
max-width: 220px;
flex: 0 0 auto;
background: var(--glass);
border: 1px solid var(--line);
border-radius: var(--r);
padding: 12px 13px;
display: flex;
flex-direction: column;
gap: 6px;
position: relative;
transition: border-color 0.15s, background 0.15s;
}
/* ── Status Variants ──────────────────────────────── */
.task-active {
border-left: 2px solid var(--st-work);
background: rgba(61, 220, 151, 0.04);
}
.task-pending {
border-left: 2px solid var(--st-think);
background: rgba(52, 214, 245, 0.04);
}
.task-blocked {
border-left: 2px solid var(--st-block);
background: rgba(255, 106, 106, 0.04);
}
/* ── Priority Badge ───────────────────────────────── */
.prio-badge {
display: inline-block;
align-self: flex-start;
font-family: 'JetBrains Mono', monospace;
font-size: 9px;
font-weight: 600;
padding: 1px 7px;
border-radius: 20px;
line-height: 1.5;
}
.prio-high {
background: rgba(255, 106, 106, 0.18);
color: var(--st-block);
}
.prio-medium {
background: rgba(124, 108, 255, 0.14);
color: var(--a-mid);
}
.prio-low {
background: rgba(255, 255, 255, 0.06);
color: var(--tx-3);
}
/* ── Title ─────────────────────────────────────────── */
.task-title {
font-family: 'Manrope', sans-serif;
font-size: 12px;
font-weight: 600;
color: var(--tx);
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* ── Agent ─────────────────────────────────────────── */
.task-agent {
font-family: 'JetBrains Mono', monospace;
font-size: 9px;
color: var(--tx-3);
font-variant-numeric: tabular-nums;
}
/* ── Progress Bar ──────────────────────────────────── */
.task-progress {
margin-top: 2px;
}
.bar-track {
height: 3px;
background: rgba(255, 255, 255, 0.06);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.bar-fill {
height: 100%;
border-radius: 2px;
transition: width 0.4s ease;
}
/* Status-specific bar colors */
.task-active .bar-fill {
background: var(--grad);
}
.task-pending .bar-fill {
background: var(--grad);
opacity: 0.45;
}
.task-blocked .bar-fill {
background: var(--st-block);
opacity: 0.55;
}
/* ── Skeleton ─────────────────────────────────── */
.taskcard.skeleton {
height: 98px;
background: var(--glass);
animation: skeleton-pulse 1.5s ease-in-out infinite;
}
@keyframes skeleton-pulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 0.8; }
}
/* ── Error ────────────────────────────────────── */
.task-error {
display: flex;
align-items: center;
gap: 8px;
font-family: 'Manrope', sans-serif;
font-size: 11px;
color: #fda4b0;
padding: 12px;
white-space: nowrap;
}
.error-icon { flex: 0 0 auto; font-size: 14px; }
/* ── Empty ────────────────────────────────────── */
.task-empty {
font-family: 'Manrope', sans-serif;
font-size: 11px;
color: var(--tx-3);
font-style: italic;
padding: 12px;
white-space: nowrap;
}
</style>