This commit is contained in:
team3
2026-06-18 14:30:21 +02:00
parent d8932c90d6
commit 9c0f622e0c
30 changed files with 45808 additions and 217 deletions

View File

@@ -1,14 +1,14 @@
<script setup>
import { ref, computed, watch, onMounted, 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, fetchStats, fetchTopicFortschritt, fetchGuideLocks } 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, fetchProviders, fetchStats, fetchTopicFortschritt, fetchGuideLocks, fetchFolders, updateQuelle as apiUpdateQuelle } from './api.js'
import { usePolling } from './composables/usePolling.js'
import TopicSidebar from './components/TopicSidebar.vue'
import TopicDetail from './components/TopicDetail.vue'
import BausteineUebersicht from './components/BausteineUebersicht.vue'
import ElementsSidebar from './components/elements/ElementsSidebar.vue'
import ElementsOverview from './components/ElementsOverview.vue'
const guides = ref([])
const projects = ref([])
const backendTopics = ref([])
const selectedTopic = ref(null)
const previewGuide = ref(null)
@@ -24,6 +24,8 @@ const bausteine = ref({ ...EMPTY_BAUSTEINE })
const activeBausteine = ref([])
const provider = ref(localStorage.getItem('provider') || 'claude')
const providers = ref([])
const folders = ref({ projekt: [], uni: [] }) // Ordner für die Quellen-Auswahl
const bausteineView = ref(false) // Bausteine-Übersicht im Hauptbereich
const stats = ref(null)
const fortschritt = ref({})
const locks = ref({}) // Sperr-Gründe pro Format (Backend = einzige Regel-Quelle)
@@ -47,6 +49,15 @@ function setProvider(id) {
localStorage.setItem('provider', id)
}
async function loadFolders() {
try {
const [projekt, uni] = await Promise.all([fetchFolders('projekt'), fetchFolders('uni')])
folders.value = { projekt, uni }
} catch (e) {
console.error('Fehler beim Laden der Ordner:', e)
}
}
async function loadProviders() {
try {
providers.value = await fetchProviders()
@@ -86,12 +97,7 @@ function onSidebarLeave() {
if (!sidebarPinned.value) sidebarSticky.value = false
}
const projectNames = computed(() => projects.value.map((p) => p.name))
const topics = computed(() => {
const isProject = new Set(projectNames.value)
return backendTopics.value.filter((t) => !isProject.has(t))
})
const topics = computed(() => backendTopics.value)
const doneByFormat = computed(() => {
const map = {}
@@ -174,15 +180,7 @@ async function loadBausteine() {
}
}
async function loadProjects() {
try {
projects.value = await fetchProjects()
} catch (e) {
console.error('Fehler beim Laden der Projekte:', e)
}
}
const FORMAT_ORDER = ['OnePager', 'MiniGuide', 'Guide', 'FullGuide']
const FORMAT_ORDER = ['OnePager', 'MiniGuide', 'Guide', 'ProGuide', 'FullGuide']
function autoPreview() {
const map = doneByFormat.value
@@ -201,6 +199,7 @@ function selectTopic(topic) {
sidebarSticky.value = false
elementsOpen.value = false
elementsView.value = false
bausteineView.value = false
elementOpenId.value = null
localStorage.setItem('lastTopic', topic)
loadBausteine()
@@ -258,6 +257,24 @@ async function handleCreateThema({ topic, instructions, sourceType, sourceOrt })
startPolling()
}
async function handleUpdateQuelle({ topic, type, ort, spec }) {
uiError.value = null
try {
await apiUpdateQuelle(topic, { type, ort, spec })
} catch (e) {
uiError.value = e.message
return
}
await loadBausteine() // Schritt-Anzeige folgt ggf. der neuen Quelle (z.B. Link → „Quelle laden")
}
function handleOpenBausteineView() {
if (!selectedTopic.value) return
bausteineView.value = true
elementsView.value = false
previewGuide.value = null
}
async function handleFormatClick({ format, instructions }) {
if (!selectedTopic.value) return
// Kein Duplikat-Start: läuft für Thema+Format schon eine Generierung, ignorieren
@@ -277,23 +294,16 @@ async function handleFormatClick({ format, instructions }) {
startPolling()
}
async function handleDeleteProject(name) {
await apiDeleteProject(name)
if (selectedTopic.value === name) {
selectedTopic.value = null
previewGuide.value = null
}
await loadProjects()
}
function handlePreview(guide) {
previewGuide.value = guide
elementsView.value = false
bausteineView.value = false
}
function handleOpenElements() {
if (!selectedTopic.value) return
elementsView.value = true
bausteineView.value = false
// Rechte Sidebar bleibt zu — sie öffnet erst beim Klick auf ein Element.
}
@@ -338,10 +348,10 @@ async function handleDeleteTopic(topic) {
}
onMounted(async () => {
await Promise.all([loadGuides(), loadTopics(), loadProjects(), loadProviders()])
await Promise.all([loadGuides(), loadTopics(), loadProviders(), loadFolders()])
const savedTopic = localStorage.getItem('lastTopic')
const savedFormat = localStorage.getItem('lastFormat')
if (savedTopic && [...topics.value, ...projectNames.value].includes(savedTopic)) {
if (savedTopic && topics.value.includes(savedTopic)) {
selectTopic(savedTopic)
await nextTick()
const g = doneByFormat.value[savedFormat]
@@ -358,7 +368,6 @@ onMounted(async () => {
<div v-if="!sidebarPinned && sidebarSticky" class="sidebar-backdrop" @click="sidebarSticky = false"></div>
<TopicSidebar
:topics="topics"
:projects="projectNames"
:selectedTopic="selectedTopic"
:stats="stats"
:fortschritt="fortschritt"
@@ -374,17 +383,19 @@ onMounted(async () => {
:dark="darkMode"
:provider="provider"
:providers="providers"
:folders="folders"
@setProvider="setProvider"
@toggleDark="toggleDark"
@select="selectTopic"
@create="createTopic"
@createThema="handleCreateThema"
@updateQuelle="handleUpdateQuelle"
@openBausteineView="handleOpenBausteineView"
@formatClick="handleFormatClick"
@bausteineClick="handleBausteineClick"
@cancelBausteine="handleCancelBausteine"
@resetBausteine="handleResetBausteine"
@deleteTopic="handleDeleteTopic"
@deleteProject="handleDeleteProject"
@cancelGuide="handleCancel"
@deleteGuide="handleDeleteGuide"
@dismissError="handleDismissError"
@@ -394,8 +405,13 @@ onMounted(async () => {
@togglePin="toggleSidebarPin"
@sidebarLeave="onSidebarLeave"
/>
<BausteineUebersicht
v-if="selectedTopic && bausteineView"
:topic="selectedTopic"
@close="bausteineView = false"
/>
<ElementsOverview
v-if="selectedTopic && elementsView"
v-else-if="selectedTopic && elementsView"
:topic="selectedTopic"
:version="elementsVersion"
@open="handleOpenElementDetail"