Update Suchen im Föderalen Entwicklungsportal authored by Jürgen Voskuhl's avatar Jürgen Voskuhl
...@@ -21,6 +21,7 @@ Ziel ist die Implementierung einer Suchfunktion für das Föderale Entwicklungsp ...@@ -21,6 +21,7 @@ Ziel ist die Implementierung einer Suchfunktion für das Föderale Entwicklungsp
* **Durchsuchbare Inhalte:** * **Durchsuchbare Inhalte:**
* **FIT-Standards:** Inhalte aus `content/fit-standards/from_openproject.json`. Durchsucht werden sollen die Felder `name`, `shortDescription` und `text`. * **FIT-Standards:** Inhalte aus `content/fit-standards/from_openproject.json`. Durchsucht werden sollen die Felder `name`, `shortDescription` und `text`.
* **Ressourcen:** Inhalte aus den Markdown-Dateien (`.de.md` und `.en.md`) in den Unterverzeichnissen von `content/resources/`. Durchsucht werden sollen das Front Matter (`name`, `shortDescription`) sowie der Hauptinhalt (Body nach dem Front Matter). * **Ressourcen:** Inhalte aus den Markdown-Dateien (`.de.md` und `.en.md`) in den Unterverzeichnissen von `content/resources/`. Durchsucht werden sollen das Front Matter (`name`, `shortDescription`) sowie der Hauptinhalt (Body nach dem Front Matter).
* Bereitstellung für Drittanwendungen: Die genannten Inhalte stehen idealerweise auch Drittanwendungen zur Verfügung.
* **Internationalisierung:** * **Internationalisierung:**
* Die gesamte Suchfunktionalität (Suchfelder, Ergebnisseite, durchsuchte Inhalte) muss für die unterstützten Sprachen Deutsch (`de`) und Englisch (`en`) funktionieren. * Die gesamte Suchfunktionalität (Suchfelder, Ergebnisseite, durchsuchte Inhalte) muss für die unterstützten Sprachen Deutsch (`de`) und Englisch (`en`) funktionieren.
* **Technologie:** * **Technologie:**
...@@ -52,15 +53,28 @@ Aufgrund der Anforderungen (client-seitig, einfache Integration, gute Fehlertole ...@@ -52,15 +53,28 @@ Aufgrund der Anforderungen (client-seitig, einfache Integration, gute Fehlertole
1. **Abhängigkeiten installieren:** 1. **Abhängigkeiten installieren:**
* `fuse.js` zu den Projektabhängigkeiten hinzufügen (`yarn add fuse.js`). * `fuse.js` zu den Projektabhängigkeiten hinzufügen (`yarn add fuse.js`).
2. **Datenbeschaffung & Konsolidierung (Build Time / Serverseitig):** 2. **Suchdaten-JSON-Dateien generieren (Build Time):**
* **Skript erstellen:** Ein Skript implementieren (z.B. `scripts/build-search-data.js`). Dieses Skript benötigt Zugriff auf Dateisystemfunktionen (`fs`) und Pfadmanipulation (`path`).
* **Funktionalität:**
* Das Skript iteriert durch die unterstützten Sprachen (z.B. `['de', 'en']`).
* Für jede Sprache (`lang`):
* Es ruft die notwendigen Funktionen auf (wahrscheinlich adaptiert aus `shared/use-content.ts`), um Markdown-Dateien für 'fit-standards' und 'resources' für diese spezifische Sprache zu lesen und zu parsen.
* Es kombiniert die Ergebnisse beider Quellen zu einem einzigen Array.
* Es reichert jeden Eintrag im Array an mit:
* Einem korrekt lokalisierten `link` (z.B. `/[lang]/resources/{slug}` oder `/[lang]/fit-standards/{slug}`).
* Einem `typeKey` (z.B. 'resource', 'fit-standard') zur einfachen Identifizierung und späteren Übersetzungs-Suche.
* Es stellt sicher, dass das Ausgabe-Verzeichnis (z.B. `public/search-data`) existiert.
* Es schreibt das finale, angereicherte Array für die Sprache in eine JSON-Datei (z.B. `public/search-data/[lang].json`).
* **Integration:** Den Build-Befehl in `package.json` (z.B. das `build`-Skript) anpassen, um dieses Node.js-Skript (`node scripts/build-search-data.js`) _vor_ dem standardmäßigen `next build`-Befehl auszuführen. Dies stellt sicher, dass die JSON-Dateien existieren, wenn `getStaticProps` sie benötigt.
3. **Datenbeschaffung & Konsolidierung (Build Time / Serverseitig):**
* **Strategie:** In `getStaticProps` der Suchseite (`pages/[lang]/search.tsx`) die Daten für die angeforderte Sprache sammeln. * **Strategie:** In `getStaticProps` der Suchseite (`pages/[lang]/search.tsx`) die Daten für die angeforderte Sprache sammeln.
* **Aufruf:** `readMarkdownFilesFromResources('fit-standards', lang)` und `readMarkdownFilesFromResources('resources', lang)` aus `shared/use-content.ts` verwenden. * **Aufruf:** `readMarkdownFilesFromResources('fit-standards', lang)` und `readMarkdownFilesFromResources('resources', lang)` aus `shared/use-content.ts` verwenden.
* **Kombinieren:** Die Ergebnisse beider Aufrufe zu einem einzigen Array zusammenführen. Jedes Objekt im Array ist vom Typ `ResourceContent` und enthält `name`, `shortDescription` und `text`. Einen `link` (korrekt lokalisiert, z.B. `/[lang]/resources/{slug}` oder `/fit-standards/{slug}`) und einen `type` ('standard' oder 'resource') hinzufügen. * **Kombinieren:** Die Ergebnisse beider Aufrufe zu einem einzigen Array zusammenführen. Jedes Objekt im Array ist vom Typ `ResourceContent` und enthält `name`, `shortDescription` und `text`. Einen `link` (korrekt lokalisiert, z.B. `/[lang]/resources/{slug}` oder `/fit-standards/{slug}`) und einen `type` ('standard' oder 'resource') hinzufügen.
* **Props:** Dieses sprachspezifische Array als Prop an die Seitenkomponente übergeben. * **Props:** Dieses sprachspezifische Array als Prop an die Seitenkomponente übergeben.
3. **Fuse.js Index erstellen (Client-Seitig):** 4. **Fuse.js Index erstellen (Client-Seitig):**
* Auf der Suchergebnisseite (`pages/[lang]/search.tsx`) das sprachspezifische Datenarray aus den Props empfangen. * Auf der Suchergebnisseite (`pages/[lang]/search.tsx`) das sprachspezifische Datenarray aus den Props empfangen.
* Mittels `useEffect` beim Laden der Komponente eine Fuse.js-Instanz erstellen. Fuse.js konfigurieren, um die Felder `name`, `shortDescription` und `text` zu durchsuchen. * Mittels `useEffect` beim Laden der Komponente eine Fuse.js-Instanz erstellen. Fuse.js konfigurieren, um die Felder `name`, `shortDescription` und `text` zu durchsuchen.
4. **Suchfeld-Komponente (`components/SearchInput.tsx`):** 5. **Suchfeld-Komponente (`components/SearchInput.tsx`):**
* Eine wiederverwendbare React-Komponente erstellen. * Eine wiederverwendbare React-Komponente erstellen.
* Enthält `<input type="search">` und Such-Icon/Button. * Enthält `<input type="search">` und Such-Icon/Button.
* `useState` für den Eingabewert verwenden. * `useState` für den Eingabewert verwenden.
...@@ -68,7 +82,7 @@ Aufgrund der Anforderungen (client-seitig, einfache Integration, gute Fehlertole ...@@ -68,7 +82,7 @@ Aufgrund der Anforderungen (client-seitig, einfache Integration, gute Fehlertole
* Bei Absenden: Zu `/[lang]/search?q={inputValue}` navigieren (`lang` aus `useRouter` beziehen). * Bei Absenden: Zu `/[lang]/search?q={inputValue}` navigieren (`lang` aus `useRouter` beziehen).
* **Integration 1 (Header):** In `views/layout/NavigationHeader.tsx` (Desktop-Layout) einbinden. * **Integration 1 (Header):** In `views/layout/NavigationHeader.tsx` (Desktop-Layout) einbinden.
* **Integration 2 (Startseite):** In `pages/[lang]/index.tsx` (oder verwendeter Hero-Komponente) einbinden. Styling gemäß Screenshot anpassen (gelber Hintergrund). * **Integration 2 (Startseite):** In `pages/[lang]/index.tsx` (oder verwendeter Hero-Komponente) einbinden. Styling gemäß Screenshot anpassen (gelber Hintergrund).
5. **Suchergebnisseite (`pages/[lang]/search.tsx`):** 6. **Suchergebnisseite (`pages/[lang]/search.tsx`):**
* Die dynamische Routen-Datei erstellen. * Die dynamische Routen-Datei erstellen.
* `getStaticPaths` implementieren (`/de/search`, `/en/search`). * `getStaticPaths` implementieren (`/de/search`, `/en/search`).
* `getStaticProps` implementieren (wie in Schritt 2 beschrieben). * `getStaticProps` implementieren (wie in Schritt 2 beschrieben).
...@@ -104,7 +118,7 @@ Aufgrund der Anforderungen (client-seitig, einfache Integration, gute Fehlertole ...@@ -104,7 +118,7 @@ Aufgrund der Anforderungen (client-seitig, einfache Integration, gute Fehlertole
* Die `components/SearchResultItem.tsx` Komponente verwenden (Titel, Quelle, Beschreibung, Link, Hover, Tooltip). * Die `components/SearchResultItem.tsx` Komponente verwenden (Titel, Quelle, Beschreibung, Link, Hover, Tooltip).
* Fall "Keine Ergebnisse" (wenn `displayedResults` leer ist) behandeln. * Fall "Keine Ergebnisse" (wenn `displayedResults` leer ist) behandeln.
* **Hinweis für zukünftige Paginierung:** Die `displayedResults` können später einfach in Seiten aufgeteilt werden (z.B. mittels `slice()` und zusätzlichem State für die aktuelle Seite), ohne die Such- oder Filterlogik grundlegend ändern zu müssen. Die Paginierungskomponente würde dann vor und nach der Ergebnisliste eingefügt werden. * **Hinweis für zukünftige Paginierung:** Die `displayedResults` können später einfach in Seiten aufgeteilt werden (z.B. mittels `slice()` und zusätzlichem State für die aktuelle Seite), ohne die Such- oder Filterlogik grundlegend ändern zu müssen. Die Paginierungskomponente würde dann vor und nach der Ergebnisliste eingefügt werden.
6. **Feinschliff & Testen:** 7. **Feinschliff & Testen:**
* Funktionalität (Suche, Filterung, Reset-Filter, Links, **korrekte Pill-Counts**), Layout, Interaktionen (Hover, Tooltips, Pill-Aktivierung) und Styling in beiden Sprachen gründlich testen. * Funktionalität (Suche, Filterung, Reset-Filter, Links, **korrekte Pill-Counts**), Layout, Interaktionen (Hover, Tooltips, Pill-Aktivierung) und Styling in beiden Sprachen gründlich testen.
## 3. Diagramm (Konzeptioneller Fluss) ## 3. Diagramm (Konzeptioneller Fluss)
... ...
......