diff --git a/app/Services/Help/HelpSyncService.php b/app/Services/Help/HelpSyncService.php index 5996628..de27426 100644 --- a/app/Services/Help/HelpSyncService.php +++ b/app/Services/Help/HelpSyncService.php @@ -4,6 +4,7 @@ namespace App\Services\Help; use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; @@ -70,6 +71,8 @@ class HelpSyncService } } + $articles = $this->hydrateRelatedTitles($articles); + $disk = config('help.disk'); $compiledPath = trim(config('help.compiled_path'), '/'); $written = []; @@ -87,6 +90,58 @@ class HelpSyncService return $written; } + private function hydrateRelatedTitles(Collection $articles): Collection + { + $titleIndex = $articles->mapWithKeys(function (array $article) { + $audience = Arr::get($article, 'audience'); + $locale = Arr::get($article, 'locale'); + $slug = Arr::get($article, 'slug'); + + if (! $audience || ! $locale || ! $slug) { + return []; + } + + return [$this->articleKey((string) $audience, (string) $locale, (string) $slug) => Arr::get($article, 'title')]; + }); + + return $articles->map(function (array $article) use ($titleIndex) { + $related = Arr::get($article, 'related', []); + + if (empty($related)) { + return $article; + } + + $audience = (string) Arr::get($article, 'audience'); + $locale = (string) Arr::get($article, 'locale'); + + $article['related'] = collect($related) + ->map(function ($item) use ($titleIndex, $audience, $locale) { + $slug = is_array($item) ? Arr::get($item, 'slug') : (is_string($item) ? $item : null); + + if (! $slug) { + return null; + } + + $title = $titleIndex->get($this->articleKey($audience, $locale, $slug)); + + return array_filter([ + 'slug' => $slug, + 'title' => $title, + ], static fn ($value) => $value !== null && $value !== ''); + }) + ->filter() + ->values() + ->all(); + + return $article; + }); + } + + private function articleKey(string $audience, string $locale, string $slug): string + { + return sprintf('%s::%s::%s', $audience, $locale, $slug); + } + private function parseFile(SplFileInfo $file): array { $contents = $this->files->get($file->getPathname()); diff --git a/docs/help/de/admin/faq-admin.md b/docs/help/de/admin/faq-admin.md index 96ea1cd..4f410fa 100644 --- a/docs/help/de/admin/faq-admin.md +++ b/docs/help/de/admin/faq-admin.md @@ -3,7 +3,7 @@ title: "FAQ: Event-Admin" locale: de slug: faq-admin audience: admin -summary: "Kurze Antworten auf die haeufigsten Fragen von Event-Admins." +summary: "Kurze Antworten auf die häufigsten Fragen von Event-Admins." version_introduced: 2025.4 requires_app_version: "^3.2.0" status: draft @@ -19,8 +19,8 @@ related: **Wo finde ich Join-Link oder QR-Code?** Im Event-Dashboard unter den Schnellaktionen. -**Gaeste koennen nicht beitreten.** -Pruefe, ob das Event **veroeffentlicht** ist, und teile den Join-Link oder QR erneut. +**Gäste können nicht beitreten.** +Prüfe, ob das Event **veröffentlicht** ist, und teile den Join-Link oder QR erneut. ## Uploads & Moderation **Uploads erscheinen nicht.** @@ -31,7 +31,7 @@ Im Control Room ausblenden, dann verschwindet es sofort aus der Galerie. ## Live Show **Die Live Show bleibt leer.** -Pruefe den Show-Link und ob Fotos freigegeben wurden. +Prüfe den Show-Link und ob Fotos freigegeben wurden. ### Weitere Hilfe -`control-room-moderation` fuer die Queue oder `live-show-setup` fuer die Leinwand. +`control-room-moderation` für die Queue oder `live-show-setup` für die Leinwand. diff --git a/docs/help/de/admin/index.md b/docs/help/de/admin/index.md index 65c75fa..77daf1f 100644 --- a/docs/help/de/admin/index.md +++ b/docs/help/de/admin/index.md @@ -18,7 +18,7 @@ Dieses Portal sammelt alles, was Event-Admins für Vorbereitung, Kern-Workflows | --- | --- | --- | | Dashboard | Wo sehe ich Status, KPIs und Schnellaktionen? | `tenant-dashboard-overview` | | Event-Vorbereitung | Was muss vor dem Start erledigt sein? | `event-prep-checklist` | -| FAQ | Was sind die haeufigsten Fragen? | `faq-admin` | +| FAQ | Was sind die häufigsten Fragen? | `faq-admin` | | Control Room | Wie moderiere ich Uploads und steuere die Queue? | `control-room-moderation` | | Live Show | Wie richte ich den Live-Show-Player ein? | `live-show-setup` | | Abschluss & Exporte | Was erledige ich nach dem Event? | `post-event-wrapup` | diff --git a/resources/js/admin/api.ts b/resources/js/admin/api.ts index a3a7194..aa66b15 100644 --- a/resources/js/admin/api.ts +++ b/resources/js/admin/api.ts @@ -365,7 +365,7 @@ export type HelpCenterArticleSummary = { updated_at?: string; status?: string; translation_state?: string; - related?: Array<{ slug: string }>; + related?: Array<{ slug: string; title?: string | null }>; }; export type HelpCenterArticle = HelpCenterArticleSummary & { diff --git a/tests/Feature/Help/HelpSyncServiceTest.php b/tests/Feature/Help/HelpSyncServiceTest.php index 87d7de9..ce1d057 100644 --- a/tests/Feature/Help/HelpSyncServiceTest.php +++ b/tests/Feature/Help/HelpSyncServiceTest.php @@ -20,5 +20,15 @@ class HelpSyncServiceTest extends TestCase $this->assertNotEmpty($result); Storage::disk('local')->assertExists('help/guest/en/articles.json'); Storage::disk('local')->assertExists('help/guest/de/articles.json'); + Storage::disk('local')->assertExists('help/admin/en/articles.json'); + + $articles = json_decode(Storage::disk('local')->get('help/admin/en/articles.json'), true); + $this->assertIsArray($articles); + + $controlRoom = collect($articles)->firstWhere('slug', 'control-room-moderation'); + $this->assertNotNull($controlRoom); + + $related = collect($controlRoom['related'] ?? [])->firstWhere('slug', 'event-prep-checklist'); + $this->assertSame('Event Preparation Checklist', $related['title'] ?? null); } }