This commit is contained in:
Team3
2026-06-06 22:30:14 +02:00
parent 80b964fef4
commit 07b2338e67
5 changed files with 108 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
import { fetchGuides, fetchTopics, createTopic as apiCreateTopic, deleteTopic as apiDeleteTopic, createGuide as apiCreate, deleteGuide, cancelGuide as apiCancel, fetchBausteineStatus, fetchActiveBausteine, createBausteine as apiCreateBausteine, cancelBausteine as apiCancelBausteine, deleteBausteine as apiDeleteBausteine, fetchProjects, deleteProject as apiDeleteProject, fetchProviders } from './api.js'
import { fetchGuides, fetchTopics, createTopic as apiCreateTopic, deleteTopic as apiDeleteTopic, createGuide as apiCreate, deleteGuide, cancelGuide as apiCancel, fetchBausteineStatus, fetchActiveBausteine, createBausteine as apiCreateBausteine, cancelBausteine as apiCancelBausteine, deleteBausteine as apiDeleteBausteine, fetchProjects, deleteProject as apiDeleteProject, fetchProviders, fetchStats } from './api.js'
import TopicSidebar from './components/TopicSidebar.vue'
import TopicDetail from './components/TopicDetail.vue'
@@ -21,6 +21,15 @@ const bausteine = ref({ ...EMPTY_BAUSTEINE })
const activeBausteine = ref([])
const provider = ref(localStorage.getItem('provider') || 'claude')
const providers = ref([])
const stats = ref(null)
async function loadStats() {
try {
stats.value = await fetchStats()
} catch (e) {
console.error('Fehler beim Laden der Statistik:', e)
}
}
function setProvider(id) {
provider.value = id
@@ -112,6 +121,7 @@ async function loadTopics() {
async function loadGuides() {
try {
guides.value = await fetchGuides()
loadStats()
} catch (e) {
console.error('Fehler beim Laden:', e)
}
@@ -288,6 +298,7 @@ onUnmounted(() => {
:topics="topics"
:projects="projectNames"
:selectedTopic="selectedTopic"
:stats="stats"
:doneByFormat="doneByFormat"
:latestByFormat="latestByFormat"
:allGuides="guides"
@@ -318,6 +329,7 @@ onUnmounted(() => {
:previewGuide="previewGuide"
:dark="darkMode"
:provider="provider"
@progressChanged="loadStats"
/>
<div v-else class="empty-main">
<p>Thema in der Sidebar anlegen oder auswählen.</p>

View File

@@ -41,6 +41,11 @@ export async function deleteBausteine(topic) {
await fetch(`${BASE}/bausteine?topic=${encodeURIComponent(topic)}`, { method: 'DELETE' })
}
export async function fetchStats() {
const res = await fetch(`${BASE}/stats`)
return res.json()
}
export async function fetchProviders() {
const res = await fetch(`${BASE}/providers`)
return res.json()

View File

@@ -28,6 +28,8 @@ const props = defineProps({
provider: { type: String, default: 'claude' },
})
const emit = defineEmits(['progressChanged'])
const isOnePager = computed(() => props.previewGuide?.format === 'OnePager')
// --- Inhalt laden ---
@@ -73,6 +75,7 @@ async function toggleChapter(title) {
try {
const res = await setProgress(props.previewGuide.id, title, newState)
doneChapters.value = new Set(res.chapters || [])
emit('progressChanged')
} catch {
const rollback = new Set(doneChapters.value)
if (newState) rollback.delete(title)

View File

@@ -5,6 +5,7 @@ const props = defineProps({
topics: { type: Array, required: true },
projects: { type: Array, default: () => [] },
selectedTopic: { type: String, default: null },
stats: { type: Object, default: null },
doneByFormat: { type: Object, default: () => ({}) },
latestByFormat: { type: Object, default: () => ({}) },
allGuides: { type: Array, default: () => [] },
@@ -25,6 +26,19 @@ function providerAvailable(id) {
const PROVIDER_LABELS = { claude: 'Claude', minimax: 'MiniMax', lokal: 'Lokal' }
// Tracker oben in der Navigation: Themen gesamt, pro Format erstellt/absolviert
const trackerItems = computed(() => {
if (!props.stats) return []
const f = props.stats.formate || {}
const fmt = (k) => `${f[k]?.absolviert ?? 0}/${f[k]?.erstellt ?? 0}`
return [
{ label: 'Themen', value: String(props.stats.themen ?? 0), title: 'Themen inkl. Projekte' },
{ label: 'MiniGuides', value: fmt('MiniGuide'), title: 'absolviert/erstellt' },
{ label: 'Guides', value: fmt('Guide'), title: 'absolviert/erstellt' },
{ label: 'FullGuides', value: fmt('FullGuide'), title: 'absolviert/erstellt' },
]
})
const formats = [
{ key: 'OnePager', label: 'OnePager' },
{ key: 'MiniGuide', label: 'MiniGuide' },
@@ -167,6 +181,12 @@ function confirmDeleteProject(name) {
<template>
<aside class="sidebar" @mouseleave="emit('sidebarLeave')">
<div class="stats-bar" v-if="trackerItems.length">
<div class="stat" v-for="item in trackerItems" :key="item.label" :title="item.title">
<span class="stat-value">{{ item.value }}</span>
<span class="stat-label">{{ item.label }}</span>
</div>
</div>
<div class="new-topic">
<button
class="pin-btn"
@@ -407,6 +427,40 @@ function confirmDeleteProject(name) {
border-color: var(--accent-border);
}
.stats-bar {
display: flex;
padding: 0.5rem 0.75rem;
gap: 4px;
border-bottom: 1px solid var(--border);
}
.stat {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 1px;
padding: 4px 2px;
background: var(--panel-soft);
border: 1px solid var(--border);
border-radius: 6px;
cursor: default;
}
.stat-value {
font-size: 0.8rem;
font-weight: 700;
color: var(--text);
}
.stat-label {
font-size: 0.58rem;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--text-faint);
white-space: nowrap;
}
.provider-toggle {
display: flex;
gap: 0;