Suche ueber Aufgaben
CI / pruefen (pull_request) Canceled after 0s

Zwei Luecken, die im Issue zusammen stehen.

**Im Aufgabendashboard fehlte ein Suchfeld.** Es gab Filter nach Status und
Projekt, aber keine Moeglichkeit, nach einem Wort zu suchen. Das Suchfeld
deckt Titel, Beschreibung, Labels und Projektname ab. Gefiltert wird im
Browser - die Aufgaben liegen dort ohnehin alle vor, es braucht also keine
zusaetzliche Abfrage und es gibt keine Verzoegerung beim Tippen.

Wer sucht, sucht dabei auch in Erledigtem: sonst faende man die Aufgabe von
letzter Woche nicht wieder, obwohl man genau sie meint. Der Schalter
"Erledigte einblenden" gilt nur ohne Suchbegriff.

**Die Volltextsuche kannte nur Notizen.** Task ist jetzt ebenfalls dabei,
nach demselben Muster: deutsche Textsuche, ts_headline fuer den Ausschnitt,
Hervorhebung als [[…]] statt als HTML. Deutsche Stammformen greifen damit
auch hier - "Rechnung" findet "Rechnungen".

Dazu ein Ausdrucksindex Task_fulltext_idx wie Note_fulltext_idx. Der
Ausdruck muss Zeichen fuer Zeichen dem in der Abfrage entsprechen, sonst
greift er nicht; nachgeprueft mit EXPLAIN, der Plan zeigt einen
Bitmap Index Scan darauf. "prisma migrate diff" laesst ihn in Ruhe -
Gegenprobe kommt leer heraus.

Das Antwortfeld heisst weiterhin "hits" und meint die Notizen. Umbenennen
haette nur Aufrufer gebrochen, ohne etwas zu verbessern; Aufgaben kommen als
eigenes Feld dazu. Beide Stellen, die Treffer anzeigen - Projektbrett und
Schnellsuche -, zeigen sie jetzt.

Geprueft: 261 Unit-Tests, 36 E2E-Tests. Die Volltextsuche zusaetzlich gegen
die laufende Anwendung: Treffer in Titel und Beschreibung, Erledigtes
inbegriffen, Stammformen greifen, fremde Konten finden nichts.

Refs #4
This commit is contained in:
2026-08-18 03:39:02 +02:00
parent 0ef0b3d5ca
commit afe57c4428
7 changed files with 333 additions and 25 deletions
+26 -3
View File
@@ -244,7 +244,7 @@ test("Wochenrueckblick zeigt, was diese Woche passiert ist", async ({ page }) =>
// Auch ueber die Schnellsuche erreichbar.
await page.goto("/");
await page.keyboard.press("Control+k");
await page.getByPlaceholder(/Projekt, Notiz oder Bereich/).fill("Rückblick");
await page.getByPlaceholder(/^Projekt, /).fill("Rückblick");
await page.keyboard.press("Enter");
await expect(page).toHaveURL(/\/rueckblick/);
});
@@ -264,11 +264,34 @@ test("Globale Zeitleiste zeigt den Verlauf ueber alle Projekte", async ({ page }
// Auch ueber die Schnellsuche erreichbar.
await page.goto("/");
await page.keyboard.press("Control+k");
await page.getByPlaceholder(/Projekt, Notiz oder Bereich/).fill("Verlauf");
await page.getByPlaceholder(/^Projekt, /).fill("Verlauf");
await page.keyboard.press("Enter");
await expect(page).toHaveURL(/\/verlauf/);
});
test("Aufgabendashboard laesst sich durchsuchen", async ({ page }) => {
await page.getByRole("link", { name: "Aufgaben", exact: true }).click();
await expect(page.getByRole("heading", { name: "Aufgaben", level: 1 })).toBeVisible();
const suchfeld = page.getByLabel("Aufgaben durchsuchen");
await expect(suchfeld).toBeVisible();
// Aus den frueheren Tests gibt es "Erste Aufgabe" und "Zweite Aufgabe".
await suchfeld.fill("Erste");
await expect(page.getByRole("listitem").filter({ hasText: "Erste Aufgabe" })).toBeVisible();
await expect(page.getByRole("listitem").filter({ hasText: "Zweite Aufgabe" })).toBeHidden();
// Ohne Treffer erklaert der Leerzustand, worin gesucht wurde.
await suchfeld.fill("gibtesnichtxyz");
await expect(page.getByText("Keine Treffer")).toBeVisible();
await expect(page.getByText(/Titel, Beschreibung, Labels und Projektname/)).toBeVisible();
// Zuruecksetzen bringt alles wieder.
await page.getByRole("button", { name: "Suche zuruecksetzen" }).click();
await expect(suchfeld).toHaveValue("");
await expect(page.getByRole("listitem").filter({ hasText: "Erste Aufgabe" })).toBeVisible();
});
test("Schnellsuche findet Projekt und Notiz", async ({ page }) => {
await page.keyboard.press("Control+k");
await expect(page.getByRole("dialog", { name: "Schnellsuche" })).toBeVisible();
@@ -276,7 +299,7 @@ test("Schnellsuche findet Projekt und Notiz", async ({ page }) => {
// Ohne Eingabe stehen Projekte und Bereiche bereit.
await expect(page.getByRole("dialog").getByText("Oberflaechentest")).toBeVisible();
await page.getByPlaceholder(/Projekt, Notiz oder Bereich/).fill("Zaunkoenig");
await page.getByPlaceholder(/^Projekt, /).fill("Zaunkoenig");
await expect(page.getByRole("dialog").getByText(/in Oberflaechentest/)).toBeVisible();
// Enter springt zum ersten Treffer.