- 3/3 tasks complete (RED test → GREEN implementation → full suite gate) - 88/88 web tests passing (18 test files, +7 link-widget tests) - web + api tsc --noEmit clean - LinkWidget: single-link D-06 via FavoriteLink backend reuse
7.0 KiB
phase, plan, subsystem, tags, status, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | status | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 08-dashboard-widgets-vollimplementierung | 04 | dashboard-widgets |
|
complete |
|
|
|
|
|
Phase 08 Plan 04: Link Widget Summary
Single-Link-Widget (DASH-09 / D-06) via TDD Red/Green-Zyklus: ein kompaktes Widget das genau einen Link anzeigt, den FavoriteLink-Backend von Plan 03 (widgetId-Scoping) wiederverwendet, und zwischen Zeilen- und Kachelansicht umgeschaltet werden kann.
What Was Built
-
LinkWidget (
link-widget.tsx): 'use client', useTranslations('widgets'). Laed beim Mount via fetchFavorites(instanceId) und nimmt den ersten Eintrag als den einzigen Link (Single-Link-Constraint D-06). Kein neues Backend — FavoriteLink-Tabelle und FavoritesModule aus Plan 03 werden via widgetId=instanceId isoliert genutzt. -
Single-Link-Enforcement: Das Hinzufuegen-Formular wird nur gerendert wenn
link == null(nicht wenn ein Link bereits existiert). Das ist der "D-06: genau einen Link" Contract. -
Ansichten (D-06): Standard-Listenansicht (
data-testid="link-list": Icon + Titel in einer Zeile) und Kachelansicht (data-testid="link-tile": Icon oben, Titel darunter zentriert). Umschaltbar im Edit-Modus via viewMode-Toggle-Buttons, persistent via updateWidgetConfig(). -
CRUD im Edit-Modus: Hinzufuegen-Formular (wenn kein Link vorhanden), Inline-Bearbeiten (Titel, URL, optionale Logo-URL), Loeschen-Button. Alle CRUD-Operationen rufen createFavorite/updateFavorite/deleteFavorite auf.
-
Icon-Display:
<img src={iconUrl}>mit onError-Hide +<span data-testid="link-letter-fallback">als Buchstaben-Fallback. Kein dangerouslySetInnerHTML (T-08-11). renderIcon()-Helper fuer list/tile. -
Sicherheit: Links mit
target="_blank" rel="noreferrer"(T-08-12). Anchor-Klick im Edit-Modus via preventDefault() blockiert. -
i18n:
link.*-Keys in de.json und en.json hinzugefuegt: loading, empty, addTitle, addUrl, addButton, listView, gridView, editButton, deleteButton, saveButton, cancelButton, error. -
page.tsx Wiring:
wireLinkWidget(LinkWidget)und entsprechender Import hinzugefuegt.
Tasks
| Task | Description | Status | Commit |
|---|---|---|---|
| 1 | RED: Failing tests fuer LinkWidget single-link contract | Complete | 4657e50 |
| 2 | GREEN: LinkWidget implementation + i18n keys + page.tsx wiring | Complete | e9914f6 |
| 3 | Full Phase-8 Suite + Type-Check Gate | Complete | (verification only) |
Test Results
- link-widget.test.tsx: 7 Tests — fetchFavorites mit instanceId, Anchor target/_blank/rel=noreferrer, empty state, add-Formular, single-link enforcement, inline edit, view toggle (list/tile), letter fallback
- Full Web Suite: 88/88 Tests bestehen (18 Test-Dateien; +7 neue gegenueber Plan 03's 81/81)
- Web TypeScript: sauber (0 Fehler)
- API TypeScript: sauber (0 Fehler)
Deviations from Plan
Worktree Fast-Forward (auto-fix, Rule 3)
Found during: Pre-task setup
Issue: Das Worktree-Branch worktree-agent-a2d7feebc2349e393 war bei 88db54f (Quick-Fix-Commit aus frueherem Task), nicht am erwarteten Basis-Commit 7b42a3a (Main nach Wave 3). Phase-08 wave-1, wave-2 und wave-3 Artefakte (widget-registry, FavoritesModule, StopwatchWidget) fehlten im Worktree.
Fix: git merge main --ff-only — Fast-Forward auf 7b42a3a. Keine Konflikte. Node-Modules-Symlinks auf main repo danach erstellt.
Impact: Keiner. Alle vorherigen Plan-Artefakte standen vollstaendig zur Verfuegung.
Node-Modules-Symlinks (auto-fix, Rule 3)
Found during: Task 1 Verifikation (RED-Phase)
Issue: Worktree hatte keine node_modules (vitest: not found).
Fix: Symlinks von MAIN_REPO/{,apps/web,apps/api,packages/shared}/node_modules -> entsprechende Worktree-Verzeichnisse.
Impact: Keiner.
dangerouslySetInnerHTML grep-count = 1 (kein Problem)
Das Vorkommen ist in einem JSDoc-Kommentar (Zeile 26) der erklaert WARUM dangerouslySetInnerHTML nicht verwendet wird. Kein tatsaechlicher JSX-Attribut-Einsatz. T-08-11 korrekt mitigiert.
Known Stubs
Keine Stubs. LinkWidget ist vollstaendig implementiert und mit dem FavoriteLink-Backend verbunden.
Threat Surface Scan
Alle Threats aus dem Plan-Threat-Register wurden mitigiert. Keine neuen Sicherheitsoberflaechen eingefuehrt:
| Threat ID | Status |
|---|---|
| T-08-10 (Spoofing via FavoritesService-Reuse) | Mitigiert — FavoritesService userId+widgetId-Scope von Plan 03 uebernommen (kein neuer Endpunkt) |
| T-08-11 (XSS via icon render) | Mitigiert — iconUrl nur via <img src>; kein dangerouslySetInnerHTML |
| T-08-12 (open redirect) | Mitigiert — Anchor mit target="_blank" rel="noreferrer" |
Self-Check: PASSED
| Item | Status |
|---|---|
| link-widget.tsx | FOUND |
| link-widget.test.tsx | FOUND |
| wireLinkWidget in page.tsx | FOUND |
| link.* keys in de.json | FOUND |
| link.* keys in en.json | FOUND |
Commit 4657e50 (RED) |
FOUND |
Commit e9914f6 (GREEN) |
FOUND |
| Full suite 88/88 | PASSED |
| Web TypeScript: 0 errors | PASSED |
| API TypeScript: 0 errors | PASSED |