From 781bc9f9ee5a982b8d4f8ec6275ba68cb426bb73 Mon Sep 17 00:00:00 2001 From: Schalli Date: Mon, 5 Oct 2026 08:22:43 +0200 Subject: [PATCH] fix(quick-261005-blw): Links in Notiz-Kacheln oeffnen in neuem Tab NoteLink ersetzt der Markdown-Vorschau: target=_blank, rel=noopener noreferrer; #-Sprungmarken bleiben im Fenster. Co-Authored-By: Claude Opus 5.5 (1M context) --- .planning/HANDOFF.json | 96 ------------------- .planning/STATE.md | 3 +- .../261005-blw-PLAN.md | 14 +++ .../261005-blw-SUMMARY.md | 13 +++ CHANGELOG.md | 4 + .../dashboard/widgets/note-task-list.test.tsx | 27 +++++- .../dashboard/widgets/note-task-list.tsx | 28 ++++++ .../dashboard/widgets/note-widget.tsx | 3 +- 8 files changed, 89 insertions(+), 99 deletions(-) delete mode 100644 .planning/HANDOFF.json create mode 100644 .planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-PLAN.md create mode 100644 .planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-SUMMARY.md diff --git a/.planning/HANDOFF.json b/.planning/HANDOFF.json deleted file mode 100644 index 326694f..0000000 --- a/.planning/HANDOFF.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "version": "1.0", - "timestamp": "2026-10-03T01:28:08.977Z", - "phase": null, - "phase_name": "Quick-Auftraege 02./03.10. nach Freigabe 1.9.2 (keine GSD-Phase)", - "phase_dir": null, - "plan": null, - "task": 0, - "total_tasks": 0, - "status": "paused", - "completed_tasks": [ - { - "id": 1, - "name": "261002-fm5: Finanzbuchhaltung - Module Kantinenabrechnung + Handelsware (aus HeadFlow portiert, DATEV-Export)", - "status": "done", - "commit": "b94d267" - }, - { - "id": 2, - "name": "261002-icv: Freigabestufe Benutzen/Verwalten (@ModuleManage), Matrix + Benutzerdetails", - "status": "done", - "commit": "7188733" - }, - { - "id": 3, - "name": "261002-k67 + 261002-kxc: Modul Nextcloud-Status (Ampel, Logos, Dashboard-Kachel) + Benachrichtigung bei Rot (Mail + Desktop)", - "status": "done", - "commit": "14674fc" - }, - { - "id": 4, - "name": "Duenne Widget-Scrollleiste; Nextcloud-Logos breiter/eckig, Upload auf 256 px verkleinert; Doppelklick-Sperre; Docker-Log je Anfrage + je Pruefung", - "status": "done", - "commit": "19340fc" - }, - { - "id": 5, - "name": "Desktop: Dateidialog folgt Dunkelmodus (set_theme); Linux-Dashboard ohne Einblend-Animation, Melder-Drossel 10 s", - "status": "done", - "commit": "53a49a1" - }, - { - "id": 6, - "name": "261003-387: Kategorien durch Admins bearbeitbar; Oberflaechentexte ohne Mandant", - "status": "done", - "commit": "3562b15" - } - ], - "remaining_tasks": [], - "blockers": [], - "async_jobs": [], - "human_actions_pending": [ - { - "action": "alpha auf 3562b15 ziehen (CI 491 gruen)", - "context": "Kategorien-Verwaltung + neutrale Texte", - "blocking": false - }, - { - "action": "Linux-Desktop-App per Tray aktualisieren, Upload im Dunkelmodus und Dashboard-Aufbau pruefen", - "context": "set_theme-Fix und Animations-Abschaltung nur per Messung/Unit-Test geprueft, nicht im echten Client des Users", - "blocking": false - }, - { - "action": "Logo der Schallicloud auf alpha neu hochladen", - "context": "altes Logo 2346 px wird erst beim Neuhochladen verkleinert", - "blocking": false - }, - { - "action": "Woche ab 05.10.: Kollege testet echte Kantinen-CSV und Handelsware-XLSX auf alpha + DATEV-Import", - "context": "Umlaute der Handelsware-TXT (UTF-8) evtl. auf Windows-1252 umstellen", - "blocking": false - } - ], - "decisions": [ - { - "decision": "Freigaben vergeben nur Admins; Verwalten darf Modul-Einstellungen", - "rationale": "User 02.10.", - "phase": "quick" - }, - { - "decision": "Firmenwerte (Beraternr, Mandantennr, Lohnart, Erloeskonto) nur als Einstellungen, nie als Vorgabe", - "rationale": "keine kundenspezifischen Defaults", - "phase": "quick" - }, - { - "decision": "Nextcloud-Ampel: gruen=neuester Patch im unterstuetzten Zweig, gelb=Update oder EOL<90 Tage, rot=EOL/unerreichbar/Wartung", - "rationale": "User-Auswahl 02.10.", - "phase": "quick" - } - ], - "uncommitted_files": [ - "?? .planning/quick/261002-fm5-finanzbuchhaltung-module-kantinenabrechn/testdata/" - ], - "next_action": "Beim Start fragen: alpha gezogen? Linux-App (dunkler Upload-Dialog, Dashboard-Aufbau) ok? Danach im alpha-Log pruefen, ob Anfrage-Schuebe (/reminders, /modules/active, /alerts) weg sind. Offen beantworten lassen: Menuename 'Administrator' vs. Seitentitel 'Verwaltung' angleichen?", - "context_notes": "main = origin/main = 3562b15 (+ wip-Commit). live = v1.9.2; seither nur Beta. DISPLAY :10 auf dem Dev-Host ist die SICHTBARE Sitzung des Users - keine GUI-Tests ohne Ankuendigung (Rezept in Memory reference_linux_client_test). Lokal: MailHog laeuft (8025), Testkategorie 'Serverraum', Test-Clouds im Nextcloud-Modul. Docker-Log hat jetzt [HTTP]-Zeilen je Anfrage." -} \ No newline at end of file diff --git a/.planning/STATE.md b/.planning/STATE.md index f0b251f..291f4bc 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -31,7 +31,7 @@ See: .planning/PROJECT.md (updated 2026-07-17) Phase: 18 (desktop-client-fertigstellen) — COMPLETE (2026-09-17, Verifikation passed, Windows-Bedienprobe bestanden) Plan: 6 of 6 Status: Alle 18 Phasen abgeschlossen; Version 1.2.0 freigegeben. Kein laufender Meilenstein. Nach 1.2.0 auf main (Beta): Bildmarke in Akzentfarbe, CI-Desktop-Skip, Favoriten-Symbol/-Sortierung, Desktop-Server-Adresse, Update in der App (signiert), Versionszeile auf der Setup-Seite — alles verifiziert und auf VM/CI nachgewiesen -Last activity: 2026-10-03 - Quick 261003-387 Kategorien bearbeitbar (lokal nachgewiesen, nicht gepusht) +Last activity: 2026-10-05 - Quick 261005-blw Notiz-Links in neuem Tab Progress: [██████████] 99% @@ -492,6 +492,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests. | 261002-k67 | Modul Nextcloud-Status mit Ampel-Kacheln und Dashboard-Uebersicht | 2026-10-02 | 5ef7b0c..87a7b7c | [261002-k67-modul-nextcloud-status-mit-ampel-kacheln](.planning/quick/261002-k67-modul-nextcloud-status-mit-ampel-kacheln/) | | 261002-kxc | Nextcloud-Status: Benachrichtigung bei Rot je Benutzer (Mail + Desktop-Hinweis), Klartext-Fehler | 2026-10-02 | faed0d7..6c4bff6 | [261002-kxc-nextcloud-status-benachrichtigung-bei-ro](.planning/quick/261002-kxc-nextcloud-status-benachrichtigung-bei-ro/) | | 261003-387 | Kategorien durch Admins bearbeitbar (anlegen, umbenennen, sortieren, loeschen mit Verschieben, Module zuordnen) | 2026-10-03 | 8ec116c..f2c0a89 | [261003-387-kategorien-durch-admins-bearbeitbar-umbe](.planning/quick/261003-387-kategorien-durch-admins-bearbeitbar-umbe/) | +| 261005-blw | Links in Notiz-Kacheln oeffnen in neuem Tab | 2026-10-05 | (dieser Commit) | [261005-blw-notiz-links-in-neuem-tab](.planning/quick/261005-blw-notiz-links-in-neuem-tab/) | ## Deferred Items diff --git a/.planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-PLAN.md b/.planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-PLAN.md new file mode 100644 index 0000000..9e06613 --- /dev/null +++ b/.planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-PLAN.md @@ -0,0 +1,14 @@ +--- +quick_id: 261005-blw +slug: notiz-links-in-neuem-tab +date: 2026-10-05 +--- + +# Notiz-Links in neuem Tab + +User 05.10.: URL in einer Notiz oeffnet im selben Fenster. Links sollen immer in einem neuen Tab oeffnen. + +## Task 1 +- `NoteLink` in `note-task-list.tsx`: ersetzt `` der Markdown-Vorschau, setzt `target="_blank"` + `rel="noopener noreferrer"`, `#`-Sprungmarken bleiben im Fenster, `node` nicht ins DOM. +- In `note-widget.tsx` per `previewOptions.components.a` einhaengen. +- Test mit echtem `MDEditor.Markdown` + `rehypeSanitize` (Markdown-Link, nackte URL, Sprungmarke). diff --git a/.planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-SUMMARY.md b/.planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-SUMMARY.md new file mode 100644 index 0000000..2514aa8 --- /dev/null +++ b/.planning/quick/261005-blw-notiz-links-in-neuem-tab/261005-blw-SUMMARY.md @@ -0,0 +1,13 @@ +--- +quick_id: 261005-blw +status: complete +date: 2026-10-05 +--- + +# 261005-blw: Notiz-Links in neuem Tab — SUMMARY + +- `NoteLink` (note-task-list.tsx) ersetzt Links der Notiz-Vorschau: neuer Tab, `rel="noopener noreferrer"`; `#...` bleibt im selben Fenster. +- Eingehaengt ueber `previewOptions.components.a` in note-widget.tsx. +- Desktop-App: Opener-Link-Skript faengt `target="_blank"` auf `document` ab und oeffnet im System-Browser. +- Nachweis: Vitest note-* 24/24 gruen (neuer Test mit echtem MDEditor.Markdown + rehypeSanitize), tsc sauber; Biome-Befunde der drei Dateien unveraendert gegenueber HEAD (5, vorbestehend). +- Nicht im Browser geprueft (lokaler Web-Container nicht neu gebaut). diff --git a/CHANGELOG.md b/CHANGELOG.md index 2338dcc..5c7317c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T - Handelsware: Eine Excel-Liste mit Handelswaren-Umsätzen hochladen. Tessera ordnet jedem Produkt sein Konto zu, markiert neue Produkte mit „neu“ und vergibt ihnen das nächste freie Gegenkonto. Das Buchungsdatum wird aus dem Dateinamen abgeleitet (letzter Tag des Monats) und lässt sich ändern. Neue Konten werden erst beim Herunterladen der Buchungsdatei gespeichert. Im Reiter „Konten“ pflegen Sie die Kontenliste, lesen sie aus einer CSV-Datei ein (ersetzt alle vorhandenen Konten, nach Rückfrage) und exportieren sie als CSV. Standard-Erlöskonto und Startwert für das Gegenkonto trägt ein Administrator einmalig ein. - Modul-Freigaben haben jetzt zwei Stufen: „Benutzen“ (wie bisher) und „Verwalten“. Wer ein Modul verwalten darf, ändert dessen Einstellungen selbst, ohne Administrator zu sein, zum Beispiel in der Kantinenabrechnung, bei Handelsware und bei den Proxmox-Servern. Ein Administrator wählt die Stufe je Gruppe in der Freigaben-Matrix oder je Benutzer in den Benutzerdetails; bestehende Freigaben bleiben „Benutzen“. Freigaben vergeben und Module aktivieren dürfen weiterhin nur Administratoren. Das Modul DKV-Rechnung steht Administratoren und Benutzern mit „Verwalten“ zur Verfügung. +### Behoben + +- Links in Notiz-Kacheln öffnen jetzt immer in einem neuen Tab, statt das Dashboard im selben Fenster zu verlassen. In der Desktop-App öffnen sie wie gewohnt im Browser. + ## 1.9.2 – 2026-10-02 ### Neu diff --git a/apps/web/src/components/dashboard/widgets/note-task-list.test.tsx b/apps/web/src/components/dashboard/widgets/note-task-list.test.tsx index 1a419a7..609a496 100644 --- a/apps/web/src/components/dashboard/widgets/note-task-list.test.tsx +++ b/apps/web/src/components/dashboard/widgets/note-task-list.test.tsx @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'; import { describe, expect, it, vi } from 'vitest'; import MDEditor from '@uiw/react-md-editor'; import rehypeSanitize from 'rehype-sanitize'; -import { NoteCheckbox, isTaskLine, toggleTaskLine } from './note-task-list'; +import { NoteCheckbox, NoteLink, isTaskLine, toggleTaskLine } from './note-task-list'; describe('note-task-list', () => { describe('isTaskLine', () => { @@ -143,5 +143,30 @@ describe('note-task-list', () => { expect((boxes[2] as HTMLInputElement).checked).toBe(true); expect((boxes[3] as HTMLInputElement).checked).toBe(false); }); + + it('oeffnet Links in einem neuen Tab, Sprungmarken im selben Fenster', () => { + const SRC = + '[Doku](https://example.com/doku) und https://example.org\n\n[oben](#oben)'; + + const { container } = render( + , + ); + + const doku = container.querySelector('a[href="https://example.com/doku"]'); + const auto = container.querySelector('a[href="https://example.org"]'); + const hash = container.querySelector('a[href="#oben"]'); + + for (const link of [doku, auto]) { + expect(link?.getAttribute('target')).toBe('_blank'); + expect(link?.getAttribute('rel')).toBe('noopener noreferrer'); + expect(link?.hasAttribute('node')).toBe(false); + } + expect(hash).not.toBeNull(); + expect(hash?.hasAttribute('target')).toBe(false); + }); }); }); diff --git a/apps/web/src/components/dashboard/widgets/note-task-list.tsx b/apps/web/src/components/dashboard/widgets/note-task-list.tsx index 7073cc2..fce4098 100644 --- a/apps/web/src/components/dashboard/widgets/note-task-list.tsx +++ b/apps/web/src/components/dashboard/widgets/note-task-list.tsx @@ -115,3 +115,31 @@ export function NoteCheckbox({ /> ); } + +/** + * Ersetzt das von react-markdown gerenderte ``: Links aus Notizen oeffnen + * immer in einem neuen Tab, damit das Dashboard nicht verlassen wird. + * Sprungmarken innerhalb der Notiz (`#...`) bleiben im selben Fenster. + * `node` wird wie bei NoteCheckbox nicht ins DOM durchgereicht. In der + * Desktop-App faengt das Opener-Link-Skript `target="_blank"` ab und + * oeffnet den Link im System-Browser. + */ +export function NoteLink({ + node: _node, + href, + children, + ...rest +}: React.AnchorHTMLAttributes & { node?: unknown }) { + if (href?.startsWith('#')) { + return ( + + {children} + + ); + } + return ( + + {children} + + ); +} diff --git a/apps/web/src/components/dashboard/widgets/note-widget.tsx b/apps/web/src/components/dashboard/widgets/note-widget.tsx index 021aee4..b4aaa7e 100644 --- a/apps/web/src/components/dashboard/widgets/note-widget.tsx +++ b/apps/web/src/components/dashboard/widgets/note-widget.tsx @@ -8,7 +8,7 @@ import MDEditor, { commands } from '@uiw/react-md-editor'; import rehypeSanitize from 'rehype-sanitize'; import type { WidgetProps } from '../widget-registry'; import { updateWidgetConfig } from '@/lib/dashboard-api'; -import { NoteCheckbox, toggleTaskLine } from './note-task-list'; +import { NoteCheckbox, NoteLink, toggleTaskLine } from './note-task-list'; const DEBOUNCE_MS = 1500; @@ -156,6 +156,7 @@ export function NoteWidget({ instanceId, config }: WidgetProps) { onToggle={(element) => handleCheckboxToggleRef.current(element)} /> ), + a: NoteLink, }, }), [],