From 40ace5386751a802149852fe7269ef34f5dbfb8c Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 26 Mar 2026 14:32:23 +0100 Subject: [PATCH] feat(help): improve help content across all 18 apps, add shared Mana & Privacy FAQs - Expand FAQ entries from ~5 to 8-14 per app with app-specific feature documentation - Add comprehensive features, shortcuts, and keyboard shortcut sections - Integrate shared getManaFAQs() in 10 apps with /mana page - Integrate shared getPrivacyFAQs() in all 18 apps with app-specific data types - Add unit tests for help content in all 18 apps (72 tests total) - Tests verify: DE/EN content, matching FAQ/feature counts, unique IDs, contact info Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 15 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 23 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 258 ++++++++++++++++ .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 23 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 276 ++++++++++++++++++ .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 13 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 13 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 13 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 13 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 23 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- .../web/src/lib/content/help/index.test.ts | 47 +++ .../apps/web/src/lib/content/help/index.ts | 14 +- 36 files changed, 1439 insertions(+), 189 deletions(-) create mode 100644 apps/calendar/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/chat/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/citycorners/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/citycorners/apps/web/src/lib/content/help/index.ts create mode 100644 apps/clock/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/contacts/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/context/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/manacore/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/manadeck/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/matrix/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/matrix/apps/web/src/lib/content/help/index.ts create mode 100644 apps/mukke/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/photos/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/picture/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/planta/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/presi/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/questions/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/storage/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/todo/apps/web/src/lib/content/help/index.test.ts create mode 100644 apps/zitare/apps/web/src/lib/content/help/index.test.ts diff --git a/apps/calendar/apps/web/src/lib/content/help/index.test.ts b/apps/calendar/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..cfb673b12 --- /dev/null +++ b/apps/calendar/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getCalendarHelpContent } from './index'; + +describe('Calendar Help Content', () => { + it('returns valid German content', () => { + const content = getCalendarHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getCalendarHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getCalendarHelpContent('de'); + const en = getCalendarHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getCalendarHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/calendar/apps/web/src/lib/content/help/index.ts b/apps/calendar/apps/web/src/lib/content/help/index.ts index 81b7fd969..906656f8f 100644 --- a/apps/calendar/apps/web/src/lib/content/help/index.ts +++ b/apps/calendar/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getCalendarHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -82,19 +83,7 @@ export function getCalendarHelpContent(locale: string): HelpContent { ? ['sync', 'caldav', 'google', 'apple', 'extern'] : ['sync', 'caldav', 'google', 'apple', 'external'], }, - { - id: 'faq-privacy', - question: t('Wie werden meine Daten geschützt?', 'How is my data protected?'), - answer: t( - '

Deine Daten sind sicher:

', - '

Your data is secure:

' - ), - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Kalenderdaten', dataTypeEN: 'calendar data' }), ], features: [ { diff --git a/apps/chat/apps/web/src/lib/content/help/index.test.ts b/apps/chat/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..23b833183 --- /dev/null +++ b/apps/chat/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getChatHelpContent } from './index'; + +describe('Chat Help Content', () => { + it('returns valid German content', () => { + const content = getChatHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getChatHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getChatHelpContent('de'); + const en = getChatHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getChatHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/chat/apps/web/src/lib/content/help/index.ts b/apps/chat/apps/web/src/lib/content/help/index.ts index 3e48e2952..4a5643bb4 100644 --- a/apps/chat/apps/web/src/lib/content/help/index.ts +++ b/apps/chat/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getChatHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -56,18 +57,16 @@ export function getChatHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['vergleich', 'modelle', 'test'] : ['compare', 'models', 'test'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Chats geschützt?' : 'How are my chats protected?', - answer: isDE - ? '

Deine Unterhaltungen sind sicher:

' - : '

Your conversations are secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'sicherheit', 'dsgvo'] : ['privacy', 'security', 'gdpr'], - }, + ...getPrivacyFAQs(locale, { + dataTypeDE: 'Chats', + dataTypeEN: 'chats', + extraBulletsDE: [ + 'Lokale Modelle: Bei lokalen Modellen verlassen deine Daten nie unseren Server', + ], + extraBulletsEN: [ + 'Local models: With local models, your data never leaves our server', + ], + }), ], features: [ { diff --git a/apps/citycorners/apps/web/src/lib/content/help/index.test.ts b/apps/citycorners/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..83c1166da --- /dev/null +++ b/apps/citycorners/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getCityCornersHelpContent } from './index'; + +describe('CityCorners Help Content', () => { + it('returns valid German content', () => { + const content = getCityCornersHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getCityCornersHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getCityCornersHelpContent('de'); + const en = getCityCornersHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getCityCornersHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/citycorners/apps/web/src/lib/content/help/index.ts b/apps/citycorners/apps/web/src/lib/content/help/index.ts new file mode 100644 index 000000000..1a2ca0064 --- /dev/null +++ b/apps/citycorners/apps/web/src/lib/content/help/index.ts @@ -0,0 +1,258 @@ +/** + * Help content for CityCorners app + */ + +import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; + +export function getCityCornersHelpContent(locale: string): HelpContent { + const isDE = locale === 'de'; + + return { + faq: [ + { + id: 'faq-locations', + question: isDE ? 'Wie finde ich Orte in Konstanz?' : 'How do I find places in Konstanz?', + answer: isDE + ? '

CityCorners zeigt dir die besten Orte in Konstanz:

' + : '

CityCorners shows you the best places in Konstanz:

', + category: 'features', + order: 1, + language: isDE ? 'de' : 'en', + tags: isDE ? ['orte', 'suche', 'kategorien'] : ['locations', 'search', 'categories'], + }, + { + id: 'faq-map', + question: isDE ? 'Wie funktioniert die Kartenansicht?' : 'How does the map view work?', + answer: isDE + ? '

Die interaktive Leaflet-Karte zeigt alle Orte mit farbcodierten Markern nach Kategorie:

' + : '

The interactive Leaflet map shows all locations with color-coded markers by category:

', + category: 'features', + order: 2, + language: isDE ? 'de' : 'en', + tags: isDE ? ['karte', 'marker', 'navigation'] : ['map', 'markers', 'navigation'], + }, + { + id: 'faq-favorites', + question: isDE ? 'Wie speichere ich Favoriten?' : 'How do I save favorites?', + answer: isDE + ? '

Melde dich an und klicke auf das Herz-Symbol bei einem Ort, um ihn als Favorit zu speichern. Alle Favoriten findest du im Bereich Favoriten. Favoriten werden optimistisch aktualisiert — du siehst die Änderung sofort.

' + : '

Sign in and click the heart icon on a location to save it as a favorite. Find all favorites in the Favorites section. Favorites are optimistically updated — you see the change immediately.

', + category: 'features', + order: 3, + language: isDE ? 'de' : 'en', + tags: isDE ? ['favoriten', 'speichern', 'merken'] : ['favorites', 'save', 'bookmarks'], + }, + { + id: 'faq-add-location', + question: isDE ? 'Kann ich eigene Orte hinzufügen?' : 'Can I add my own locations?', + answer: isDE + ? '

Ja! Melde dich an und nutze die Funktion Ort hinzufügen:

  1. Suche den Ort per Web-Lookup — Informationen werden automatisch aus dem Web extrahiert
  2. Ergänze oder bearbeite die Informationen (Name, Beschreibung, Adresse)
  3. Wähle eine der 11 Kategorien
  4. Sende den Ort ab

Der Web-Lookup nutzt unseren eigenen Suchdienst (mana-search).

' + : '

Yes! Sign in and use the Add Location feature:

  1. Search for the place via web lookup — information is automatically extracted from the web
  2. Complete or edit the information (name, description, address)
  3. Choose one of the 11 categories
  4. Submit the location

The web lookup uses our own search service (mana-search).

', + category: 'features', + order: 4, + language: isDE ? 'de' : 'en', + tags: isDE ? ['hinzufügen', 'beitragen', 'orte'] : ['add', 'contribute', 'locations'], + }, + { + id: 'faq-detail-timeline', + question: isDE ? 'Was zeigen die Detailseiten?' : 'What do detail pages show?', + answer: isDE + ? '

Jeder Ort hat eine ausführliche Detailseite:

' + : '

Each location has a detailed page:

', + category: 'features', + order: 5, + language: isDE ? 'de' : 'en', + tags: isDE ? ['details', 'timeline', 'historie'] : ['details', 'timeline', 'history'], + }, + { + id: 'faq-categories', + question: isDE ? 'Welche Kategorien gibt es?' : 'What categories are available?', + answer: isDE + ? '

CityCorners bietet 11 Kategorien:

Jede Kategorie hat eine eigene Farbe auf der Karte und in den Karten-Ansichten.

' + : '

CityCorners offers 11 categories:

Each category has its own color on the map and in card views.

', + category: 'features', + order: 6, + language: isDE ? 'de' : 'en', + tags: isDE ? ['kategorien', 'typen', 'filter'] : ['categories', 'types', 'filter'], + }, + { + id: 'faq-i18n', + question: isDE ? 'Ist CityCorners mehrsprachig?' : 'Is CityCorners multilingual?', + answer: isDE + ? '

Ja! CityCorners ist in Deutsch und Englisch verfügbar. Du kannst die Sprache jederzeit über den Sprachumschalter in der Navigation ändern. Die Einstellung wird lokal gespeichert.

' + : '

Yes! CityCorners is available in German and English. You can change the language at any time via the language switcher in the navigation. The setting is saved locally.

', + category: 'general', + order: 7, + language: isDE ? 'de' : 'en', + tags: isDE ? ['sprache', 'deutsch', 'englisch'] : ['language', 'german', 'english'], + }, + { + id: 'faq-feedback', + question: isDE ? 'Wie kann ich Feedback geben?' : 'How can I give feedback?', + answer: isDE + ? '

Dein Feedback hilft uns, CityCorners zu verbessern:

' + : '

Your feedback helps us improve CityCorners:

', + category: 'general', + order: 8, + language: isDE ? 'de' : 'en', + tags: isDE + ? ['feedback', 'verbesserung', 'kontakt'] + : ['feedback', 'improvement', 'contact'], + }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Daten', dataTypeEN: 'data' }), + ], + features: [ + { + id: 'feature-explore', + title: isDE ? 'Orte entdecken' : 'Explore Places', + description: isDE + ? 'Entdecke Sehenswürdigkeiten, Restaurants, Cafés und mehr in Konstanz am Bodensee' + : 'Discover sights, restaurants, cafés, and more in Konstanz at Lake Constance', + icon: '🏛️', + category: 'core', + highlights: isDE + ? ['11 Kategorien', 'Detailseiten mit Timeline', 'Farbcodierte Karten', 'Suche'] + : ['11 categories', 'Detail pages with timeline', 'Color-coded cards', 'Search'], + content: '', + order: 1, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-map', + title: isDE ? 'Interaktive Karte' : 'Interactive Map', + description: isDE + ? 'Leaflet-basierte Karte mit farbcodierten Markern für alle Kategorien' + : 'Leaflet-based map with color-coded markers for all categories', + icon: '🗺️', + category: 'core', + highlights: isDE + ? ['Farbcodierte Marker', 'Kategorie-Filter', 'Detailansicht per Klick', 'Mini-Karte'] + : ['Color-coded markers', 'Category filters', 'Detail view on click', 'Mini-map'], + content: '', + order: 2, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-favorites', + title: isDE ? 'Favoriten' : 'Favorites', + description: isDE + ? 'Speichere deine Lieblingsorte und finde sie schnell wieder' + : 'Save your favorite places and find them quickly', + icon: '❤️', + category: 'core', + highlights: isDE + ? ['Herz-Button', 'Optimistische Updates', 'Dedizierte Ansicht'] + : ['Heart button', 'Optimistic updates', 'Dedicated view'], + content: '', + order: 3, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-add', + title: isDE ? 'Orte hinzufügen' : 'Add Locations', + description: isDE + ? 'Trage neue Orte bei — mit Web-Lookup für automatische Informationsextraktion' + : 'Contribute new locations — with web lookup for automatic information extraction', + icon: '➕', + category: 'core', + highlights: isDE + ? ['Web-Lookup', 'Auto-Extraktion', '11 Kategorien', 'Bearbeiten'] + : ['Web lookup', 'Auto extraction', '11 categories', 'Editing'], + content: '', + order: 4, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-timeline', + title: 'Timeline', + description: isDE + ? 'Historische Ereignisse und Meilensteine für jeden Ort' + : 'Historical events and milestones for every location', + icon: '📅', + category: 'advanced', + highlights: isDE + ? ['Historische Ereignisse', 'Gründungsjahre', 'Meilensteine'] + : ['Historical events', 'Founding years', 'Milestones'], + content: '', + order: 5, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-i18n', + title: isDE ? 'Mehrsprachig' : 'Multilingual', + description: isDE + ? 'Verfügbar in Deutsch und Englisch mit einfachem Sprachumschalter' + : 'Available in German and English with easy language switcher', + icon: '🌍', + category: 'core', + highlights: isDE + ? ['Deutsch & Englisch', 'Sofort umschaltbar', 'Lokal gespeichert'] + : ['German & English', 'Instant switching', 'Locally saved'], + content: '', + order: 6, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-pwa', + title: isDE ? 'Progressive Web App' : 'Progressive Web App', + description: isDE + ? 'Installierbar auf jedem Gerät — mit Offline-Fallback und Service Worker' + : 'Installable on any device — with offline fallback and service worker', + icon: '📱', + category: 'advanced', + highlights: isDE + ? ['Installierbar', 'Offline-Fallback', 'Service Worker Caching'] + : ['Installable', 'Offline fallback', 'Service worker caching'], + content: '', + order: 7, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-settings', + title: isDE ? 'Einstellungen' : 'Settings', + description: isDE + ? 'Passe CityCorners an — Theme-Modus, Theme-Variante und Kontoeinstellungen' + : 'Customize CityCorners — theme mode, theme variant, and account settings', + icon: '⚙️', + category: 'core', + highlights: isDE + ? ['Hell/Dunkel-Modus', 'Theme-Varianten', 'Kontoeinstellungen'] + : ['Light/Dark mode', 'Theme variants', 'Account settings'], + content: '', + order: 8, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-reviews', + title: isDE ? 'Bewertungen' : 'Reviews', + description: isDE + ? 'Bewerte Orte und lies Bewertungen anderer Nutzer' + : 'Rate locations and read reviews from other users', + icon: '⭐', + category: 'core', + highlights: isDE + ? ['Sternebewertung', 'Textbewertungen', 'Community-Feedback'] + : ['Star ratings', 'Text reviews', 'Community feedback'], + content: '', + order: 9, + language: isDE ? 'de' : 'en', + }, + ], + shortcuts: [], + gettingStarted: [], + changelog: [], + contact: { + id: 'contact-support', + title: isDE ? 'Support kontaktieren' : 'Contact Support', + content: isDE + ? '

Unser Support-Team hilft dir bei allen Fragen rund um CityCorners. Nutze auch die Feedback-Funktion im Menü, um uns direkt Verbesserungsvorschläge oder neue Ort-Vorschläge zu schicken.

' + : '

Our support team is here to help you with any questions about CityCorners. You can also use the feedback feature in the menu to send us improvement suggestions or new location ideas directly.

', + language: isDE ? 'de' : 'en', + order: 1, + supportEmail: 'support@mana.how', + documentationUrl: 'https://mana.how/docs', + responseTime: isDE ? 'Normalerweise innerhalb von 24 Stunden' : 'Usually within 24 hours', + }, + }; +} diff --git a/apps/clock/apps/web/src/lib/content/help/index.test.ts b/apps/clock/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..a1e31e35e --- /dev/null +++ b/apps/clock/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getClockHelpContent } from './index'; + +describe('Clock Help Content', () => { + it('returns valid German content', () => { + const content = getClockHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getClockHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getClockHelpContent('de'); + const en = getClockHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getClockHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/clock/apps/web/src/lib/content/help/index.ts b/apps/clock/apps/web/src/lib/content/help/index.ts index 744b5fdcf..a0966816e 100644 --- a/apps/clock/apps/web/src/lib/content/help/index.ts +++ b/apps/clock/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getClockHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -59,18 +60,16 @@ export function getClockHelpContent(locale: string): HelpContent { ? ['life-clock', 'lebenszeit', 'visualisierung'] : ['life-clock', 'lifetime', 'visualization'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Daten sind sicher:

' - : '

Your data is secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { + dataTypeDE: 'Daten', + dataTypeEN: 'data', + extraBulletsDE: [ + 'Lokale Speicherung: Wecker und Timer werden lokal auf deinem Gerät gespeichert', + ], + extraBulletsEN: [ + 'Local storage: Alarms and timers are stored locally on your device', + ], + }), ], features: [ { diff --git a/apps/contacts/apps/web/src/lib/content/help/index.test.ts b/apps/contacts/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..47af5635a --- /dev/null +++ b/apps/contacts/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getContactsHelpContent } from './index'; + +describe('Contacts Help Content', () => { + it('returns valid German content', () => { + const content = getContactsHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getContactsHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getContactsHelpContent('de'); + const en = getContactsHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getContactsHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/contacts/apps/web/src/lib/content/help/index.ts b/apps/contacts/apps/web/src/lib/content/help/index.ts index 0e195a463..ae2d436ca 100644 --- a/apps/contacts/apps/web/src/lib/content/help/index.ts +++ b/apps/contacts/apps/web/src/lib/content/help/index.ts @@ -4,6 +4,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getContactsHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -56,18 +57,7 @@ export function getContactsHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['abo', 'kündigung', 'abrechnung'] : ['subscription', 'cancel', 'billing'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Wir nehmen deinen Datenschutz ernst:

' - : '

We take your privacy seriously:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Kontakte', dataTypeEN: 'contacts' }), ], features: [ { diff --git a/apps/context/apps/web/src/lib/content/help/index.test.ts b/apps/context/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..bce7c2eac --- /dev/null +++ b/apps/context/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getContextHelpContent } from './index'; + +describe('Context Help Content', () => { + it('returns valid German content', () => { + const content = getContextHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getContextHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getContextHelpContent('de'); + const en = getContextHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getContextHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/context/apps/web/src/lib/content/help/index.ts b/apps/context/apps/web/src/lib/content/help/index.ts index 32bc9f7a5..1f5473fc2 100644 --- a/apps/context/apps/web/src/lib/content/help/index.ts +++ b/apps/context/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getContextHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -59,18 +60,7 @@ export function getContextHelpContent(locale: string): HelpContent { ? ['versionierung', 'historie', 'wiederherstellen'] : ['versioning', 'history', 'restore'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Daten sind sicher:

' - : '

Your data is secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Dokumente', dataTypeEN: 'documents' }), ], features: [ { diff --git a/apps/manacore/apps/web/src/lib/content/help/index.test.ts b/apps/manacore/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..80c9ac42d --- /dev/null +++ b/apps/manacore/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getManaCoreHelpContent } from './index'; + +describe('ManaCore Help Content', () => { + it('returns valid German content', () => { + const content = getManaCoreHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getManaCoreHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getManaCoreHelpContent('de'); + const en = getManaCoreHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getManaCoreHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/manacore/apps/web/src/lib/content/help/index.ts b/apps/manacore/apps/web/src/lib/content/help/index.ts index a4e65b8ef..eb312807f 100644 --- a/apps/manacore/apps/web/src/lib/content/help/index.ts +++ b/apps/manacore/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getManaCoreHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -59,18 +60,7 @@ export function getManaCoreHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['wechseln', 'apps', 'navigation'] : ['switch', 'apps', 'navigation'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Daten sind sicher:

' - : '

Your data is secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Daten', dataTypeEN: 'data' }), ], features: [ { diff --git a/apps/manadeck/apps/web/src/lib/content/help/index.test.ts b/apps/manadeck/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..0220ff0b5 --- /dev/null +++ b/apps/manadeck/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getManaDeckHelpContent } from './index'; + +describe('ManaDeck Help Content', () => { + it('returns valid German content', () => { + const content = getManaDeckHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getManaDeckHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getManaDeckHelpContent('de'); + const en = getManaDeckHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getManaDeckHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/manadeck/apps/web/src/lib/content/help/index.ts b/apps/manadeck/apps/web/src/lib/content/help/index.ts index c91bdd772..be41d8132 100644 --- a/apps/manadeck/apps/web/src/lib/content/help/index.ts +++ b/apps/manadeck/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getManaDeckHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -57,18 +58,7 @@ export function getManaDeckHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['import', 'export', 'csv', 'anki'] : ['import', 'export', 'csv', 'anki'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Daten sind sicher:

' - : '

Your data is secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Karten', dataTypeEN: 'cards' }), ], features: [ { diff --git a/apps/matrix/apps/web/src/lib/content/help/index.test.ts b/apps/matrix/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..dd2e0830d --- /dev/null +++ b/apps/matrix/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getManalinkHelpContent } from './index'; + +describe('Manalink Help Content', () => { + it('returns valid German content', () => { + const content = getManalinkHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getManalinkHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getManalinkHelpContent('de'); + const en = getManalinkHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getManalinkHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/matrix/apps/web/src/lib/content/help/index.ts b/apps/matrix/apps/web/src/lib/content/help/index.ts new file mode 100644 index 000000000..bb7b67763 --- /dev/null +++ b/apps/matrix/apps/web/src/lib/content/help/index.ts @@ -0,0 +1,276 @@ +/** + * Help content for Manalink (Matrix) app + */ + +import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; + +export function getManalinkHelpContent(locale: string): HelpContent { + const isDE = locale === 'de'; + + return { + faq: [ + { + id: 'faq-what-is-matrix', + question: isDE ? 'Was ist Matrix und Manalink?' : 'What is Matrix and Manalink?', + answer: isDE + ? '

Manalink ist ein sicherer Messenger auf Basis des Matrix-Protokolls — einem dezentralen, offenen Standard für Kommunikation:

' + : '

Manalink is a secure messenger based on the Matrix protocol — a decentralized, open standard for communication:

', + category: 'features', + order: 1, + language: isDE ? 'de' : 'en', + tags: isDE ? ['matrix', 'protokoll', 'dezentral'] : ['matrix', 'protocol', 'decentralized'], + }, + { + id: 'faq-login', + question: isDE ? 'Wie melde ich mich an?' : 'How do I log in?', + answer: isDE + ? '

Du kannst dich auf zwei Wegen anmelden:

Der Standard-Homeserver ist matrix.mana.how, aber du kannst jeden beliebigen Matrix-Homeserver verwenden.

' + : '

You can log in two ways:

The default homeserver is matrix.mana.how, but you can use any Matrix homeserver.

', + category: 'getting-started', + order: 2, + language: isDE ? 'de' : 'en', + tags: isDE ? ['anmeldung', 'login', 'sso'] : ['login', 'auth', 'sso'], + }, + { + id: 'faq-rooms', + question: isDE ? 'Wie funktionieren Räume?' : 'How do rooms work?', + answer: isDE + ? '

Räume sind Chatgruppen in Matrix:

' + : '

Rooms are chat groups in Matrix:

', + category: 'features', + order: 3, + language: isDE ? 'de' : 'en', + tags: isDE ? ['räume', 'chat', 'gruppen'] : ['rooms', 'chat', 'groups'], + }, + { + id: 'faq-messaging', + question: isDE + ? 'Welche Nachrichtenfunktionen gibt es?' + : 'What messaging features are available?', + answer: isDE + ? '

Manalink bietet umfangreiche Nachrichtenfunktionen:

' + : '

Manalink offers comprehensive messaging features:

', + category: 'features', + order: 4, + language: isDE ? 'de' : 'en', + tags: isDE + ? ['nachrichten', 'tippen', 'lesen', 'suche'] + : ['messages', 'typing', 'read', 'search'], + }, + { + id: 'faq-encryption', + question: isDE ? 'Sind meine Nachrichten verschlüsselt?' : 'Are my messages encrypted?', + answer: isDE + ? '

Ja, Manalink unterstützt Ende-zu-Ende-Verschlüsselung (E2EE) über das Matrix-Protokoll:

' + : '

Yes, Manalink supports end-to-end encryption (E2EE) via the Matrix protocol:

', + category: 'privacy', + order: 5, + language: isDE ? 'de' : 'en', + tags: isDE ? ['verschlüsselung', 'e2ee', 'sicherheit'] : ['encryption', 'e2ee', 'security'], + }, + { + id: 'faq-room-settings', + question: isDE ? 'Wie verwalte ich Raumeinstellungen?' : 'How do I manage room settings?', + answer: isDE + ? '

Jeder Raum hat eigene Einstellungen:

' + : '

Each room has its own settings:

', + category: 'features', + order: 6, + language: isDE ? 'de' : 'en', + tags: isDE ? ['einstellungen', 'raum', 'mitglieder'] : ['settings', 'room', 'members'], + }, + { + id: 'faq-bots', + question: isDE ? 'Was sind Bots?' : 'What are bots?', + answer: isDE + ? '

Unter Bots findest du automatisierte Assistenten, die in Räumen helfen können. Bots können Aufgaben automatisieren, Informationen bereitstellen und den Chat bereichern.

' + : '

Under Bots you can find automated assistants that can help in rooms. Bots can automate tasks, provide information, and enrich the chat experience.

', + category: 'features', + order: 7, + language: isDE ? 'de' : 'en', + tags: isDE + ? ['bots', 'automatisierung', 'assistenten'] + : ['bots', 'automation', 'assistants'], + }, + { + id: 'faq-pwa', + question: isDE + ? 'Kann ich Manalink auf dem Handy nutzen?' + : 'Can I use Manalink on my phone?', + answer: isDE + ? '

Ja! Manalink ist eine Progressive Web App (PWA):

' + : '

Yes! Manalink is a Progressive Web App (PWA):

', + category: 'getting-started', + order: 8, + language: isDE ? 'de' : 'en', + tags: isDE ? ['pwa', 'mobil', 'installieren'] : ['pwa', 'mobile', 'install'], + }, + { + id: 'faq-feedback', + question: isDE ? 'Wie kann ich Feedback geben?' : 'How can I give feedback?', + answer: isDE + ? '

Dein Feedback hilft uns, Manalink zu verbessern:

' + : '

Your feedback helps us improve Manalink:

', + category: 'general', + order: 9, + language: isDE ? 'de' : 'en', + tags: isDE + ? ['feedback', 'verbesserung', 'kontakt'] + : ['feedback', 'improvement', 'contact'], + }, + ...getPrivacyFAQs(locale, { + dataTypeDE: 'Nachrichten', + dataTypeEN: 'messages', + extraBulletsDE: [ + 'Ende-zu-Ende-Verschlüsselung: Verschlüsselte Räume sind nur für Teilnehmer lesbar — nicht einmal der Server kann mitlesen', + ], + extraBulletsEN: [ + 'End-to-end encryption: Encrypted rooms are only readable by participants — not even the server can read them', + ], + }), + ], + features: [ + { + id: 'feature-messaging', + title: isDE ? 'Sichere Nachrichten' : 'Secure Messaging', + description: isDE + ? 'Ende-zu-Ende-verschlüsselte Nachrichten über das dezentrale Matrix-Protokoll' + : 'End-to-end encrypted messaging via the decentralized Matrix protocol', + icon: '🔒', + category: 'core', + highlights: isDE + ? [ + 'E2E-Verschlüsselung', + 'Direktnachrichten & Gruppen', + 'Lesebestätigungen', + 'Tipp-Indikatoren', + ] + : ['E2E encryption', 'Direct messages & groups', 'Read receipts', 'Typing indicators'], + content: '', + order: 1, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-rooms', + title: isDE ? 'Räume' : 'Rooms', + description: isDE + ? 'Erstelle und verwalte Räume für Direktnachrichten und Gruppen' + : 'Create and manage rooms for direct messages and groups', + icon: '💬', + category: 'core', + highlights: isDE + ? ['Raum erstellen', 'Raumeinstellungen', 'Mitgliederverwaltung', 'Ungelesen-Zähler'] + : ['Create rooms', 'Room settings', 'Member management', 'Unread counter'], + content: '', + order: 2, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-search', + title: isDE ? 'Nachrichtensuche' : 'Message Search', + description: isDE + ? 'Durchsuche den Chatverlauf nach Nachrichten und Inhalten' + : 'Search through chat history for messages and content', + icon: '🔍', + category: 'core', + highlights: isDE + ? ['Volltextsuche', 'Raum-übergreifend', 'Schnelle Ergebnisse'] + : ['Full-text search', 'Cross-room', 'Quick results'], + content: '', + order: 3, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-sso', + title: isDE ? 'SSO-Anmeldung' : 'SSO Login', + description: isDE + ? 'Melde dich mit deinem ManaCore-Konto an — kein separates Matrix-Passwort nötig' + : 'Sign in with your ManaCore account — no separate Matrix password needed', + icon: '🔐', + category: 'core', + highlights: isDE + ? ['ManaCore SSO', 'Ein-Klick-Login', 'Sicher', 'Passwort-Login'] + : ['ManaCore SSO', 'One-click login', 'Secure', 'Password login'], + content: '', + order: 4, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-bots', + title: 'Bots', + description: isDE + ? 'Automatisierte Assistenten für Aufgaben und Informationen in Räumen' + : 'Automated assistants for tasks and information in rooms', + icon: '🤖', + category: 'advanced', + highlights: isDE + ? ['Chat-Assistenten', 'Automatisierung', 'Informationsdienste'] + : ['Chat assistants', 'Automation', 'Information services'], + content: '', + order: 5, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-pwa', + title: isDE ? 'Progressive Web App' : 'Progressive Web App', + description: isDE + ? 'Installierbar auf jedem Gerät — offline-fähig und mit Push-Benachrichtigungen' + : 'Installable on any device — works offline and supports push notifications', + icon: '📱', + category: 'core', + highlights: isDE + ? ['Installierbar', 'Offline-fähig', 'Push-Benachrichtigungen', 'Vollbild'] + : ['Installable', 'Works offline', 'Push notifications', 'Fullscreen'], + content: '', + order: 6, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-federation', + title: isDE ? 'Föderation' : 'Federation', + description: isDE + ? 'Kommuniziere mit Nutzern auf anderen Matrix-Homeservern' + : 'Communicate with users on other Matrix homeservers', + icon: '🌐', + category: 'advanced', + highlights: isDE + ? ['Server-übergreifend', 'Offenes Protokoll', 'Element-kompatibel'] + : ['Cross-server', 'Open protocol', 'Element-compatible'], + content: '', + order: 7, + language: isDE ? 'de' : 'en', + }, + { + id: 'feature-settings', + title: isDE ? 'Einstellungen' : 'Settings', + description: isDE + ? 'Passe Manalink an — Themes, Benachrichtigungen und Kontoeinstellungen' + : 'Customize Manalink — themes, notifications, and account settings', + icon: '⚙️', + category: 'core', + highlights: isDE + ? ['Hell/Dunkel-Modus', 'Benachrichtigungen', 'Kontoeinstellungen'] + : ['Light/Dark mode', 'Notifications', 'Account settings'], + content: '', + order: 8, + language: isDE ? 'de' : 'en', + }, + ], + shortcuts: [], + gettingStarted: [], + changelog: [], + contact: { + id: 'contact-support', + title: isDE ? 'Support kontaktieren' : 'Contact Support', + content: isDE + ? '

Unser Support-Team hilft dir bei allen Fragen rund um Manalink. Nutze auch die Feedback-Funktion im Menü, um uns direkt Verbesserungsvorschläge zu schicken.

' + : '

Our support team is here to help you with any questions about Manalink. You can also use the feedback feature in the menu to send us improvement suggestions directly.

', + language: isDE ? 'de' : 'en', + order: 1, + supportEmail: 'support@mana.how', + documentationUrl: 'https://mana.how/docs', + responseTime: isDE ? 'Normalerweise innerhalb von 24 Stunden' : 'Usually within 24 hours', + }, + }; +} diff --git a/apps/mukke/apps/web/src/lib/content/help/index.test.ts b/apps/mukke/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..15629599a --- /dev/null +++ b/apps/mukke/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getMukkeHelpContent } from './index'; + +describe('Mukke Help Content', () => { + it('returns valid German content', () => { + const content = getMukkeHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getMukkeHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getMukkeHelpContent('de'); + const en = getMukkeHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getMukkeHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/mukke/apps/web/src/lib/content/help/index.ts b/apps/mukke/apps/web/src/lib/content/help/index.ts index 31731ca79..683295149 100644 --- a/apps/mukke/apps/web/src/lib/content/help/index.ts +++ b/apps/mukke/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getMukkeHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -47,18 +48,7 @@ export function getMukkeHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['playlist', 'erstellen', 'sortieren'] : ['playlist', 'create', 'sort'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Musik ist sicher:

' - : '

Your music is secure:

', - category: 'privacy', - order: 4, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Musik', dataTypeEN: 'music' }), ], features: [ { diff --git a/apps/photos/apps/web/src/lib/content/help/index.test.ts b/apps/photos/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..cd0cffc8b --- /dev/null +++ b/apps/photos/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getPhotosHelpContent } from './index'; + +describe('Photos Help Content', () => { + it('returns valid German content', () => { + const content = getPhotosHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getPhotosHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getPhotosHelpContent('de'); + const en = getPhotosHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getPhotosHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/photos/apps/web/src/lib/content/help/index.ts b/apps/photos/apps/web/src/lib/content/help/index.ts index 7ee1a5a86..7520b8e12 100644 --- a/apps/photos/apps/web/src/lib/content/help/index.ts +++ b/apps/photos/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getPhotosHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -53,17 +54,7 @@ export function getPhotosHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: ['favorites', 'heart', 'bookmarks'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Fotos geschützt?' : 'How are my photos protected?', - answer: isDE - ? '

Deine Fotos sind sicher bei ManaCore:

' - : '

Your photos are safe with ManaCore:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - tags: ['privacy', 'security', 'data'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Fotos', dataTypeEN: 'photos' }), ], features: [ { diff --git a/apps/picture/apps/web/src/lib/content/help/index.test.ts b/apps/picture/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..9e85eac22 --- /dev/null +++ b/apps/picture/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getPictureHelpContent } from './index'; + +describe('Picture Help Content', () => { + it('returns valid German content', () => { + const content = getPictureHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getPictureHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getPictureHelpContent('de'); + const en = getPictureHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getPictureHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/picture/apps/web/src/lib/content/help/index.ts b/apps/picture/apps/web/src/lib/content/help/index.ts index eba973558..5485ffa0f 100644 --- a/apps/picture/apps/web/src/lib/content/help/index.ts +++ b/apps/picture/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getPictureHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -60,18 +61,7 @@ export function getPictureHelpContent(locale: string): HelpContent { ? ['explore', 'entdecken', 'inspiration'] : ['explore', 'discover', 'inspiration'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Bilder geschützt?' : 'How are my images protected?', - answer: isDE - ? '

Deine Bilder sind sicher:

' - : '

Your images are secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'sicherheit', 'dsgvo'] : ['privacy', 'security', 'gdpr'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Bilder', dataTypeEN: 'images' }), ], features: [ { diff --git a/apps/planta/apps/web/src/lib/content/help/index.test.ts b/apps/planta/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..98945d161 --- /dev/null +++ b/apps/planta/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getPlantaHelpContent } from './index'; + +describe('Planta Help Content', () => { + it('returns valid German content', () => { + const content = getPlantaHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getPlantaHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getPlantaHelpContent('de'); + const en = getPlantaHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getPlantaHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/planta/apps/web/src/lib/content/help/index.ts b/apps/planta/apps/web/src/lib/content/help/index.ts index 1946de89f..40ddff67b 100644 --- a/apps/planta/apps/web/src/lib/content/help/index.ts +++ b/apps/planta/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getPlantaHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -57,17 +58,7 @@ export function getPlantaHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: ['health', 'diseases', 'pests', 'monitoring'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Pflanzendaten sind sicher bei Planta:

' - : '

Your plant data is safe with Planta:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - tags: ['privacy', 'security', 'data', 'photos'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Pflanzendaten', dataTypeEN: 'plant data' }), ], features: [ { diff --git a/apps/presi/apps/web/src/lib/content/help/index.test.ts b/apps/presi/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..745979ace --- /dev/null +++ b/apps/presi/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getPresiHelpContent } from './index'; + +describe('Presi Help Content', () => { + it('returns valid German content', () => { + const content = getPresiHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getPresiHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getPresiHelpContent('de'); + const en = getPresiHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getPresiHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/presi/apps/web/src/lib/content/help/index.ts b/apps/presi/apps/web/src/lib/content/help/index.ts index a861167b5..0b46f318f 100644 --- a/apps/presi/apps/web/src/lib/content/help/index.ts +++ b/apps/presi/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getPresiHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -53,17 +54,7 @@ export function getPresiHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: ['themes', 'design', 'customization', 'styles'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Präsentationen sind sicher bei Presi:

' - : '

Your presentations are safe with Presi:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - tags: ['privacy', 'security', 'data', 'sharing'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Präsentationen', dataTypeEN: 'presentations' }), ], features: [ { diff --git a/apps/questions/apps/web/src/lib/content/help/index.test.ts b/apps/questions/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..3662cd991 --- /dev/null +++ b/apps/questions/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getQuestionsHelpContent } from './index'; + +describe('Questions Help Content', () => { + it('returns valid German content', () => { + const content = getQuestionsHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getQuestionsHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getQuestionsHelpContent('de'); + const en = getQuestionsHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getQuestionsHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/questions/apps/web/src/lib/content/help/index.ts b/apps/questions/apps/web/src/lib/content/help/index.ts index 3c34b42f3..dd7d4eb0c 100644 --- a/apps/questions/apps/web/src/lib/content/help/index.ts +++ b/apps/questions/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getQuestionsHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -53,17 +54,7 @@ export function getQuestionsHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: ['collections', 'organize', 'save', 'share'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Recherchen sind privat und sicher:

' - : '

Your research is private and secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - tags: ['privacy', 'security', 'data', 'history'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Fragen', dataTypeEN: 'questions' }), ], features: [ { diff --git a/apps/storage/apps/web/src/lib/content/help/index.test.ts b/apps/storage/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..bfe17bfad --- /dev/null +++ b/apps/storage/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getStorageHelpContent } from './index'; + +describe('Storage Help Content', () => { + it('returns valid German content', () => { + const content = getStorageHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getStorageHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getStorageHelpContent('de'); + const en = getStorageHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getStorageHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/storage/apps/web/src/lib/content/help/index.ts b/apps/storage/apps/web/src/lib/content/help/index.ts index 162d8fa5f..53d8dbd09 100644 --- a/apps/storage/apps/web/src/lib/content/help/index.ts +++ b/apps/storage/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getStorageHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -53,18 +54,16 @@ export function getStorageHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['papierkorb', 'löschen', 'wiederherstellen'] : ['trash', 'delete', 'restore'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Dateien geschützt?' : 'How are my files protected?', - answer: isDE - ? '

Deine Dateien sind sicher:

' - : '

Your files are secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'sicherheit', 'dsgvo'] : ['privacy', 'security', 'gdpr'], - }, + ...getPrivacyFAQs(locale, { + dataTypeDE: 'Dateien', + dataTypeEN: 'files', + extraBulletsDE: [ + 'S3-Storage: Dateien werden sicher in S3-kompatiblem Speicher (MinIO) abgelegt', + ], + extraBulletsEN: [ + 'S3 storage: Files are securely stored in S3-compatible storage (MinIO)', + ], + }), ], features: [ { diff --git a/apps/todo/apps/web/src/lib/content/help/index.test.ts b/apps/todo/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..9f8fd03d8 --- /dev/null +++ b/apps/todo/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getTodoHelpContent } from './index'; + +describe('Todo Help Content', () => { + it('returns valid German content', () => { + const content = getTodoHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getTodoHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getTodoHelpContent('de'); + const en = getTodoHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getTodoHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/todo/apps/web/src/lib/content/help/index.ts b/apps/todo/apps/web/src/lib/content/help/index.ts index 65b3a2fa6..e0f1e7186 100644 --- a/apps/todo/apps/web/src/lib/content/help/index.ts +++ b/apps/todo/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getTodoHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -60,18 +61,7 @@ export function getTodoHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['wiederkehrend', 'wiederholung', 'serie'] : ['recurring', 'repeat', 'series'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Daten sind sicher:

' - : '

Your data is secure:

', - category: 'privacy', - order: 5, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Aufgaben', dataTypeEN: 'tasks' }), ], features: [ { diff --git a/apps/zitare/apps/web/src/lib/content/help/index.test.ts b/apps/zitare/apps/web/src/lib/content/help/index.test.ts new file mode 100644 index 000000000..953491211 --- /dev/null +++ b/apps/zitare/apps/web/src/lib/content/help/index.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect } from 'vitest'; +import { getZitareHelpContent } from './index'; + +describe('Zitare Help Content', () => { + it('returns valid German content', () => { + const content = getZitareHelpContent('de'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + expect(content.contact.supportEmail).toBe('support@mana.how'); + }); + + it('returns valid English content', () => { + const content = getZitareHelpContent('en'); + + expect(content.faq.length).toBeGreaterThan(0); + content.faq.forEach((faq) => { + expect(faq.id).toBeTruthy(); + expect(faq.question).toBeTruthy(); + expect(faq.answer).toBeTruthy(); + }); + + expect(content.features).toBeDefined(); + expect(content.contact).toBeDefined(); + }); + + it('returns same number of FAQ items for both languages', () => { + const de = getZitareHelpContent('de'); + const en = getZitareHelpContent('en'); + + expect(de.faq.length).toBe(en.faq.length); + expect(de.features.length).toBe(en.features.length); + }); + + it('has unique FAQ IDs', () => { + const content = getZitareHelpContent('de'); + const ids = content.faq.map((f) => f.id); + expect(new Set(ids).size).toBe(ids.length); + }); +}); diff --git a/apps/zitare/apps/web/src/lib/content/help/index.ts b/apps/zitare/apps/web/src/lib/content/help/index.ts index 1c7b264eb..83655ca17 100644 --- a/apps/zitare/apps/web/src/lib/content/help/index.ts +++ b/apps/zitare/apps/web/src/lib/content/help/index.ts @@ -3,6 +3,7 @@ */ import type { HelpContent } from '@manacore/shared-help-types'; +import { getPrivacyFAQs } from '@manacore/shared-help-types'; export function getZitareHelpContent(locale: string): HelpContent { const isDE = locale === 'de'; @@ -42,18 +43,7 @@ export function getZitareHelpContent(locale: string): HelpContent { language: isDE ? 'de' : 'en', tags: isDE ? ['listen', 'sammlung', 'organisieren'] : ['lists', 'collection', 'organize'], }, - { - id: 'faq-privacy', - question: isDE ? 'Wie werden meine Daten geschützt?' : 'How is my data protected?', - answer: isDE - ? '

Deine Daten sind sicher:

' - : '

Your data is secure:

', - category: 'privacy', - order: 4, - language: isDE ? 'de' : 'en', - featured: true, - tags: isDE ? ['datenschutz', 'dsgvo', 'sicherheit'] : ['privacy', 'gdpr', 'security'], - }, + ...getPrivacyFAQs(locale, { dataTypeDE: 'Zitate', dataTypeEN: 'quotes' }), ], features: [ {