Files
nexus/frontend/src/views/Dashboard/FlowBoard.vue
T
reviewer 0f8939306d
CI - Build & Test / Backend (.NET) (push) Successful in 26s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 3s
feat: mobile-responsive dashboard v2
2026-06-14 12:16:06 +02:00

193 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">
/**
* FlowBoard Das neue V2 Dashboard
*
* Layout:
* Stage (AlertBar + FlowCanvas) + Rail (IrisChat) + TaskStrip (unten)
*
* Datenquellen:
* - AgentStore: agents, models, AlertBar-Metriken, Modal-Status
* - ChatStore: messages, isThinking, sendMessage()
* - TaskStore: tasks
*
* Polling startet bei Mount, stoppt bei Unmount.
*/
import { ref, onMounted, onUnmounted } from 'vue'
import { useAgentStore } from '../../stores/agents'
import { useChatStore } from '../../stores/chat'
import { useTaskStore } from '../../stores/tasks'
import AlertBar from '../../components/dashboard/v2/AlertBar.vue'
import FlowCanvas from '../../components/dashboard/v2/FlowCanvas.vue'
import IrisChat from '../../components/dashboard/v2/IrisChat.vue'
import TaskStrip from '../../components/dashboard/v2/TaskStrip.vue'
import AgentDetailModal from '../../components/dashboard/v2/AgentDetailModal.vue'
import type { AgentNodeData } from '../../composables/useFlowLayout'
import { extraAgentPool } from '../../composables/useFlowLayout'
/* ── Stores ──────────────────────────────────────── */
const agentStore = useAgentStore()
const chatStore = useChatStore()
const taskStore = useTaskStore()
/* ── Agent Layout State ───────────────────────────── */
const agentPositions = ref<Record<string, { x: number; y: number }>>({})
const enteringIds = ref<string[]>([])
const localAgentPool = ref<AgentNodeData[]>([...extraAgentPool])
/* ── Event Handlers ───────────────────────────────── */
function handleSelect(id: string) {
agentStore.selectAgent(id)
}
function handleCloseModal() {
agentStore.selectAgent(null)
}
function handleChangeModel(agentId: string, modelAlias: string) {
// Modal emits the alias (display name); resolve to model ID for the API
const model = agentStore.models.find(m => m.alias === modelAlias)
const modelId = model?.id ?? modelAlias
agentStore.changeModel(agentId, modelId)
}
function handleAdd() {
const pool = localAgentPool.value
if (pool.length === 0) return
const next = pool.shift()!
enteringIds.value.push(next.id)
agentStore.agents.push(next)
setTimeout(() => {
const idx = enteringIds.value.indexOf(next.id)
if (idx !== -1) enteringIds.value.splice(idx, 1)
}, 600)
}
function handleResetLayout() {
agentPositions.value = {}
}
function handleUpdatePositions(pos: Record<string, { x: number; y: number }>) {
agentPositions.value = { ...pos }
}
function handleBlockerClick() {
console.log('[FlowBoard] blocker clicked')
}
function handleChatSend(text: string) {
chatStore.sendMessage(text)
}
/* ── Lifecycle ────────────────────────────────────── */
onMounted(() => {
agentStore.startPolling()
chatStore.startPolling()
taskStore.startPolling()
})
onUnmounted(() => {
agentStore.stopPolling()
chatStore.stopPolling()
taskStore.stopPolling()
})
</script>
<template>
<div class="flow-board">
<!-- Stage + Rail row -->
<div class="board-body">
<!-- Stage: AlertBar + FlowCanvas + TaskStrip -->
<div class="stage">
<AlertBar
:active-count="agentStore.activeCount"
:think-count="agentStore.thinkCount"
:idle-count="agentStore.idleCount"
:blocker-count="agentStore.blockerCount"
:today-cost="agentStore.todayCost"
:today-tokens="agentStore.todayTokens"
@blocker-click="handleBlockerClick"
/>
<FlowCanvas
:agents="agentStore.agentList"
:positions="agentPositions"
:entering-ids="enteringIds"
@select="handleSelect"
@add="handleAdd"
@reset-layout="handleResetLayout"
@update-positions="handleUpdatePositions"
/>
<TaskStrip :tasks="taskStore.taskList" :loading="taskStore.loading" :error="taskStore.error" />
</div>
<!-- Rail: IrisChat -->
<IrisChat
:messages="chatStore.messageList"
:is-thinking="chatStore.isThinking"
:error="chatStore.error"
@send="handleChatSend"
/>
</div>
<!-- Agent Detail Modal -->
<AgentDetailModal
v-if="agentStore.modalOpen && agentStore.selectedAgent"
:agent="agentStore.selectedAgent"
:agent-order="agentStore.agentOrder"
@close="handleCloseModal"
@select="handleSelect"
@change-model="handleChangeModel"
/>
</div>
</template>
<style scoped>
.flow-board {
height: 100%;
display: flex;
flex-direction: column;
min-height: 0;
animation: fade-in 0.35s ease-out;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.board-body {
flex: 1;
display: flex;
flex-direction: row;
gap: 18px;
padding: 18px 20px;
overflow: hidden;
min-height: 0;
}
.stage {
flex: 1;
display: flex;
flex-direction: column;
gap: 14px;
min-height: 0;
min-width: 0;
overflow: hidden;
}
@media (max-width: 767px) {
.board-body {
flex-direction: column;
padding: 8px;
gap: 10px;
}
.stage {
flex: 1;
}
}
</style>