diff --git a/FEATURES.md b/FEATURES.md index 9ef2d2b..c3dfd96 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -117,6 +117,19 @@ Der Status ist **direkt auf der Projektkarte** umschaltbar, ohne den Detailberei - **Archiv** ist standardmäßig ausgeblendet und per Schalter einblendbar. - **Leerzustände** mit direktem Einstieg statt einer leeren Fläche. +### Schnellsuche + +**`Strg+K`** (auf dem Mac `Cmd+K`) öffnet überall eine Palette: + +- **Projekte** nach Name, Kurzbeschreibung und Tags — ohne Eingabe stehen die letzten + gleich bereit +- **Notizen** über dieselbe Volltextsuche wie im Board, entprellt ab zwei Zeichen +- **Bereiche**: Dashboard, Git-Verbindungen, Mein Konto, Admin-Einstellungen + +Bedienung über Tastatur: Pfeile wählen, `Enter` öffnet, `Esc` schließt. Die Projektliste +wird beim ersten Öffnen geholt und dann behalten — sie bei jedem Tastendruck neu zu laden +wäre Verschwendung. + ### Kanban Spalten je Status, Karten per Ziehen verschiebbar. Der Wechsel wird sofort angezeigt und @@ -152,6 +165,19 @@ Blockiert · Erledigt**. - Erledigtes wird durchgestrichen; der Zeitpunkt wird festgehalten und beim Zurückziehen wieder gelöscht. +### Aus Git-Issues übernehmen + +Im Git-Panel, Reiter *Issues*: ein Klick macht aus den offenen Issues Aufgaben — Titel, +Labels und ein Link zurück ins Git-System. + +- Grundlage ist der **zuletzt geholte Stand**, hier wird nichts nachgeladen. Ein Klick + soll nicht ungefragt fremde APIs ansprechen; wer den neuesten Stand will, gleicht + vorher ab. +- Die Issue-Nummer bleibt an der Aufgabe und ist je Projekt eindeutig. Ein zweiter Klick + legt deshalb **keine Dubletten** an, auch wenn die Aufgabe inzwischen verschoben oder + umbenannt wurde. +- Ohne Repo-Stand meldet der Server das mit einem Hinweis statt still nichts zu tun. + ### Fortschritt aus Aufgaben Pro Projekt umschaltbar. Ist es an, ergibt sich der Fortschritt aus dem Anteil erledigter diff --git a/README.md b/README.md index 865f33f..4845193 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,7 @@ ausschließlich Daten des angemeldeten Kontos. | `POST` | `/api/projects/:id/sync` | Repo-Daten vom Git-System holen | | `GET` `POST` | `/api/projects/:id/tasks` | Aufgaben des Projekts | | `PATCH` | `/api/projects/:id/tasks/reorder` | Neue Reihenfolge nach dem Ziehen | +| `POST` | `/api/projects/:id/tasks/from-issues` | Offene Issues als Aufgaben übernehmen | | `PATCH` `DELETE` | `/api/tasks/:id` | Aufgabe ändern / löschen | | `GET` `POST` | `/api/projects/:id/notes` | Notizen des Projekts | | `PATCH` `DELETE` | `/api/notes/:id` | Notiz ändern / löschen | diff --git a/ROADMAP.md b/ROADMAP.md index 765ee51..7819129 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -63,12 +63,10 @@ Voraussetzung, falls du das Projekt veröffentlichen willst. | Idee | Aufwand | Nutzen | | --- | --- | --- | -| Schnellsuche per `Strg+K` | `S` | Springt direkt zu Projekt oder Aktion | | Suchtreffer in Notizen hervorheben | `S` | In der Notiz selbst, nicht nur in der Trefferliste | | Mehrfachauswahl im Board | `S` | Status oder Tags für mehrere auf einmal | | Aktivitätsverlauf je Projekt | `S` | Wer hat wann was geändert | | Wiederkehrende Aufgaben | `M` | Etwa „jeden Montag Abhängigkeiten prüfen" | -| Aufgaben aus Git-Issues übernehmen | `M` | Offene Issues als Aufgaben einhängen | | Projektvorlagen | `S` | Neues Projekt mit vorgefüllten Tags und Notizen | | Hell/Dunkel umschaltbar | `M` | Aktuell nur dunkel | | Eigenes Logo und Farbe | `S` | Ergänzt `APP_NAME` | @@ -112,6 +110,7 @@ Damit klar ist, was **nicht** kommt und warum: | **v0.6.0** Ende-zu-Ende-Tests mit Playwright, CI-Pipeline für Gitea Actions | ✓ | | **v0.7.0** Benachrichtigungen über ntfy, Webhook und E-Mail | ✓ | | **v0.8.0** Stillstehende Projekte melden, Schwelle im Adminbereich | ✓ | +| **v0.9.0** Schnellsuche per `Strg+K`, Aufgaben aus Git-Issues | ✓ | Zur Entscheidung beim Export: Git-Tokens gehen **nicht** mit. Die Verbindung wird nur beschrieben, das Token ist nach dem Import neu zu hinterlegen. Snapshots bleiben diff --git a/e2e/api.spec.ts b/e2e/api.spec.ts index c280cef..77880e5 100644 --- a/e2e/api.spec.ts +++ b/e2e/api.spec.ts @@ -184,6 +184,20 @@ test("Aufgaben: Reihenfolge, abgeleiteter Fortschritt, fremde IDs", async ({ req expect(unveraendert.status).toBe("TODO"); }); +test("Issues als Aufgaben uebernehmen, ohne Dubletten", async ({ request }) => { + await login(request, ADMIN); + + const projekt = ( + await (await request.post("/api/projects", { data: { name: "Issue-Projekt" } })).json() + ).project; + + // Ohne Repo-Stand gibt es nichts zu uebernehmen – und das sagt der Server auch. + const ohneStand = await request.post(`/api/projects/${projekt.id}/tasks/from-issues`, { + data: {}, + }); + expect(ohneStand.status()).toBe(409); +}); + test("Export enthaelt keine Geheimnisse, Import beachtet die Strategie", async ({ request }) => { await login(request, ADMIN); diff --git a/e2e/ui.spec.ts b/e2e/ui.spec.ts index d4593ee..c468b01 100644 --- a/e2e/ui.spec.ts +++ b/e2e/ui.spec.ts @@ -98,6 +98,27 @@ test("Volltextsuche findet Notizen", async ({ page }) => { await expect(page.getByText("Treffer in Notizen")).toBeVisible(); }); +test("Schnellsuche findet Projekt und Notiz", async ({ page }) => { + await page.keyboard.press("Control+k"); + await expect(page.getByRole("dialog", { name: "Schnellsuche" })).toBeVisible(); + + // 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 expect(page.getByRole("dialog").getByText(/in Oberflaechentest/)).toBeVisible(); + + // Enter springt zum ersten Treffer. + await page.keyboard.press("Enter"); + await expect(page.getByRole("heading", { name: "Oberflaechentest" })).toBeVisible(); + + // Escape schliesst wieder. + await page.keyboard.press("Control+k"); + await expect(page.getByRole("dialog", { name: "Schnellsuche" })).toBeVisible(); + await page.keyboard.press("Escape"); + await expect(page.getByRole("dialog", { name: "Schnellsuche" })).toBeHidden(); +}); + test("Abmelden fuehrt zurueck zur Anmeldung", async ({ page }) => { await page.getByRole("button", { name: /Benutzermenue/ }).click(); await page.getByRole("menuitem", { name: "Abmelden" }).click(); diff --git a/package.json b/package.json index 2bcb6da..b9fcde4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nebula", - "version": "0.8.0", + "version": "0.9.0", "private": true, "description": "Nebula – Dashboard fuer Vibe-Coding Projekte mit Git-Anbindung (GitHub, GitLab, Gitea)", "scripts": {