eeb6174de0
- ASP.NET Core 10 Backend (JWT Auth, Agent config API) - Vue 3 Frontend (Dashboard, Team, Agents, Config Editor) - PostgreSQL Database - Docker Compose setup - Mission Control Dashboard redesign
211 lines
6.0 KiB
Vue
211 lines
6.0 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref } from 'vue'
|
|
|
|
type FeedStatus = 'running' | 'done' | 'waiting' | 'error' | 'info'
|
|
|
|
interface FeedItem {
|
|
time: string
|
|
status: FeedStatus
|
|
text: string
|
|
label: string
|
|
date: 'today' | 'yesterday' | 'week'
|
|
}
|
|
|
|
const allFeed: FeedItem[] = [
|
|
{ time: '09:17', status: 'running', text: 'OpenClaw analysiert Memory-Datenbank.', label: 'Memory', date: 'today' },
|
|
{ time: '09:19', status: 'done', text: 'Repository Refactoring abgeschlossen.', label: 'Coding', date: 'today' },
|
|
{ time: '09:21', status: 'done', text: '3 neue Erinnerungen gespeichert.', label: 'Memory', date: 'today' },
|
|
{ time: '09:25', status: 'done', text: 'Dungeon-Service erfolgreich kompiliert.', label: 'Coding', date: 'today' },
|
|
{ time: '09:28', status: 'error', text: 'Build fehlgeschlagen — NullReferenceException in EnemyFactory.', label: 'Coding', date: 'today' },
|
|
{ time: '09:31', status: 'waiting', text: 'Iris hat "Steuerunterlagen" auf Freitag verschoben.', label: 'Personal', date: 'today' },
|
|
{ time: '10:02', status: 'running', text: 'Programmer arbeitet an TeamView-Redesign.', label: 'Coding', date: 'today' },
|
|
{ time: '10:15', status: 'done', text: 'AgentDetailView deployed.', label: 'System', date: 'today' },
|
|
{ time: '10:22', status: 'running', text: 'Architekt prüft Compose-Konfiguration.', label: 'System', date: 'today' },
|
|
{ time: '10:45', status: 'done', text: 'Reviewer: Code-Review abgeschlossen, keine Findings.', label: 'Agenten', date: 'today' },
|
|
{ time: '11:00', status: 'running', text: 'Researcher analysiert API-Dokumentation.', label: 'Research', date: 'today' },
|
|
{ time: '11:30', status: 'waiting', text: 'Executor wartet auf Deployment-Freigabe.', label: 'System', date: 'today' },
|
|
{ time: '15:22', status: 'done', text: 'Nexus Dashboard Migration geplant.', label: 'Coding', date: 'yesterday' },
|
|
{ time: '16:05', status: 'done', text: 'Docker Compose Optimierung abgeschlossen.', label: 'System', date: 'yesterday' },
|
|
]
|
|
|
|
const feedFilter = ref<string | null>(null)
|
|
const filterLabels = ['Alle', 'Coding', 'Research', 'Personal', 'Memory', 'Agenten', 'System']
|
|
|
|
const filteredFeed = computed(() => {
|
|
if (!feedFilter.value || feedFilter.value === 'Alle') return allFeed
|
|
return allFeed.filter(item => item.label === feedFilter.value)
|
|
})
|
|
|
|
const feedGroups = computed(() => {
|
|
const groups: { date: string; items: FeedItem[] }[] = []
|
|
const dates = ['today', 'yesterday', 'week'] as const
|
|
for (const d of dates) {
|
|
const items = filteredFeed.value.filter(i => i.date === d)
|
|
if (items.length) {
|
|
groups.push({
|
|
date: d === 'today' ? 'Heute' : d === 'yesterday' ? 'Gestern' : 'Diese Woche',
|
|
items,
|
|
})
|
|
}
|
|
}
|
|
return groups
|
|
})
|
|
|
|
const statusColor = (s: FeedStatus): string => {
|
|
const m: Record<FeedStatus, string> = {
|
|
running: '#3b82f6',
|
|
done: '#22c55e',
|
|
waiting: '#eab308',
|
|
error: '#ef4444',
|
|
info: '#6b7385',
|
|
}
|
|
return m[s]
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="feed-panel">
|
|
<h2 class="feed-title">Operations Feed</h2>
|
|
|
|
<div class="filter-pills">
|
|
<button
|
|
v-for="label in filterLabels"
|
|
:key="label"
|
|
:class="{ active: feedFilter === label || (!feedFilter && label === 'Alle') }"
|
|
@click="feedFilter = label === 'Alle' ? null : label"
|
|
>
|
|
{{ label }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="feed-list">
|
|
<template v-for="group in feedGroups" :key="group.date">
|
|
<div class="feed-date-heading">{{ group.date }}</div>
|
|
<div
|
|
v-for="(item, idx) in group.items"
|
|
:key="idx"
|
|
class="feed-item"
|
|
>
|
|
<span class="feed-time">{{ item.time }}</span>
|
|
<span class="feed-dot" :style="{ background: statusColor(item.status) }"></span>
|
|
<span class="feed-text">{{ item.text }}</span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.feed-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
min-height: 420px;
|
|
padding: 18px;
|
|
background: rgba(22, 27, 34, 0.8);
|
|
border: 1px solid rgba(139, 124, 246, 0.12);
|
|
border-radius: 16px;
|
|
box-shadow: 0 4px 24px rgba(0,0,0,0.3);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-backdrop-filter: blur(12px);
|
|
transition: all 0.2s ease;
|
|
}
|
|
.feed-panel:hover {
|
|
border-color: rgba(139, 124, 246, 0.18);
|
|
}
|
|
.feed-title {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
color: #e8eaf0;
|
|
}
|
|
|
|
/* Filter pills */
|
|
.filter-pills {
|
|
display: flex;
|
|
gap: 4px;
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
padding-bottom: 2px;
|
|
}
|
|
.filter-pills::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
.filter-pills button {
|
|
flex-shrink: 0;
|
|
padding: 4px 10px;
|
|
border: 1px solid rgba(139, 124, 246, 0.08);
|
|
border-radius: 20px;
|
|
background: transparent;
|
|
color: #6b7385;
|
|
font-size: 9px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
white-space: nowrap;
|
|
}
|
|
.filter-pills button:hover {
|
|
border-color: rgba(139, 124, 246, 0.25);
|
|
color: #7e8799;
|
|
}
|
|
.filter-pills button.active {
|
|
background: rgba(139, 124, 246, 0.12);
|
|
border-color: rgba(139, 124, 246, 0.25);
|
|
color: #a78bfa;
|
|
}
|
|
|
|
/* Feed list */
|
|
.feed-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
}
|
|
.feed-date-heading {
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
color: #6b7385;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
padding: 8px 0 4px;
|
|
}
|
|
.feed-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 5px 6px;
|
|
border-radius: 6px;
|
|
transition: background 0.15s;
|
|
}
|
|
.feed-item:hover {
|
|
background: rgba(139, 124, 246, 0.04);
|
|
}
|
|
.feed-time {
|
|
font-size: 9px;
|
|
color: #6b7385;
|
|
flex-shrink: 0;
|
|
width: 36px;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.feed-dot {
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
box-shadow: 0 0 4px currentColor;
|
|
}
|
|
.feed-text {
|
|
font-size: 10.5px;
|
|
line-height: 1.3;
|
|
color: #7e8799;
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.feed-panel {
|
|
order: 2;
|
|
}
|
|
}
|
|
</style>
|