5244e9fd3d
- TeamNetwork ins Dashboard verschoben (center column) - 3-Spalten-Layout auf volle Desktop-Breite (kein max-width) - Agent-Grid dynamisch: 2 Spalten, erweitert nach unten (4→6→8 Agenten) - SVG-Bézier-Linien mit ResizeObserver passen sich an - 'Team' aus Navigation, Router und standaloneViews entfernt - /team Route gelöscht
169 lines
4.4 KiB
Vue
169 lines
4.4 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import { Activity } from '@lucide/vue'
|
|
import { RouterView, useRoute, useRouter } from 'vue-router'
|
|
import { useOperationsStore } from './stores/operations'
|
|
import { useAuthStore } from './stores/auth'
|
|
import AppSidebar from './components/layout/AppSidebar.vue'
|
|
import AppHeader from './components/layout/AppHeader.vue'
|
|
import ModuleView from './components/ModuleView.vue'
|
|
|
|
const store = useOperationsStore()
|
|
const auth = useAuthStore()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const activeView = computed(() => {
|
|
if (route.name === 'Settings') return 'Settings'
|
|
if (route.name === 'ProjectDetail') return 'ProjectDetail'
|
|
return String(route.name ?? 'Dashboard')
|
|
})
|
|
|
|
const routePaths: Record<string, string> = {
|
|
Dashboard: '/dashboard', Memory: '/memory', Docs: '/docs', Security: '/security',
|
|
Projects: '/projects', 'Task Board': '/tasks', Incidents: '/incidents', Calendar: '/calendar',
|
|
Agents: '/agents', Models: '/models', Activity: '/activity', 'Mobile Chat': '/chat', Settings: '/settings',
|
|
}
|
|
|
|
const navigate = (label: string) => {
|
|
mobileNavOpen.value = false
|
|
return router.push(routePaths[label] ?? '/dashboard')
|
|
}
|
|
const mobileNavOpen = ref(false)
|
|
|
|
const standaloneViews = computed(() => ['Dashboard', 'Settings', 'ProjectDetail', 'Memory', 'Docs', 'Security', 'Incidents', 'Calendar', 'AgentDetail', 'Agents'].includes(activeView.value))
|
|
|
|
onMounted(() => {
|
|
if (auth.isAuthenticated) store.refresh()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<RouterView v-if="route.name === 'Login'" />
|
|
<div v-else class="shell">
|
|
<AppSidebar
|
|
:active-view="activeView"
|
|
:mobile-nav-open="mobileNavOpen"
|
|
:queued-tasks="store.snapshot.metrics.queuedTasks"
|
|
:incidents="store.snapshot.metrics.incidents"
|
|
@navigate="navigate"
|
|
/>
|
|
|
|
<main>
|
|
<AppHeader
|
|
:connected="store.connected"
|
|
@toggle-mobile-nav="mobileNavOpen = !mobileNavOpen"
|
|
/>
|
|
|
|
<section class="content">
|
|
<RouterView v-if="standaloneViews" />
|
|
|
|
<template v-else>
|
|
<div class="page-heading">
|
|
<div>
|
|
<span class="eyebrow">MISSION CONTROL</span>
|
|
<h1>{{ activeView }}</h1>
|
|
<p>System overview and operational intelligence across Noveria.</p>
|
|
</div>
|
|
<button class="refresh" @click="store.refresh()">
|
|
<Activity :size="15" :class="{ spin: store.loading }" />
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
|
|
<ModuleView
|
|
:view="activeView"
|
|
:snapshot="store.snapshot"
|
|
:routing="store.routing"
|
|
@create-project="store.createProject"
|
|
@create-task="store.createTask"
|
|
@update-task-state="store.updateTaskState"
|
|
/>
|
|
</template>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
:root {
|
|
--bg: #0b0d13;
|
|
--panel: #11141b;
|
|
--line: #1f2330;
|
|
--accent: #7b6ef2;
|
|
--accent-soft: rgba(123,110,242,.08);
|
|
--text: #e8eaf0;
|
|
--text-dim: #6f7889;
|
|
--green: #27ae60;
|
|
--red: #e74c3c;
|
|
--yellow: #f1c40f;
|
|
--orange: #e67e22;
|
|
}
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
html { font-size: 15px; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
.shell {
|
|
display: flex;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
main {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.page-heading {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
margin-bottom: 20px;
|
|
gap: 12px;
|
|
}
|
|
.page-heading h1 { margin: 0; font-size: 18px; }
|
|
.page-heading p { margin: 4px 0 0; font-size: 10px; color: var(--text-dim); }
|
|
.eyebrow {
|
|
font-size: 8.5px;
|
|
font-weight: 700;
|
|
letter-spacing: .12em;
|
|
color: var(--accent);
|
|
text-transform: uppercase;
|
|
}
|
|
.refresh {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
flex-shrink: 0;
|
|
padding: 6px 11px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
color: var(--text-dim);
|
|
font-size: 9px;
|
|
cursor: pointer;
|
|
transition: background .15s;
|
|
}
|
|
.refresh:hover { background: var(--accent-soft); color: #d8dbe3; }
|
|
|
|
.spin { animation: spin 1s linear infinite; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
@media (max-width: 860px) {
|
|
.kanban { grid-template-columns: 1fr; }
|
|
}
|
|
</style>
|