This commit is contained in:
team3
2026-05-28 18:10:58 +02:00
parent cc1ea166c8
commit c1370a9f6a
2 changed files with 179 additions and 32 deletions

View File

@@ -4,12 +4,14 @@ import { ref, computed } from 'vue'
const props = defineProps({
topics: { type: Array, required: true },
selectedTopic: { type: String, default: null },
guidesByFormat: { type: Object, default: () => ({}) },
doneByFormat: { type: Object, default: () => ({}) },
latestByFormat: { type: Object, default: () => ({}) },
allGuides: { type: Array, default: () => [] },
bausteineActive: { type: Boolean, default: false },
pinned: { type: Boolean, default: true },
})
const emit = defineEmits(['select', 'create', 'formatClick', 'deleteTopic', 'cancelGuide', 'deleteGuide', 'preview', 'rework', 'showBausteine'])
const emit = defineEmits(['select', 'create', 'formatClick', 'deleteTopic', 'cancelGuide', 'deleteGuide', 'preview', 'rework', 'showBausteine', 'togglePin', 'sidebarLeave'])
const formats = [
{ key: 'OnePager', label: 'OnePager' },
@@ -27,14 +29,22 @@ const activeGenerations = computed(() => {
})
function guideStatus(format) {
const guide = props.guidesByFormat[format]
if (!guide) return 'none'
return guide.status
if (props.doneByFormat[format]) return 'done'
const latest = props.latestByFormat[format]
if (!latest) return 'none'
if (latest.status === 'error') return 'none'
return latest.status
}
function errorMsg(format) {
const latest = props.latestByFormat[format]
if (latest?.status === 'error') return latest.error_msg || 'Fehler bei der Generierung'
return ''
}
function handleFormatClick(format) {
const guide = props.guidesByFormat[format]
if (guide?.status === 'done') {
const guide = props.doneByFormat[format]
if (guide) {
emit('preview', guide)
}
}
@@ -60,16 +70,33 @@ function handlePlay(format) {
}
function handleRefresh(format) {
const guide = props.guidesByFormat[format]
const guide = props.doneByFormat[format]
if (!guide) return
const text = activeInput.value === format ? inputText.value.trim() : ''
emit('rework', { guideId: guide.id, instructions: text || 'Überarbeite das Layout' })
if (!text) return
emit('rework', { guideId: guide.id, instructions: text })
activeInput.value = null
inputText.value = ''
}
function handleInputEnter(format) {
const text = inputText.value.trim()
if (props.doneByFormat[format] && text) {
handleRefresh(format)
} else {
handlePlay(format)
}
}
function dismissError(format) {
const latest = props.latestByFormat[format]
if (latest?.status === 'error') {
emit('deleteGuide', latest.id)
}
}
function handleDelete(format) {
const guide = props.guidesByFormat[format]
const guide = props.latestByFormat[format]
if (!guide) return
if (guide.status === 'generating' || guide.status === 'queued') {
if (!confirm('Generierung abbrechen?')) return
@@ -91,8 +118,13 @@ function submit() {
</script>
<template>
<aside class="sidebar">
<aside class="sidebar" @mouseleave="emit('sidebarLeave')">
<div class="new-topic">
<button
class="pin-btn"
:title="pinned ? 'Sidebar ausblenden' : 'Sidebar fixieren'"
@click="emit('togglePin')"
>{{ pinned ? '⇤' : '⇥' }}</button>
<input
v-model="newTopic"
placeholder="Neues Thema…"
@@ -128,16 +160,7 @@ function submit() {
>&times;</span>
</button>
<div class="format-actions">
<template v-if="guideStatus(f.key) === 'done'">
<button class="action-btn refresh" title="Überarbeiten" @click="handleRefresh(f.key)"></button>
<button
class="action-btn pencil"
:class="{ active: activeInput === f.key }"
title="Anweisungen"
@click="toggleInput(f.key)"
></button>
</template>
<template v-else-if="guideStatus(f.key) !== 'generating' && guideStatus(f.key) !== 'queued'">
<template v-if="guideStatus(f.key) !== 'generating' && guideStatus(f.key) !== 'queued'">
<button class="action-btn play" title="Generieren" @click="handlePlay(f.key)"></button>
<button
class="action-btn pencil"
@@ -148,12 +171,23 @@ function submit() {
</template>
</div>
</div>
<div v-if="errorMsg(f.key)" class="format-error">
<span class="format-error-text">{{ errorMsg(f.key) }}</span>
<button class="format-error-x" title="Fehler entfernen" @click="dismissError(f.key)">&times;</button>
</div>
<div v-if="activeInput === f.key" class="format-input">
<input
v-model="inputText"
:placeholder="guideStatus(f.key) === 'done' ? 'Was soll überarbeitet werden?' : 'Anweisungen (optional)…'"
@keyup.enter="guideStatus(f.key) === 'done' ? handleRefresh(f.key) : handlePlay(f.key)"
:placeholder="doneByFormat[f.key] ? 'Was soll überarbeitet werden?' : 'Anweisungen (optional)…'"
@keyup.enter="handleInputEnter(f.key)"
/>
<button
v-if="doneByFormat[f.key]"
class="action-btn refresh"
title="Überarbeiten"
:disabled="!inputText.trim()"
@click="handleRefresh(f.key)"
></button>
</div>
</div>
<div class="bausteine-btn-wrapper">
@@ -214,6 +248,20 @@ function submit() {
cursor: not-allowed;
}
.new-topic .pin-btn {
background: #f8f9fb;
color: #4b5563;
border: 1px solid #d8dde3;
font-weight: 600;
padding: 6px 8px;
}
.new-topic .pin-btn:hover {
background: #ede9fe;
color: #4f46e5;
border-color: #a5b4fc;
}
.topic-list {
list-style: none;
overflow-y: auto;
@@ -332,10 +380,35 @@ function submit() {
animation: pulse 1.5s ease-in-out infinite;
}
.fmt-error .format-name {
.format-error {
display: flex;
align-items: flex-start;
gap: 4px;
padding: 2px 0.75rem 6px calc(0.75rem + 8px);
font-size: 0.72rem;
color: #991b1b;
background: #fee2e2;
border: 1px solid #f87171;
line-height: 1.3;
}
.format-error-text {
flex: 1;
word-break: break-word;
}
.format-error-x {
flex: 0 0 auto;
background: none;
border: none;
color: #991b1b;
font-size: 1rem;
line-height: 1;
cursor: pointer;
padding: 0 2px;
opacity: 0.6;
}
.format-error-x:hover {
opacity: 1;
}
.format-actions {
@@ -387,11 +460,14 @@ function submit() {
}
.format-input {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 0.75rem 8px;
}
.format-input input {
width: 100%;
flex: 1;
padding: 4px 8px;
border: 1px solid #d8dde3;
border-radius: 4px;
@@ -403,6 +479,16 @@ function submit() {
border-color: #6366f1;
}
.action-btn:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.action-btn:disabled:hover {
background: none;
border-color: transparent;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.65; }