update
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { fetchProjekte } from './api.js'
|
||||
import SeitenLeiste from './components/SeitenLeiste.vue'
|
||||
import FeatureListe from './components/FeatureListe.vue'
|
||||
import KontextMenue from './components/KontextMenue.vue'
|
||||
import PruefPanel from './components/PruefPanel.vue'
|
||||
import ChatPanel from './components/ChatPanel.vue'
|
||||
import {
|
||||
laeuft as planungLaeuft,
|
||||
sicht as planungSicht,
|
||||
anzahl as planungAnzahl,
|
||||
meldung as planungMeldung,
|
||||
lade as ladePlanung,
|
||||
starte as startePlanung,
|
||||
umsetzen as setzePlanungUm,
|
||||
brichAb as brichPlanungAb,
|
||||
} from './composables/planung.js'
|
||||
import {
|
||||
offen as pruefungOffen,
|
||||
laeuft as pruefungLaeuft,
|
||||
@@ -17,6 +28,27 @@ const projekte = ref([])
|
||||
const aktivesProjekt = ref(null)
|
||||
const meldung = ref('')
|
||||
|
||||
// Gespräch und Prüfung teilen sich die rechte Spalte — beide gleichzeitig
|
||||
// wären zwei Panels für dieselbe Fläche.
|
||||
const chatOffen = ref(false)
|
||||
|
||||
// In der Übersicht schaut man nur nach. Wer etwas ändern will, wechselt in die Planung.
|
||||
const inPlanung = computed(() => planungLaeuft.value && planungSicht.value === 'planung')
|
||||
const nurAnsehen = computed(() => planungLaeuft.value && !inPlanung.value)
|
||||
|
||||
// Umlegen heißt: die andere Seite zeigen. Gibt es noch keine Planung, entsteht sie hier.
|
||||
function schalte() {
|
||||
if (!planungLaeuft.value) return startePlanung()
|
||||
planungSicht.value = inPlanung.value ? 'uebersicht' : 'planung'
|
||||
}
|
||||
|
||||
const stand = ref(0)
|
||||
|
||||
function oeffneChat() {
|
||||
if (pruefungOffen.value) schliessePruefung()
|
||||
chatOffen.value = !chatOffen.value
|
||||
}
|
||||
|
||||
// Drei Modi im Kreis; „auto" folgt der Systemeinstellung
|
||||
const MODI = ['auto', 'hell', 'dunkel']
|
||||
const ICONS = { auto: 'A', hell: '☀', dunkel: '☾' }
|
||||
@@ -47,6 +79,26 @@ function umbenannt(alt, neu) {
|
||||
|
||||
// Prüfung gehört zum Projekt — beim Wechsel wird sie verworfen
|
||||
watch(aktivesProjekt, () => { if (pruefungOffen.value) schliessePruefung() })
|
||||
watch(aktivesProjekt, n => ladePlanung(n))
|
||||
|
||||
// Planen heißt, die Karten mit der KI zu entwickeln. Das Gespräch ist dabei kein
|
||||
// Zusatz, sondern das Werkzeug — wer die Planung sieht, sieht es auch. Am Reiter
|
||||
// und nicht am Startknopf, damit es auch nach einem Neustart wieder dasteht.
|
||||
watch(planungSicht, s => {
|
||||
if (s !== 'planung') {
|
||||
chatOffen.value = false // in der Übersicht gibt es nichts zu besprechen
|
||||
return
|
||||
}
|
||||
if (pruefungOffen.value) schliessePruefung()
|
||||
chatOffen.value = true
|
||||
})
|
||||
|
||||
// Umsetzen und Abbrechen ändern die Karten — die Liste muss neu lesen.
|
||||
async function planungBeenden(wie) {
|
||||
const ok = wie === 'umsetzen' ? await setzePlanungUm() : await brichPlanungAb()
|
||||
if (ok) stand.value++
|
||||
}
|
||||
watch(pruefungOffen, o => { if (o) chatOffen.value = false })
|
||||
|
||||
// Zuletzt aktives Projekt überlebt das Neuladen
|
||||
watch(aktivesProjekt, name => {
|
||||
@@ -74,38 +126,75 @@ onMounted(async () => {
|
||||
if (gemerkt && projekte.value.some(p => p.name === gemerkt)) {
|
||||
aktivesProjekt.value = gemerkt
|
||||
}
|
||||
// Eine Planung liegt auf der Platte. Nach einem Neustart steht sie wieder da.
|
||||
await ladePlanung(aktivesProjekt.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="raster" :class="{ pruefung: pruefungOffen }">
|
||||
<div class="raster" :class="{ pruefung: pruefungOffen || chatOffen }">
|
||||
<SeitenLeiste
|
||||
:projekte="projekte"
|
||||
:aktiv="aktivesProjekt"
|
||||
@auswaehlen="aktivesProjekt = $event"
|
||||
@geaendert="ladeProjekte"
|
||||
@umbenannt="umbenannt"
|
||||
>
|
||||
<span v-if="aktivesProjekt" class="pruefen">
|
||||
<span class="knoepfe">
|
||||
<button class="icon-knopf" title="Stil prüfen" @click="startePruefung(aktivesProjekt, 'stil')">⌾</button>
|
||||
<button class="icon-knopf" title="Features im Code: was steht ohne Code da?" @click="startePruefung(aktivesProjekt, 'feature')">↧</button>
|
||||
<button class="icon-knopf" title="Code in Features: was fehlt auf den Karten?" @click="startePruefung(aktivesProjekt, 'code')">↥</button>
|
||||
</span>
|
||||
<span v-if="pruefungLaeuft" class="fortschritt">{{ pruefungProzent }} %</span>
|
||||
</span>
|
||||
<button class="icon-knopf" :title="TITEL[modus]" @click="wechsleDarkMode">
|
||||
{{ ICONS[modus] }}
|
||||
</button>
|
||||
</SeitenLeiste>
|
||||
/>
|
||||
|
||||
<main>
|
||||
<p v-if="meldung" class="meldung">{{ meldung }}</p>
|
||||
<FeatureListe v-if="aktivesProjekt" :projekt="aktivesProjekt" />
|
||||
<p v-if="planungMeldung" class="meldung">{{ planungMeldung }}</p>
|
||||
<FeatureListe
|
||||
v-if="aktivesProjekt"
|
||||
:projekt="aktivesProjekt"
|
||||
:projekte="projekte"
|
||||
:stand="stand"
|
||||
>
|
||||
<template #werkzeuge>
|
||||
<!-- Ein Schalter, zwei Stellungen. Der Text nennt, was gerade zu sehen ist.
|
||||
Er steht immer da: „wo bin ich" ist auch dann eine Frage, wenn noch
|
||||
nichts geplant wird. Wer dann umlegt, beginnt eine Planung. -->
|
||||
<button
|
||||
class="schalter" :class="{ planung: inPlanung }"
|
||||
:title="inPlanung ? 'Wechseln zu Übersicht'
|
||||
: (planungLaeuft ? 'Wechseln zu Planung' : 'Planung beginnen')"
|
||||
@click="schalte"
|
||||
>
|
||||
⇄ {{ inPlanung ? 'Planung' : 'Übersicht' }}
|
||||
<span v-if="inPlanung && planungAnzahl" class="zahl">{{ planungAnzahl }}</span>
|
||||
</button>
|
||||
|
||||
<!-- Jedes Werkzeug hier ändert Karten. Die Übersicht sagt nur, was das Projekt
|
||||
heute kann — dort wird nichts entschieden und nichts angefasst. -->
|
||||
<template v-if="!nurAnsehen">
|
||||
<button
|
||||
class="icon-knopf"
|
||||
:class="{ an: chatOffen }"
|
||||
title="Karten im Gespräch entwickeln"
|
||||
@click="oeffneChat"
|
||||
>✎</button>
|
||||
<button class="icon-knopf" title="Stil prüfen" @click="startePruefung(aktivesProjekt, 'stil')">⌾</button>
|
||||
<template v-if="planungLaeuft">
|
||||
<button class="icon-knopf" title="Planung umsetzen" @click="planungBeenden('umsetzen')">▶</button>
|
||||
<button class="icon-knopf rot" title="Planung verwerfen" @click="planungBeenden('abbrechen')">✕</button>
|
||||
</template>
|
||||
<span v-if="pruefungLaeuft" class="fortschritt">{{ pruefungProzent }} %</span>
|
||||
</template>
|
||||
|
||||
<button class="icon-knopf" :title="TITEL[modus]" @click="wechsleDarkMode">
|
||||
{{ ICONS[modus] }}
|
||||
</button>
|
||||
</template>
|
||||
</FeatureListe>
|
||||
<p v-else-if="!meldung" class="leer">Projekt wählen oder anlegen.</p>
|
||||
</main>
|
||||
|
||||
<PruefPanel v-if="pruefungOffen" />
|
||||
<ChatPanel
|
||||
v-else-if="chatOffen && aktivesProjekt"
|
||||
:projekt="aktivesProjekt"
|
||||
@schliessen="chatOffen = false"
|
||||
/>
|
||||
<KontextMenue />
|
||||
</div>
|
||||
</template>
|
||||
@@ -127,6 +216,25 @@ main {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* Mittlere Fenster: drei Spalten passen nicht mehr. Bei 760px blieben für die
|
||||
Karten 200px übrig, und die Kartenköpfe liefen über. Das Panel legt sich dann
|
||||
unter den Arbeitsbereich, die Projektliste bleibt daneben. */
|
||||
@media (min-width: 46.01rem) and (max-width: 64rem) {
|
||||
.raster.pruefung {
|
||||
grid-template-columns: 15rem 1fr;
|
||||
grid-template-rows: 1fr 45vh;
|
||||
/* Benannte Bereiche statt Auto-Platzierung: mit `grid-row` allein landete
|
||||
der Arbeitsbereich unter der Leiste statt daneben. */
|
||||
grid-template-areas:
|
||||
"leiste inhalt"
|
||||
"leiste panel";
|
||||
}
|
||||
.raster.pruefung > aside { grid-area: leiste; }
|
||||
.raster.pruefung > main { grid-area: inhalt; }
|
||||
.raster.pruefung > .panel,
|
||||
.raster.pruefung > .chat { grid-area: panel; }
|
||||
}
|
||||
|
||||
/* Schmale Fenster: die Projektliste legt sich als Leiste nach oben */
|
||||
@media (max-width: 46rem) {
|
||||
.raster, .raster.pruefung {
|
||||
@@ -141,16 +249,31 @@ main {
|
||||
margin-top: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
/* Fortschritt sitzt unter den Prüf-Icons */
|
||||
.pruefen {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
line-height: 1.1;
|
||||
}
|
||||
.pruefen .knoepfe { display: flex; }
|
||||
.fortschritt {
|
||||
font-size: 0.65rem;
|
||||
font-size: 0.7rem;
|
||||
color: var(--dezent);
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
.icon-knopf.an { color: var(--akzent); background: var(--code-bg); }
|
||||
|
||||
/* Ein Schalter statt zweier Reiter. Beschriftet mit dem, was zu sehen ist —
|
||||
das Pfeilpaar sagt, dass ein Klick die andere Seite zeigt. */
|
||||
.schalter {
|
||||
border: 1px solid var(--rand);
|
||||
background: none;
|
||||
color: var(--dezent);
|
||||
border-radius: 0.4rem;
|
||||
padding: 0.2rem 0.55rem;
|
||||
margin-right: 0.3rem;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 0.78rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.schalter.planung {
|
||||
color: var(--akzent-text);
|
||||
background: var(--akzent);
|
||||
border-color: var(--akzent);
|
||||
}
|
||||
.schalter .zahl { margin-left: 0.3rem; font-weight: 700; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user