Initial commit: Nexus Mission Control Platform

- 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
This commit is contained in:
Bao
2026-06-09 16:31:42 +02:00
commit eeb6174de0
248 changed files with 19706 additions and 0 deletions
@@ -0,0 +1,210 @@
<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>