Beteiligte in die Projekteinstellungen, TODOs aus Protokoll, Bewegung
Beteiligten-Zuordnung verlagert: p.assign ist jetzt ein Stammdatum und nur noch in den Projekteinstellungen editierbar (planCycleAssign()). Wochenbericht und Projektboard zeigen sie nur noch an, mit Verweis dorthin (springZuEinstellungen()). cycleAssign() ist damit entfallen. Neu "TODOs aus Protokoll": ein externes Besprechungsprotokoll als PDF wird per KI eingelesen und daraus in einem zweiten KI-Schritt Aufgaben abgeleitet. Eigener Bereich in der Aufgaben-/GANTT-Leiste, bewusst unabhaengig von den gespeicherten Protokollen - nichts davon wird abgelegt. Drei Schritte in einem Dialog mit fester Fusszeile; die Vorschlagsliste als kompakte, aufklappbare Zeilen, weil ein echter Import leicht 20 Vorschlaege liefert. Sichtbarkeit je Aufgabe im GANTT: neue Spalte plan_todos.show_in_gantt (Vorgabe 1, idempotente Migration) und eine Checkbox im Aufgaben-Dialog. Eine terminierte Aufgabe kann damit gezielt aus dem Terminplan herausgehalten werden, bleibt sonst aber eine vollwertige Aufgabe. plan-build-gantt.js und plan-drag.js pruefen dieselbe Bedingung, damit Zeilenzahl und Umsortierung nicht auseinanderlaufen. KI-Timeout dritte Stufe: ein abweichender, aber entfernter Endpunkt (Drittanbieter-Proxy) bekommt 2 Minuten statt der 45 Sekunden der echten Anbieter-API. Gemessen: die Punkte-Erkennung eines realen Protokolls brauchte dort 39 Sekunden, lag also stets knapp unter dem alten Limit und brach bei jeder Schwankung ab. Bewegung: Hover- und Klick-Rueckmeldung als Token-Satz in :root, angewandt auf die anklickbaren Zeilen, Schalter und Dialoge. Die Akzentleiste ist ein inset-Schatten statt eines positionierten Elements, damit Drag-and-Drop unberuehrt bleibt; prefers-reduced-motion war bisher gar nicht vorhanden und kippt jetzt einfach die Tokens. Plan Checker: optionaler Projektabgleich des Plankopfs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6ba93b2a92
commit
93505e0e48
17 files changed
+1883
-334
No files matched your search
@@ -172,14 +172,20 @@ user-facing text.
|
||||
│ │ ├── plan-state.js # PlanApp-Zustand, Datenladepath, savePlan, Toasts
|
||||
│ │ ├── plan-sections.js # Bereichs-Navigation (showPlanSection, URL-Fragment)
|
||||
│ │ │ # + Dashboard-Karten (renderPlanDashboard)
|
||||
│ │ ├── plan-info.js # Status/Kommentar, Gruppe, amtierende LPH, LPH-%,
|
||||
│ │ │ # Beteiligten-Matrix (verteilt auf Dashboard+Einstellungen)
|
||||
│ │ ├── plan-info.js # Status/Kommentar, Gruppe, amtierende LPH, LPH-%
|
||||
│ │ │ # (Dashboard-Bereich; die Beteiligten-Matrix ist
|
||||
│ │ │ # entfallen, siehe plan-settings.js)
|
||||
│ │ ├── plan-settings.js # Bereich "Einstellungen": Projektangaben, Honorar,
|
||||
│ │ │ # beauftragte Leistungsphasen (renderPlanSettings)
|
||||
│ │ │ # beauftragte Leistungsphasen, Beteiligten-Matrix
|
||||
│ │ │ # (renderPlanSettings, planCycleAssign)
|
||||
│ │ ├── plan-tasks.js # Bereich "Aufgaben": Aufgaben-/Meilensteinliste
|
||||
│ │ │ # (renderPlanTasks; öffnet dieselben Dialoge wie das GANTT)
|
||||
│ │ ├── plan-history.js # Kommentar-Verlauf über alle Sitzungen (+ Vorwochen-Helfer)
|
||||
│ │ ├── plan-templates.js # Aufgaben aus LPH-Vorlagen (lph_templates)
|
||||
│ │ ├── plan-protokoll-todos.js # "+ TODOs aus Protokoll": externes PDF
|
||||
│ │ │ # per KI einlesen + Aufgaben daraus ableiten —
|
||||
│ │ │ # unabhängig von plan-protokoll.js, nichts
|
||||
│ │ │ # davon wird gespeichert
|
||||
│ │ ├── plan-honorar.js # Terminplan aus Honorar + beauftragten LPH errechnen
|
||||
│ │ ├── plan-build-gantt.js # buildGanttHtml-Erweiterung (TODO- und Meilensteinzeilen)
|
||||
│ │ ├── plan-gantt.js # renderPlanGantt, Abhängigkeitslinien, Zoom, Ressourcen-Load
|
||||
@@ -809,8 +815,8 @@ on the JSON endpoints.
|
||||
The `js/plan/*` modules of the Projektbereich sit right after `gantt.js`
|
||||
(`plan-state → plan-sections → plan-info → plan-history →
|
||||
plan-build-gantt → plan-gantt → plan-drag → plan-modals → plan-tasks →
|
||||
plan-files → plan-templates → plan-honorar → plan-settings →
|
||||
plan-schedule`): they must come after `gantt.js`, whose `buildGanttHtml()`
|
||||
plan-files → plan-templates → plan-protokoll-todos → plan-honorar →
|
||||
plan-settings → plan-schedule`): they must come after `gantt.js`, whose `buildGanttHtml()`
|
||||
`plan-build-gantt.js` wraps, and `plan-history` must stay before
|
||||
`plan-build-gantt`, which uses its previous-week helpers. None of them
|
||||
collides with a main-module global — every one runs in an IIFE over
|
||||
@@ -1006,34 +1012,60 @@ authoritative, and two people could change the same field in different places.
|
||||
The split follows what kind of data it is:
|
||||
|
||||
- **Laufende Erfassung → Wochenbericht only.** The session comment
|
||||
(`p.status`), the progress per Leistungsphase (`p.lph`) and the assignment of
|
||||
people (`p.assign`) are produced in the weekly meeting. They are editable
|
||||
**only** in the Wochenbericht.
|
||||
(`p.status`) and the progress per Leistungsphase (`p.lph`) are produced in
|
||||
the weekly meeting. They are editable **only** in the Wochenbericht.
|
||||
- **Stammdaten → Projektbereich only.** Projektnummer, Bezeichnung,
|
||||
Schlagwörter, Gruppe, amtierende Leistungsphase, Auftragsstatus, Honorar, the
|
||||
beauftragte Leistungsphasen and the Aufwands-Gewichtung live in the
|
||||
Projektbereich (`einstellungen` section, `js/plan/plan-settings.js` plus
|
||||
`js/plan/plan-info.js` for Gruppe/amtierende LPH).
|
||||
beauftragte Leistungsphasen, the Aufwands-Gewichtung, and **the assignment of
|
||||
people (`p.assign`)** live in the Projektbereich (`einstellungen` section,
|
||||
`js/plan/plan-settings.js` plus `js/plan/plan-info.js` for Gruppe/amtierende
|
||||
LPH). `p.assign` moved here on Erik's explicit request ("In den
|
||||
Einstellungen der Projekte will ich auch die Projektbeteiligten einstellen
|
||||
können"), fully relocated rather than duplicated — he chose that option
|
||||
outright over making it editable in both places, to keep this same "Ein Ort
|
||||
je Angabe" invariant for the new field too. It is edited via
|
||||
`planCycleAssign(personId, role)` in `plan-settings.js`, filling
|
||||
`#planAssignMatrix` — deliberately **not** reusing `cycleAssign()`
|
||||
(removed, see below) or going through `currentWeek()`/`save()` at all: the
|
||||
Projektbereich can be deep-linked to an older week, or be a Planung-Sandbox
|
||||
plan with no `currentWeek()` entry at all (same trap `planOpenAufwand()`
|
||||
already guards against), so `planCycleAssign()` mutates `app.project`
|
||||
directly and calls `app.savePlan()`, exactly like every other Stammdatum in
|
||||
this file.
|
||||
- **Everywhere else: display plus a way there.** `renderBoardDrawer()` takes
|
||||
`opts.nurAnsicht` (set by `BOARD_OPTS` in `js/board/board.js`): same card,
|
||||
same values, same order — but no input fields, no clickable avatars, and a
|
||||
`.drawer-verweis` underneath with the button "Im Wochenbericht bearbeiten →".
|
||||
The Projektbereich does the same via `setzeBerichtVerweis()`
|
||||
(`js/plan/plan-info.js`), filling `#planStatusVerweis`, `#planLphVerweis`
|
||||
and `#planAssignVerweis`.
|
||||
same values, same order — but no input fields and a `.drawer-verweis`
|
||||
underneath with the button "Im Wochenbericht bearbeiten →" for Kommentar/
|
||||
Fortschritt. The Beteiligten-Zuordnung (`roleRows`) is now **always**
|
||||
rendered read-only inside `renderBoardDrawer()` — in the Projektboard AND
|
||||
in the Wochenbericht, regardless of `opts.nurAnsicht` — with its own
|
||||
"In den Projekteinstellungen bearbeiten →" button next to it
|
||||
(`springZuEinstellungen(pid)`, `js/gantt.js`). The Projektbereich's
|
||||
Dashboard section does the equivalent for Kommentar/Fortschritt via
|
||||
`setzeBerichtVerweis()` (`js/plan/plan-info.js`), filling
|
||||
`#planStatusVerweis`/`#planLphVerweis`; it has no such verweis for
|
||||
Beteiligte any more, since that matrix is edited right there in
|
||||
Einstellungen now, not somewhere else.
|
||||
- **The link is a real jump**, not a hint: `springZumWochenbericht(uid)`
|
||||
(`js/plan/plan-info.js`, also used by `renderBerichtVerweis()` in
|
||||
`board.js`) sets `berichtAuswahl` **before** `showTab('wochenbericht')` and
|
||||
therefore lands on that very project. Without it the user would have to find
|
||||
it again in a list of thirty. (`berichtAuswahl` is a global `let` binding
|
||||
from `js/wochenbericht/wochenbericht.js` — classic scripts, no modules, so
|
||||
assigning it from another file works.)
|
||||
assigning it from another file works.) `springZuEinstellungen(pid)` is the
|
||||
same idea in the other direction: it sets `location.hash = 'einstellungen'`
|
||||
**before** calling `openProjectPlan(pid)`, the same "set the hash first"
|
||||
trick `wandSpringeZuProjekt()` already used, since `showTab('plan')`'s own
|
||||
`updateRouteUrl()` preserves an already-set hash.
|
||||
- **Consequence:** `planStatusBlur()`, `setPlanLph()` and `setPlanAssign()` in
|
||||
`js/plan/plan-info.js` are **gone** — they had no caller left. The status
|
||||
editor's formatting toolbar (`formatStatus()` buttons) went with them in the
|
||||
Projektbereich; the Wochenbericht keeps the full editor. `editLph()`,
|
||||
`cycleAssign()` and `saveBoardComment()` in `js/board/board.js` are
|
||||
untouched — the Wochenbericht's editing hangs off them.
|
||||
Projektbereich; the Wochenbericht keeps the full editor. `editLph()` and
|
||||
`saveBoardComment()` in `js/board/board.js` are untouched — the
|
||||
Wochenbericht's editing hangs off them. `cycleAssign()` in `js/board/board.js`
|
||||
is now **also gone** (the Beteiligten relocation's own consequence,
|
||||
mirroring the three functions above) — it had exactly one caller
|
||||
(`roleRows`'s avatar `onclick`), which is now unconditionally read-only.
|
||||
- **Honorar and beauftragte Leistungsphasen stay editable in TWO places** on
|
||||
purpose (Projekteinstellungen and the "Terminplan aus Honorar" dialog, so the
|
||||
dialog remains usable on its own); both write through the same functions in
|
||||
@@ -1230,28 +1262,129 @@ step into a project explicit. Everything lives in `public/js/auftrag.js`:
|
||||
very same `openTodoModal()`/`openMilestoneModal()` as the Gantt, and the
|
||||
indices match because the list keeps `plan.todos`/`plan.milestones` order.
|
||||
The create buttons (`+ Aufgabe`, `+ Meilenstein`, `+ Vorlage`,
|
||||
`+ Terminplan`) exist twice — in the Gantt toolbar and above the list, the
|
||||
latter with an `…BtnList` id suffix; `renderPlanPage()` hides both sets on
|
||||
locked weeks.
|
||||
**A todo is only a Gantt row when it has BOTH a start and an end date.**
|
||||
`+ Terminplan`, `+ TODOs aus Protokoll`) exist twice — in the Gantt
|
||||
toolbar and above the list, the latter with an `…BtnList` id suffix;
|
||||
`renderPlanPage()` hides both sets on locked weeks.
|
||||
**"+ TODOs aus Protokoll"** (`#protokollTodosBtn`/`#protokollTodosBtnList`,
|
||||
`js/plan/plan-protokoll-todos.js`) is a second, genuinely independent way
|
||||
to turn a meeting protocol into tasks — Erik's explicit request: "ein
|
||||
Feature... das die Protokolle per KI einliest und dann aus den
|
||||
Protokollpunkten Aufgaben für die Aufgabenseite im Projekt erzeugt". It
|
||||
is deliberately **unrelated to `project_protokolle`/`protokoll_punkte`
|
||||
and to the Protokolle section** (`js/plan/plan-protokoll.js`) — Erik's
|
||||
own words: "Es sollen externe Protokolle im PDF-Format sein, die durch
|
||||
die KI eingelesen werden", i.e. an ad-hoc upload of a protocol that may
|
||||
never become (or already exist as) a stored Protokoll of this project,
|
||||
not a second entry point into the same table. Nothing is persisted on
|
||||
either end: no `project_protokolle` row, no file under
|
||||
`sessions/uploads/` — the PDF and every intermediate result live only in
|
||||
the request/response and the open dialog, the same "ad-hoc tool, no
|
||||
archive" model as the Plan Checker (`### Protokolle` below covers the
|
||||
always-persisted Protokoll-Import; this is its lightweight opposite).
|
||||
Three steps in one dialog (`#protokollTodosModal`), picked by Erik from a
|
||||
three-way design comparison (Taste-Skill artifact, "A — Schritt-Dialog"
|
||||
vs. a wide split-panel "Werkbank" vs. an inline card in the task list):
|
||||
a persistent chrome — title, a 3-segment progress bar
|
||||
(`#protokollTodosSteps`), and a fixed Zurück/Abbrechen/Weiter footer —
|
||||
stays on screen across all three steps, with only
|
||||
`#protokollTodosBody`'s content swapped per step (same "Container-Div
|
||||
wird je Phase neu befüllt" idea `applyTemplate()`'s dialog already uses).
|
||||
The right-hand "Weiter" button both advances the step AND fires that
|
||||
step's network call in one click (`protokollTodosWeiter()`); "Zurück"
|
||||
(`protokollTodosZurueck()`) only ever re-renders already-held module
|
||||
state, no re-fetch — except a re-shown file `<input>` can't be
|
||||
re-populated from script, so going back past step 0 means re-picking the
|
||||
file. A genuine KI failure (`zeigeFehler()`, red) is kept visually
|
||||
distinct from a successful-but-degraded fallback
|
||||
(`zeigeFallbackHinweis()`, the same amber `.protokoll-ki-fallback-banner`
|
||||
the Protokoll-Import uses) — a fallback is an accepted outcome, not an
|
||||
error.
|
||||
(1) upload a PDF + pick a provider → `POST
|
||||
/plan-todos-aus-protokoll/einlesen` reuses `erkennePunkteMitKI()`/
|
||||
`erkennePunkteAusText()` **unchanged** (server.js) to extract points,
|
||||
returned straight to the client, nowhere written to a table; (2) the
|
||||
extracted points appear as a checklist (every `status:'offen'` point
|
||||
pre-checked, `erledigt`/`info` pre-unchecked but still selectable — one
|
||||
surface covers both "whole protocol" and "just this one point"); the
|
||||
checked point objects (already in the browser, no server round-trip to
|
||||
refetch) go to `POST /plan-todos-aus-protokoll/aufgaben-vorschlag`,
|
||||
which runs the **second**, independent KI step, `leiteAufgabenMitKI()` →
|
||||
`leiteAufgabenMitChatGPT`/`leiteAufgabenMitClaude` (server.js, same
|
||||
`fetchMitTimeout()`/`LOKAL_DISPATCHER`/`reasoning_effort` plumbing as
|
||||
`erkennePunkteMitChatGPT`/`Claude`, own `KI_AUFGABEN_SCHEMA`/
|
||||
`KI_AUFGABEN_SYSTEM_PROMPT`) — this step *judges* the points rather than
|
||||
copying them: several points about the same action can collapse into
|
||||
one task, a purely informational point yields none; on any KI failure
|
||||
(or no provider chosen) the route falls back to a direct 1:1 mapping
|
||||
(`erkennungsart:'direkt'`), the same "always succeed, possibly degrade"
|
||||
policy as the Protokoll-Import; (3) the proposed tasks appear as a
|
||||
compact row list (`renderVorschlaegeHtml()`) — Erik's pick from a second,
|
||||
narrower Taste-Skill comparison scoped to just this step: a real test run
|
||||
had already produced 19 proposals from one protocol, and a full card per
|
||||
row would have been nothing but scrolling. Each row shows title plus a
|
||||
Fälligkeit/Zuständig badge; the full description, date field, and
|
||||
Zuständigen-Auswahl only appear once the row is tapped
|
||||
(`.protokoll-todo-vorschlag.open`, `.pt-row-detail`). A selection bar
|
||||
above the list (`N von M ausgewählt`, "Alle"/"Keine" —
|
||||
`protokollTodosAuswahlAlle()`) avoids unchecking rows one by one at that
|
||||
volume. Zuständig is pre-matched against `projectAssignees(app.project,
|
||||
app.week)` by a simple case-insensitive name match,
|
||||
`matchePersonenVonName()` — an external protocol's "Zuständig" is often
|
||||
an outside firm with no match at all, which is the expected common case,
|
||||
not a bug. Confirming pushes one `plan_todo` per checked row
|
||||
straight into `app.ensurePlanLocal().todos` **with both `start` and
|
||||
`end` always set** (today / the given Fälligkeit or today) — exactly
|
||||
`applyTemplate()`'s/`saveTodoAusKommentar()`'s push shape — specifically
|
||||
so every created task is a Gantt row immediately, not a dateless
|
||||
checklist item (see the GANTT-row rule right above); `app.savePlan()`
|
||||
then `fitPlanGanttRange()` (again mirroring `applyTemplate()`) so the
|
||||
visible GANTT window actually includes the new bars instead of only
|
||||
showing them in the plain Aufgaben list.
|
||||
**A todo is only a Gantt row when it has BOTH a start and an end date
|
||||
AND `showInGantt` is not explicitly `false`.**
|
||||
`plan.todos` is the one shared list either way — every todo always shows
|
||||
up here in Aufgaben, with its assignees (`t.assignees`, "Zuständige"
|
||||
chips in `openTodoModal()`, unrelated to whether it has a schedule) — but
|
||||
`plan-build-gantt.js` (the `buildGanttHtml()` extension that inserts
|
||||
TODO/milestone rows under the project row) only inserts a row for a todo
|
||||
with `todo.start && todo.end`; a title-only todo (created via `+ Aufgabe`
|
||||
or `+ TODO` with the date range left empty — nothing in `saveTodoEdit()`
|
||||
requires dates, only the title) stays a pure checklist item that appears
|
||||
solely here, never in the Gantt/Terminplan. The Aufgaben row for such a
|
||||
todo shows "ohne Termin" in place of a date range
|
||||
(`.plan-task-no-schedule`). Giving it a start/end later (editing the same
|
||||
todo through either view) makes a Gantt row for it appear on the next
|
||||
render — same object, no separate "convert to scheduled" action needed.
|
||||
with `todo.start && todo.end && todo.showInGantt !== false`; a
|
||||
title-only todo (created via `+ Aufgabe` or `+ TODO` with the date range
|
||||
left empty — nothing in `saveTodoEdit()` requires dates, only the title)
|
||||
stays a pure checklist item that appears solely here, never in the
|
||||
Gantt/Terminplan. The Aufgaben row for such a todo shows "ohne Termin" in
|
||||
place of a date range (`.plan-task-no-schedule`). Giving it a start/end
|
||||
later (editing the same todo through either view) makes a Gantt row for
|
||||
it appear on the next render — same object, no separate "convert to
|
||||
scheduled" action needed.
|
||||
**`showInGantt`** (`plan_todos.show_in_gantt`, `DEFAULT 1`,
|
||||
`migrateAddPlanTodoShowInGantt()`) is the independent half of that
|
||||
condition — Erik's explicit request, after "TODOs aus Protokoll"
|
||||
(`plan-protokoll-todos.js`) showed that a single import can easily create
|
||||
15-25 dated tasks at once, which — before this field existed — were all
|
||||
unconditionally forced into the Gantt as bars. A checkbox "Im Terminplan
|
||||
(GANTT) anzeigen" in the TODO-edit dialog (`#todoEditShowInGantt`,
|
||||
`App.html`, read/written in `openTodoModal()`/`saveTodoEdit()`,
|
||||
`js/plan/plan-modals.js`) lets a *dated* task still exist (tracked under
|
||||
Aufgaben, counted on "Meine Aufgaben", included in dependency-based
|
||||
Auto-Schedule/critical-path) without drawing a bar. Checked by default;
|
||||
`!== false` rather than `=== true` everywhere it's read, so a todo from
|
||||
before this column existed (or any object missing the field) keeps
|
||||
showing exactly as before — no retroactive behavior change. Carried
|
||||
through "Speichern & Neu" (`openTodoModal(null, {..., showInGantt})`)
|
||||
like the other frame fields (Zeitraum/Zuständige), so adding several
|
||||
hidden-from-Gantt tasks in a row doesn't mean re-unchecking it every
|
||||
time. **Not** touched by `plan-schedule.js`'s Auto-Schedule/critical-path
|
||||
(`t.start && t.end`, unchanged) or `plan-gantt.js`'s ghost-date overlay —
|
||||
a hidden todo is still a real, scheduled task for every purpose except
|
||||
drawing its own bar.
|
||||
`reorderPlanRows()` (`js/plan/plan-drag.js`) accounts for this: its
|
||||
DOM-row-count safety check and the reordering itself only ever touch
|
||||
todos that have `start && end` (`scheduledIds`) — a title-only todo has
|
||||
no drag handle in the Gantt to begin with and keeps its exact position in
|
||||
`plan.todos` untouched by any Gantt row drag.
|
||||
todos that have `start && end && showInGantt !== false`
|
||||
(`scheduledIds`) — exactly the same condition as the insertion above, so
|
||||
the count of draggable DOM rows and `scheduledIds.size` never drift
|
||||
apart. A title-only or Gantt-hidden todo has no drag handle in the Gantt
|
||||
to begin with and keeps its exact position in `plan.todos` untouched by
|
||||
any Gantt row drag.
|
||||
- **Leistung** — Leistungsstand & Abrechnung (`renderPlanLeistung()`,
|
||||
`js/plan/plan-leistung.js`): the value actually earned, per beauftragte
|
||||
Leistungsphase, as **Honorar × HOAI-Anteil der Phase × Fortschritt der
|
||||
@@ -1273,9 +1406,12 @@ step into a project explicit. Everything lives in `public/js/auftrag.js`:
|
||||
"### Protokolle" below for the data model and the "Fortschreiben"
|
||||
mechanism.
|
||||
- **Einstellungen** — project number/name (display only — the page is
|
||||
addressed by them), Gruppe, amtierende Leistungsphase, Beteiligten-Matrix
|
||||
(all still rendered by `plan-info.js`), plus Honorar and the **beauftragte
|
||||
Leistungsphasen** (`renderPlanSettings()`, `js/plan/plan-settings.js`).
|
||||
addressed by them), Gruppe, amtierende Leistungsphase (both still
|
||||
rendered by `plan-info.js`), plus Honorar, the **beauftragte
|
||||
Leistungsphasen** and the **Beteiligten-Matrix**
|
||||
(`renderPlanSettings()`/`planCycleAssign()`, `js/plan/plan-settings.js` —
|
||||
the Matrix moved here from a read-only `plan-info.js` display once it
|
||||
became genuinely editable, see "Ein Ort je Angabe" above).
|
||||
- `p.honorar`/`p.beauftragteLP` can now be edited in TWO places: here and in
|
||||
the "Terminplan aus Honorar" dialog (which keeps them so it stays usable on
|
||||
its own). Both write through `setHonorarBetrag()`/`toggleBeauftragteLp()` in
|
||||
@@ -3235,6 +3371,65 @@ across Gewerke or users. Erik's own words: "macht so keinen Sinn mehr."
|
||||
no special-casing needed. A redirect `GET /Test.html`-style old link isn't
|
||||
needed here since nothing ever pointed at this tab before it existed.
|
||||
|
||||
## Bewegung (Hover/Klick-Rückmeldung)
|
||||
|
||||
The app's interaction feedback is **token-driven and lives in one place**:
|
||||
five variables in `:root` (`styles.css`), applied by a clearly-marked
|
||||
"BEWEGUNG" block at the **end** of `styles.css` plus a smaller companion
|
||||
block at the end of `plan-styles.css` for the Projektbereich-only row types.
|
||||
Every rule reads through `var()`; changing the feel is a change to those
|
||||
five values, not to the components.
|
||||
|
||||
```
|
||||
--dur: 150ms kurze Rückmeldung (Fläche, Farbe, Anheben, Druck)
|
||||
--dur-lg: 200ms grössere Zustandswechsel (Dialog, Aufklappen)
|
||||
--ease-out cubic-bezier(.2,.8,.2,1), schnell los / weich aus
|
||||
--row-lift: -1px Hub einer anklickbaren Zeile beim Zeigen
|
||||
--press: .97 Stauchung beim Drücken
|
||||
```
|
||||
|
||||
Erik picked this level ("Spürbar") from a three-way live comparison
|
||||
(dezent / spürbar / lebendig) built with the Taste-Skill. **Level 3's
|
||||
entrance animations were deliberately NOT taken**: with ~30 projects in a
|
||||
list that re-renders on every save, rows animating in on each render is
|
||||
unrest, not polish. The rule that follows from that: the surface moves
|
||||
**only in response to pointing or clicking**, never on its own, and never
|
||||
in a loop.
|
||||
|
||||
- **Row lift + accent rail** on clickable rows (`.board-row-main`,
|
||||
`.bericht-rail-item`, `.planung-row`, `.dash-rand-zeile`, and in
|
||||
`plan-styles.css` `.todo-depends-item`, `.pt-row-main`, `.plan-task-row`).
|
||||
The rail is an **`inset` box-shadow growing 0 → 3px**, deliberately NOT a
|
||||
`::before` with `position:absolute`: `.board-row` is `draggable`
|
||||
(`js/projects/projects.js`), and an extra positioned element or a new
|
||||
stacking context on a dragged row can shift the drag image and hit
|
||||
targets. An inset shadow touches neither layout nor stacking. For the
|
||||
same reason the transform sits on the **inner** `.board-row-main`, never
|
||||
on the draggable `.board-row` itself (verified: the draggable element
|
||||
keeps `transform: none`).
|
||||
- **`.protokoll-liste-zeile` is the one exception** and must stay one: it
|
||||
already carries `border-left: 3px solid transparent`, which its
|
||||
`.ist-aktiv` state colours. It therefore animates *that existing border*
|
||||
instead of adding the inset shadow. Using both would paint 6 px of accent
|
||||
instead of 3.
|
||||
- **Dialogs** use `animation`, not `transition`: `.modal-bg` is toggled
|
||||
`display:none` ⇄ `flex` from JS, and a `display` change is not
|
||||
transitionable, whereas an animation restarts every time the element is
|
||||
rendered again (`modal-bg-auf` / `modal-auf`).
|
||||
- **`prefers-reduced-motion` was missing from this codebase entirely** and
|
||||
is now handled at the end of `styles.css`. Because the level is
|
||||
token-driven, the block simply flips the tokens (`--row-lift: 0px`,
|
||||
`--press: 1`, durations to 1ms); a blanket
|
||||
`transition-duration/animation-duration: 1ms !important` additionally
|
||||
catches the ~60 older rules that still hard-code their own timings.
|
||||
- **Keyboard focus** got explicit `:focus-visible` outlines on the same
|
||||
rows/chips. The lift and press effects answer mouse and touch only;
|
||||
without this the rework would have left keyboard use worse off than
|
||||
before.
|
||||
- **The Wandtafel (`body.wandtafel-aktiv`) is deliberately excluded** — a
|
||||
kiosk touch monitor has no hover, and that screen carries its own
|
||||
`--dash-*` design anyway.
|
||||
|
||||
## Docker
|
||||
|
||||
- `docker compose up --build` from the repo root. Compose maps `3003:3003`,
|
||||
|
||||
Reference in new issue
Block a user