Conversation
…ponent - Removed initialCards prop and integrated tutorialSlug for system card retrieval - Updated useOnboardingCards hook to support tutorial slug normalization and added new tutorial card definitions - Adjusted ProjectPortalEditor to ensure consistent tutorial slug handling - translations
WalkthroughThis PR refactors the participant onboarding tutorial system from initialCards-driven to tutorialSlug-driven architecture. Updates Directus sync operations, adds slug-based configuration (skip-consent, none, basic, advanced), removes initialCards component prop, and propagates corresponding UI label and translation updates across five locales. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Areas requiring extra attention:
Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
echo/frontend/src/components/participant/ParticipantOnboardingCards.tsx (2)
2-2: Nuke this IDE artifact."Start of Selection" is classic IDE cruft - ship it clean.
-// Start of Selection
220-223: Fallback message isn't localized.Edge case guard is good, but hardcoded English breaks i18n for the ~0.01% of users who hit this. Worth a quick
<Trans>wrap.// If there's no valid card, render a fallback if (!currentCard) { - return <div>No card available for the current language and section.</div>; + return ( + <div className="flex h-full items-center justify-center p-4"> + <Trans>No card available for the current language and section.</Trans> + </div> + ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
echo/directus/sync/collections/operations.json(12 hunks)echo/directus/sync/snapshot/fields/project/default_conversation_tutorial_slug.json(1 hunks)echo/frontend/src/components/participant/ParticipantOnboardingCards.tsx(6 hunks)echo/frontend/src/components/participant/hooks/useOnboardingCards.ts(1 hunks)echo/frontend/src/components/project/ProjectPortalEditor.tsx(3 hunks)echo/frontend/src/locales/de-DE.po(42 hunks)echo/frontend/src/locales/en-US.po(43 hunks)echo/frontend/src/locales/es-ES.po(42 hunks)echo/frontend/src/locales/fr-FR.po(42 hunks)echo/frontend/src/locales/nl-NL.po(43 hunks)echo/frontend/src/routes/participant/ParticipantStart.tsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
echo/frontend/**/src/routes/**/*.tsx
📄 CodeRabbit inference engine (echo/frontend/AGENTS.md)
Use Lingui macros
tfrom@lingui/core/macroandTransfrom@lingui/react/macrofor localizing UI strings in routed screens
Files:
echo/frontend/src/routes/participant/ParticipantStart.tsx
echo/frontend/**/src/{routes,components}/**/*.tsx
📄 CodeRabbit inference engine (echo/frontend/AGENTS.md)
echo/frontend/**/src/{routes,components}/**/*.tsx: Compose Mantine primitives (Stack,Group,ActionIcon, etc.) while layering Tailwind utility classes viaclassName, alongside toast feedback via@/components/common/Toaster
For Tailwind classes that need dynamic values, replace with inlinestyleprops using CSS variables instead of hardcoded hex values
Files:
echo/frontend/src/routes/participant/ParticipantStart.tsxecho/frontend/src/components/participant/ParticipantOnboardingCards.tsxecho/frontend/src/components/project/ProjectPortalEditor.tsx
echo/frontend/**/src/**/*.{css,tsx,ts}
📄 CodeRabbit inference engine (echo/frontend/AGENTS.md)
Use CSS variables
var(--app-background)andvar(--app-text)instead of hardcoded colors like#F6F4F1or#2D2D2Cto ensure theme changes propagate
Files:
echo/frontend/src/routes/participant/ParticipantStart.tsxecho/frontend/src/components/participant/hooks/useOnboardingCards.tsecho/frontend/src/components/participant/ParticipantOnboardingCards.tsxecho/frontend/src/components/project/ProjectPortalEditor.tsx
echo/frontend/**/src/{theme.tsx,components,routes}/**/*.tsx
📄 CodeRabbit inference engine (echo/frontend/AGENTS.md)
When adding new Mantine components, reference the global Mantine theme in
src/theme.tsxwith customprimarycolor palette and component defaults, preferring CSS variables in styles over hardcoded hex values
Files:
echo/frontend/src/routes/participant/ParticipantStart.tsxecho/frontend/src/components/participant/ParticipantOnboardingCards.tsxecho/frontend/src/components/project/ProjectPortalEditor.tsx
echo/frontend/**/src/{components,routes}/**/*.tsx
📄 CodeRabbit inference engine (echo/frontend/AGENTS.md)
UI mutations should surface inline feedback: pair toasts with contextual Mantine
Alertcomponents inside modals/forms for errors or warnings
Files:
echo/frontend/src/routes/participant/ParticipantStart.tsxecho/frontend/src/components/participant/ParticipantOnboardingCards.tsxecho/frontend/src/components/project/ProjectPortalEditor.tsx
🧠 Learnings (12)
📓 Common learnings
Learnt from: ussaama
Repo: Dembrane/echo PR: 205
File: echo/frontend/src/lib/query.ts:1444-1506
Timestamp: 2025-07-10T12:48:20.683Z
Learning: ussaama prefers string concatenation over template literals for simple cases where readability is clearer, even when linting tools suggest template literals. Human readability takes precedence over strict linting rules in straightforward concatenation scenarios.
📚 Learning: 2025-08-08T10:39:31.114Z
Learnt from: ussaama
Repo: Dembrane/echo PR: 259
File: echo/frontend/src/components/layout/ParticipantLayout.tsx:33-33
Timestamp: 2025-08-08T10:39:31.114Z
Learning: In echo/frontend/src/components/layout/ParticipantLayout.tsx, prefer using simple pathname.includes("start") and pathname.includes("finish") to control the settings button visibility. No need to switch to segment-based matching or add a useEffect to auto-close the modal for these routes, per ussaama’s preference in PR #259.
Applied to files:
echo/frontend/src/routes/participant/ParticipantStart.tsxecho/frontend/src/components/participant/hooks/useOnboardingCards.tsecho/frontend/src/components/participant/ParticipantOnboardingCards.tsxecho/frontend/src/locales/en-US.po
📚 Learning: 2025-12-05T00:33:33.442Z
Learnt from: CR
Repo: Dembrane/echo PR: 0
File: echo/frontend/AGENTS.md:0-0
Timestamp: 2025-12-05T00:33:33.442Z
Learning: Applies to echo/frontend/**/src/routes/**/*.tsx : Use Lingui macros `t` from `lingui/core/macro` and `Trans` from `lingui/react/macro` for localizing UI strings in routed screens
Applied to files:
echo/frontend/src/routes/participant/ParticipantStart.tsxecho/frontend/src/locales/de-DE.poecho/frontend/src/locales/es-ES.poecho/frontend/src/locales/en-US.poecho/frontend/src/locales/nl-NL.poecho/frontend/src/locales/fr-FR.po
📚 Learning: 2025-12-05T00:33:33.442Z
Learnt from: CR
Repo: Dembrane/echo PR: 0
File: echo/frontend/AGENTS.md:0-0
Timestamp: 2025-12-05T00:33:33.442Z
Learning: Applies to echo/frontend/**/src/routes/auth/**/*.tsx : Gentle login/logout flows use `useTransitionCurtain().runTransition()` before navigation—animations expect Directus session mutations to await that promise
Applied to files:
echo/frontend/src/routes/participant/ParticipantStart.tsx
📚 Learning: 2025-12-05T00:33:33.442Z
Learnt from: CR
Repo: Dembrane/echo PR: 0
File: echo/frontend/AGENTS.md:0-0
Timestamp: 2025-12-05T00:33:33.442Z
Learning: Applies to echo/frontend/**/src/routes/auth/**/*.tsx : Auth hero uses `/public/video/auth-hero.mp4` with `/public/video/auth-hero-poster.jpg` as poster; keep the bright blur overlay consistent when iterating on onboarding screens
Applied to files:
echo/frontend/src/routes/participant/ParticipantStart.tsx
📚 Learning: 2025-12-05T00:33:33.442Z
Learnt from: CR
Repo: Dembrane/echo PR: 0
File: echo/frontend/AGENTS.md:0-0
Timestamp: 2025-12-05T00:33:33.442Z
Learning: Applies to echo/frontend/**/src/routes/settings/**/*.tsx : Provide ergonomic navigation in settings-like routes: breadcrumb + back action (ActionIcon + navigate(-1)) with relevant iconography is the default
Applied to files:
echo/frontend/src/routes/participant/ParticipantStart.tsx
📚 Learning: 2025-12-05T00:33:33.442Z
Learnt from: CR
Repo: Dembrane/echo PR: 0
File: echo/frontend/AGENTS.md:0-0
Timestamp: 2025-12-05T00:33:33.442Z
Learning: Applies to echo/frontend/**/src/routes/auth/**/*.tsx : Auth surfaces reuse `HeaderView` by passing `isAuthenticated`/`loading` props—avoid rolling bespoke headers inside layouts
Applied to files:
echo/frontend/src/routes/participant/ParticipantStart.tsx
📚 Learning: 2025-07-10T12:47:06.269Z
Learnt from: ussaama
Repo: Dembrane/echo PR: 205
File: echo/frontend/src/locales/nl-NL.po:1246-1247
Timestamp: 2025-07-10T12:47:06.269Z
Learning: .po files (gettext internationalization files) are auto-generated by i18n tools like lingui/cli and should not be manually edited. The format of component paths and line numbers in these files is determined by the tool configuration, not manually written. Do not suggest manual edits to .po files.
Applied to files:
echo/frontend/src/locales/de-DE.poecho/frontend/src/locales/es-ES.poecho/frontend/src/locales/en-US.po
📚 Learning: 2025-05-30T15:38:44.413Z
Learnt from: ussaama
Repo: Dembrane/echo PR: 169
File: echo/frontend/src/components/project/ProjectPortalEditor.tsx:409-464
Timestamp: 2025-05-30T15:38:44.413Z
Learning: Badge-based selectors in ProjectPortalEditor.tsx: Keyboard navigation enhancements for accessibility are considered optional improvements rather than critical issues. The user acknowledges these suggestions but doesn't prioritize them as blockers.
Applied to files:
echo/frontend/src/locales/de-DE.poecho/frontend/src/locales/es-ES.poecho/frontend/src/locales/en-US.poecho/frontend/src/locales/nl-NL.poecho/frontend/src/locales/fr-FR.po
📚 Learning: 2025-10-28T13:47:02.926Z
Learnt from: ussaama
Repo: Dembrane/echo PR: 350
File: echo/frontend/src/components/participant/ParticipantConversationText.tsx:82-85
Timestamp: 2025-10-28T13:47:02.926Z
Learning: In text mode (echo/frontend/src/components/participant/ParticipantConversationText.tsx), participants only submit PORTAL_TEXT chunks (no audio). The “Finish” button is shown only after at least one text message is saved to Directus.
Applied to files:
echo/frontend/src/locales/de-DE.poecho/frontend/src/locales/en-US.poecho/frontend/src/locales/nl-NL.poecho/frontend/src/locales/fr-FR.po
📚 Learning: 2025-08-19T10:14:31.647Z
Learnt from: ussaama
Repo: Dembrane/echo PR: 266
File: echo/frontend/src/components/chat/ChatAccordion.tsx:214-221
Timestamp: 2025-08-19T10:14:31.647Z
Learning: In the Echo frontend codebase using Lingui, i18n IDs in Trans components (e.g., `<Trans id="any.string.here">`) can be arbitrary strings and don't need to follow specific naming conventions. They are just references used in .po files, and the actual translations need to be manually defined in the locale files.
Applied to files:
echo/frontend/src/locales/en-US.po
📚 Learning: 2025-09-16T08:34:38.109Z
Learnt from: ussaama
Repo: Dembrane/echo PR: 293
File: echo/frontend/src/components/chat/ChatTemplatesMenu.tsx:15-16
Timestamp: 2025-09-16T08:34:38.109Z
Learning: In ChatTemplatesMenu.tsx, titles are preferred over IDs for template selection logic since titles are unique one-liners and work effectively as identifiers. IDs were added primarily for better list rendering rather than selection purposes.
Applied to files:
echo/frontend/src/locales/en-US.po
🧬 Code graph analysis (1)
echo/frontend/src/components/participant/hooks/useOnboardingCards.ts (1)
echo/frontend/src/components/participant/ParticipantOnboardingCards.tsx (1)
LanguageCards(41-43)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-check-server
🔇 Additional comments (24)
echo/directus/sync/collections/operations.json (1)
17-20: No issues found with UUID reassignments.All resolve/reject references point to valid operations, no circular paths exist, and workflow graph integrity is intact. The changes are routine data migrations within the Directus sync ecosystem with proper structure.
Likely an incorrect or invalid review comment.
echo/directus/sync/snapshot/fields/project/default_conversation_tutorial_slug.json (1)
14-24: Slug choices now line up with the new tutorial flow; check legacy data once.The new
"skip-consent"option and lowercased"none"value match the frontend slug contract nicely and keep the schema default coherent. One thing to sanity‑check: if there are existing rows still storing"None"(capitalized), Directus’ dropdown won’t show a matching choice anymore—worth a small backfill to"none"to avoid “empty” selections in the admin UI.echo/frontend/src/components/project/ProjectPortalEditor.tsx (1)
529-546: Select options are correctly wired to the new slug model.The
NativeSelectdata now cleanly mirrors the four slugs (skip-consent,none,basic,advanced) with clearer copy. Values line up with the normalization logic and the Directus field config, so changing the default in one place won’t silently desync the UI.echo/frontend/src/routes/participant/ParticipantStart.tsx (1)
7-8: Route simplification to project‑only onboarding is clean; just watch the project null edge.Nice cleanup—one data source, one spinner, and a clear error surface via Mantine
Alertusing Linguitchecks all the boxes. The only assumption you’re making is thatuseParticipantProjectByIdnever hits a state whereisLoadingProject === false,projectErroris falsy, andprojectis stillundefined; in that caseproject as Projectwould blow up insideParticipantOnboardingCards. If that’s guaranteed by the hook contract, you’re good; otherwise a tiny guard before rendering the cards would harden this further.Also applies to: 26-32, 44-48
echo/frontend/src/locales/nl-NL.po (1)
535-545: PO churn here is just reference/structure updates; translations themselves are stable.All the other
~hunks around the onboarding buttons (participant.button.back,.continue,.next,"Concrete Topics","Portal Editor","Live Preview", etc.) are just updated source references and line numbers from the refactor—msgids/msgstrs remain semantically identical. No functional or UX risk here.Also applies to: 710-713, 803-807, 819-823, 895-899, 1109-1127, 1791-1793, 1865-1868, 1958-1960, 2017-2021, 2200-2203, 2479-2483, 2573-2576, 3609-3612, 3617-3620, 3627-3630
echo/frontend/src/components/participant/ParticipantOnboardingCards.tsx (2)
76-77: Clean refactor to slug-based flow.tutorialSlug derivation with
"none"fallback is clean and aligns with the new architecture. SpreadinggetSystemCards()per language is the right call.
45-45: TheProjecttype is properly available through the ambient type declarations inecho/frontend/src/lib/typesDirectus.d.ts. Since this.d.tsfile is included via thetsconfig.jsonsource directory, theProjectinterface is globally declared and requires no explicit import. The component will compile successfully.Likely an incorrect or invalid review comment.
echo/frontend/src/locales/fr-FR.po (8)
112-115: Generic{0}placeholder: adding ProjectPortalEditor ref is perfectly safeJust wiring another usage site into the same generic
{0}message; no translation risk here. Ship it.
229-236: Advanced tutorial label rename is wired + localized correctlyThe new
"Advanced (Tips and best practices)"label and retirement of the old"Tips and tricks"variant look consistent, and the FR"Avancé (Astuces et conseils)"still matches intent. The extra reference for"Advanced Settings"is just a pointer shuffle.Also applies to: 237-240
280-283: Portal load error copy is cleanNew
"An error occurred while loading the Portal..."message is clearly translated and aligned with product tone. Nothing to tighten here.
400-407: Onboarding / navigation buttons are consistent and UX‑soundNice job normalizing the onboarding CTA surface:
"Ask for Name?","Ask participants to provide their name..."→ FR copy reads naturally.- Back/Continue/Next/Ready/Skip (
Retour,Continuer,Suivant,Prêt à commencer,Passer) are consistent and match existing tone (tuform) across the participant flows."At least one topic must be selected to enable Make it concrete"→"Sélectionne au moins un sujet..."is clear and keeps the feature name as a product label.No blockers here; this is shippable UX.
Also applies to: 424-427, 481-488, 710-713, 1932-1935, 2559-2562, 3062-3065
499-506: Portal Editor + concrete feature surface area looks coherent in FRScanning this cluster:
- Tutorial mode labels (
Basic,Advanced,Brainstorm Ideas,Concrete Topics,Custom,Default,Select tutorial, instruction/tooltips) map cleanly into FR and keep the mental model intact.- Status strings around concrete topics (loading / none available) are idiomatic.
- Page scaffolding (
Page Content,Page Title,Portal Content,Portal Editor, post‑conversation page descriptions, title/portal language hints) all read like production copy, not machine leftovers.- Feature toggles (
Enable Go deeper,Enable Make it concrete,Report Notificationsand the long descriptions) are understandable and consistent with how the rest of the product talks about these capabilities.From a localization + product‑semantics standpoint this whole block is good to go.
Also applies to: 518-523, 530-533, 656-659, 857-860, 880-883, 1748-1751, 1778-1781, 1910-1913, 1983-1986, 2073-2076, 2171-2174, 2179-2182, 2435-2438, 2439-2443, 2611-2614, 2707-2710, 2919-2925, 2926-2929, 3438-3441, 3442-3445, 3446-3449, 3469-3472, 1091-1094, 1095-1098, 1099-1102, 1109-1111, 1121-1123
1006-1009: Language + mode labels are standard and predictableLocale and mode selectors are crisp:
English / French / German / Dutch / Spanish→Anglais / Français / Allemand / Néerlandais / Espagnol."Language"→"Langue","Mode"→"Mode".Exactly what a French user expects in a settings/editor surface.
Also applies to: 1137-1140, 1163-1166, 1440-1443, 1673-1675, 1862-1865, 3138-3141
2188-2191: Polish a few FR phrasings for Portal Editor copyCouple of copy cleanups to make the FR feel native‑level:
"Participant Features"
Current:Fonctionnalités participant
→ missing preposition.
"Reply Prompt"
Prompt de réponseis understandable, but you might prefer a more FR‑native"Consigne de réponse"if you want to avoid the borrowed “prompt”.
"Thank You Page Content"
Contenu de la page Mercireads like a literal label.
"This is a live preview of the participant's portal..."
Current:Cette est une vue en direct...(incorrect demonstrative; should beC'estorCeci est).
"This prompt guides how the AI responds..."
Current:Cette prompt guide...(gender mismatch;promptis treated as masculine in FR).Concrete diff proposal:
-msgid "Participant Features" -msgstr "Fonctionnalités participant" +msgid "Participant Features" +msgstr "Fonctionnalités pour les participants" @@ -msgid "Reply Prompt" -msgstr "Prompt de réponse" +msgid "Reply Prompt" +msgstr "Consigne de réponse" @@ -msgid "Thank You Page Content" -msgstr "Contenu de la page Merci" +msgid "Thank You Page Content" +msgstr "Contenu de la page de remerciement" @@ -msgid "This is a live preview of the participant's portal. You will need to refresh the page to see the latest changes." -msgstr "Cette est une vue en direct du portail du participant. Vous devrez actualiser la page pour voir les dernières modifications." +msgid "This is a live preview of the participant's portal. You will need to refresh the page to see the latest changes." +msgstr "Ceci est une vue en direct du portail du participant. Vous devrez actualiser la page pour voir les dernières modifications." @@ -msgid "This prompt guides how the AI responds to participants. Customize it to shape the type of feedback or engagement you want to encourage." -msgstr "Cette prompt guide comment l'IA répond aux participants. Personnalisez-la pour former le type de feedback ou d'engagement que vous souhaitez encourager." +msgid "This prompt guides how the AI responds to participants. Customize it to shape the type of feedback or engagement you want to encourage." +msgstr "Ce prompt guide la façon dont l'IA répond aux participants. Personnalisez-le pour définir le type de retour ou d'engagement que vous souhaitez encourager."Also applies to: 2684-2687, 3297-3300, 3420-3423, 3453-3456
⛔ Skipped due to learnings
Learnt from: ussaama Repo: Dembrane/echo PR: 169 File: echo/frontend/src/locales/fr-FR.po:1298-1301 Timestamp: 2025-05-30T15:37:46.279Z Learning: In French localization files, UI terms like "Portail" should maintain their proper capitalization according to French language conventions, even if it appears inconsistent with lowercase usage in other translations. French capitalization rules for UI terms should be respected over assumed consistency patterns.Learnt from: ussaama Repo: Dembrane/echo PR: 169 File: echo/frontend/src/locales/fr-FR.po:1880-1882 Timestamp: 2025-05-30T15:37:52.403Z Learning: In French technical contexts, especially AI/ML domains, English technical terms like "prompt" are commonly adopted and used without translation. This is acceptable and not considered an error.Learnt from: ussaama Repo: Dembrane/echo PR: 169 File: echo/frontend/src/locales/fr-FR.po:1778-1780 Timestamp: 2025-05-30T15:37:29.029Z Learning: Translation and localization suggestions should be treated as preferences rather than errors. Different phrasings in target languages can be equally valid, and users may have specific reasons for choosing particular translations.
884-887: Tighten French grammar for default/skip consent optionsTwo small FR nits worth fixing before shipping:
"Default - No tutorial (Only privacy statements)"
Current:
Par défaut - Pas de tutoriel (Seulement les déclarations de confidentialité)
- Mid‑sentence
"Seulement"should be lowercase.- Slightly smoother FR to avoid sounding literal.
"Skip data privacy slide (Host manages consent)"
Current:
Passer la carte de confidentialité (L'hôte gère la consentement)
consentementis masculine →le consentement."carte"vs"diapositive"is a bit off for “slide”.Suggested patch:
-msgid "Default - No tutorial (Only privacy statements)" -msgstr "Par défaut - Pas de tutoriel (Seulement les déclarations de confidentialité)" +msgid "Default - No tutorial (Only privacy statements)" +msgstr "Par défaut - Pas de tutoriel (seulement les déclarations de confidentialité)" @@ -msgid "Skip data privacy slide (Host manages consent)" -msgstr "Passer la carte de confidentialité (L'hôte gère la consentement)" +msgid "Skip data privacy slide (Host manages consent)" +msgstr "Passer la diapositive de confidentialité (l'hôte gère le consentement)"Also applies to: 3069-3072
⛔ Skipped due to learnings
Learnt from: ussaama Repo: Dembrane/echo PR: 169 File: echo/frontend/src/locales/fr-FR.po:1298-1301 Timestamp: 2025-05-30T15:37:46.279Z Learning: In French localization files, UI terms like "Portail" should maintain their proper capitalization according to French language conventions, even if it appears inconsistent with lowercase usage in other translations. French capitalization rules for UI terms should be respected over assumed consistency patterns.Learnt from: ussaama Repo: Dembrane/echo PR: 169 File: echo/frontend/src/locales/fr-FR.po:1847-1849 Timestamp: 2025-05-30T15:37:46.616Z Learning: User ussaama prefers not to address minor French translation grammar issues when they don't impact functionality, considering them acceptable as "language things" rather than errors that need fixing.echo/frontend/src/locales/en-US.po (3)
478-485: LGTM - New tutorial slug translations shipped.Auto-generated i18n entries for the new onboarding card options look clean. The
Advanced (Tips and best practices)and related strings are properly added. Based on learnings, these .po files are managed by lingui/cli, so no manual edits needed.
1164-1166: LGTM - Default tutorial option entry added.New
Default - No tutorial (Only privacy statements)translation entry properly added for the slug-based configuration. Clean.
3394-3396: LGTM - Skip consent option added.The
Skip data privacy slide (Host manages consent)entry is properly localized. This enables hosts to handle consent externally - solid addition for enterprise use cases.echo/frontend/src/components/participant/hooks/useOnboardingCards.ts (5)
1-12: LGTM - Clean icon imports.Solid selection of lucide-react icons for the onboarding UX. Using named imports keeps the bundle lean.
16-51: LGTM - Slug normalization is bulletproof.Clean defensive coding here:
- Lowercase normalization handles case mismatches
- Explicit validation against
validSlugsarray- Safe fallback to
"none"for garbage inputThis will handle whatever the backend throws at it. Ship it.
53-406: LGTM - Comprehensive localized basic tutorial content.Five languages fully supported with consistent structure. The en-US fallback on line 405 ensures users never see a blank onboarding. Content is static but that's fine for tutorial flows that don't change frequently.
1043-1044: Inconsistent language fallback - returnsnullinstead of en-US.
getBasicTutorialCardsandgetAdvancedTutorialCardsboth fallback to en-US when language isn't found. ButgetPrivacyCardreturnsnull, meaning users with unsupported languages get no privacy consent slide when usingslug="none".If this is intentional (strict localization for legal/compliance content), add a comment explaining why. Otherwise, consider matching the fallback behavior:
- return privacyCards[lang] || null; + return privacyCards[lang] || privacyCards["en-US"] || null;
1046-1046: LGTM - Clean hook API surface.Only exposing
getSystemCardskeeps the API tight. The helper functions are implementation details - good encapsulation. Ship it 🚀echo/frontend/src/locales/de-DE.po (1)
229-231: LGTM! Translation updates ship clean.The new tutorial options are properly translated and the old entries correctly obsoleted. Auto-generated by lingui/cli as expected—line refs updated, new strings present, obsolete markers in place. Ship it.
Based on learnings, .po files are auto-generated by lingui/cli.
Also applies to: 233-235, 881-883, 3063-3065
| const getAdvancedTutorialCards = (lang: string): LanguageCards[string] => { | ||
| const tutorialCards: Record<string, LanguageCards[string]> = { | ||
| "de-DE": [ | ||
| { | ||
| section: "Willkommen", | ||
| slides: [ | ||
| { | ||
| content: | ||
| "Nehmen Sie Ihre Stimme auf, um Fragen zu beantworten und Einfluss zu nehmen.", | ||
| cta: "Los geht's!", | ||
| extraHelp: | ||
| "Dies ist ein Mini-Tutorial. Nutzen Sie die Vor- und Zurück-Tasten zur Navigation. Nach Abschluss gelangen Sie zum Aufnahmeportal.", | ||
| icon: PartyPopper, | ||
| title: "Willkommen bei Dembrane!", | ||
| }, | ||
| { | ||
| content: | ||
| "Dembrane hilft Menschen, einfach Input von großen Gruppen zu sammeln.", | ||
| cta: "Mehr erfahren", | ||
| extraHelp: | ||
| "Ob Feedback für eine Kommune, Input im Arbeitsumfeld oder Teilnahme an Forschung – Ihre Stimme zählt!", | ||
| icon: Orbit, | ||
| title: "Was ist Dembrane?", | ||
| }, | ||
| { | ||
| content: | ||
| "Beantworten Sie Fragen in Ihrem eigenen Tempo durch Sprechen oder Tippen.", | ||
| cta: "Weiter", | ||
| extraHelp: | ||
| "Spracheingabe ist unser bevorzugter Modus, da sie natürlichere und detailliertere Antworten ermöglicht. Tippen steht immer als Alternative zur Verfügung.", | ||
| icon: Speech, | ||
| title: "Sagen Sie einfach Ihre Meinung", | ||
| }, | ||
| { | ||
| content: "Dembrane macht in Gruppen mehr Spaß!", | ||
| cta: "Weiter", | ||
| extraHelp: | ||
| "Dembrane macht mehr Spaß, wenn Sie jemanden finden, um die Fragen gemeinsam zu besprechen und Ihr Gespräch aufzunehmen. Wir können nicht sagen, wer was gesagt hat, nur welche Ideen geteilt wurden.", | ||
| icon: MessagesSquare, | ||
| title: "Allein oder in der Gruppe", | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| section: "Wie es funktioniert", | ||
| slides: [ | ||
| { | ||
| content: | ||
| "Sie erhalten die Fragen, sobald Sie im Aufnahmeportal sind.", | ||
| cta: "Verstanden", | ||
| extraHelp: | ||
| "Die Fragen variieren je nach den Bedürfnissen des Gastgebers. Sie können sich auf kommunale Themen, Arbeitserfahrungen oder Forschungsthemen beziehen. Wenn es keine spezifischen Fragen gibt, können Sie gerne alle Gedanken oder Anliegen teilen.", | ||
| icon: HelpCircle, | ||
| title: "Fragerunde", | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| section: "Datenschutz", | ||
| slides: [ | ||
| { | ||
| content: | ||
| "Als Aufnehmer haben Sie die Kontrolle über das, was Sie teilen.", | ||
| cta: "Mehr erfahren", | ||
| extraHelp: | ||
| "Vermeiden Sie die Weitergabe von Details, die Sie dem Gastgeber nicht mitteilen möchten. Seien Sie achtsam und nehmen Sie andere nicht ohne deren Zustimmung auf.", | ||
| icon: Lock, | ||
| title: "Datenschutz ist wichtig", | ||
| }, | ||
| ...(getPrivacyCard("de-DE")?.slides || []), | ||
| ], | ||
| }, | ||
| { | ||
| section: "Best Practices", | ||
| slides: [ | ||
| { | ||
| content: | ||
| "Stellen Sie sich vor, Dembrane ist auf Lautsprecher mit Ihnen. Wenn Sie sich selbst hören können, ist alles in Ordnung.", | ||
| cta: "Verstanden", | ||
| extraHelp: | ||
| "Etwas Hintergrundgeräusch ist in Ordnung, solange klar ist, wer spricht.", | ||
| icon: Volume2, | ||
| title: "Hintergrundgeräusche reduzieren", | ||
| }, | ||
| { | ||
| content: | ||
| "Stellen Sie eine stabile Verbindung für eine reibungslose Aufnahme sicher.", | ||
| cta: "Bereit!", | ||
| extraHelp: | ||
| "WLAN oder gute mobile Daten funktionieren am besten. Wenn Ihre Verbindung abbricht, keine Sorge. Sie können immer dort weitermachen, wo Sie aufgehört haben.", | ||
| icon: Wifi, | ||
| title: "Starke Internetverbindung", | ||
| }, | ||
| { | ||
| content: | ||
| "Vermeiden Sie Unterbrechungen, indem Sie Ihr Gerät entsperrt halten. Wenn es sich sperrt, entsperren Sie es einfach und fahren Sie fort.", | ||
| cta: "Okay", | ||
| extraHelp: | ||
| "Dembrane versucht, Ihr Gerät aktiv zu halten, aber manchmal können Geräte dies überschreiben. Sie können Ihre Geräteeinstellungen anpassen, um länger entsperrt zu bleiben, wenn nötig.", | ||
| icon: Smartphone, | ||
| title: "Gerät nicht sperren!", | ||
| }, | ||
| ], | ||
| }, | ||
| ], |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting shared sections to reduce duplication.
The Welcome, How it works, and Privacy sections are nearly identical between getBasicTutorialCards and getAdvancedTutorialCards. The only difference is that advanced adds the "Best Practices" section.
Could DRY this up by composing:
const getAdvancedTutorialCards = (lang: string): LanguageCards[string] => {
const basicCards = getBasicTutorialCards(lang);
const bestPractices = getBestPracticesCards(lang);
// Merge privacy card slides and append best practices section
return [...basicCards, bestPractices];
};Not blocking - the current approach is explicit and easy to reason about. Just a thought if localization updates become a maintenance burden.
🤖 Prompt for AI Agents
In echo/frontend/src/components/participant/hooks/useOnboardingCards.ts around
lines 408-512, the Welcome, How it works, and Privacy sections are duplicated
between getBasicTutorialCards and getAdvancedTutorialCards; extract the shared
sections into a small helper (e.g., getSharedTutorialSections(lang) or reuse
getBasicTutorialCards(lang)) and implement a getBestPracticesSection(lang) for
the extra advanced slides, then have getAdvancedTutorialCards call the shared
helper/getBasicTutorialCards and append the Best Practices section (ensuring
privacy slides are merged via getPrivacyCard(lang) as before) to avoid
duplication and keep behavior identical.
| const rawTutorialSlug = | ||
| project.default_conversation_tutorial_slug?.toLowerCase(); | ||
| const validSlugs = ["skip-consent", "none", "basic", "advanced"]; | ||
| const normalizedTutorialSlug = validSlugs.includes(rawTutorialSlug || "") | ||
| ? rawTutorialSlug | ||
| : "none"; | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Slug normalization looks solid; consider DRY’ing the allowed list.
Lowercasing + whitelist + fallback to "none" is exactly what you want here, and it protects you from old "None" values or random garbage. The only nit: the validSlugs literal is now another source of truth alongside Directus config / onboarding hooks. Hoisting this into a shared TUTORIAL_SLUGS constant (and maybe a TutorialSlug type) would keep things 100% in sync as you evolve the flows.
Also applies to: 247-247
🤖 Prompt for AI Agents
In echo/frontend/src/components/project/ProjectPortalEditor.tsx around lines
230-236 (and also update line 247), the allowed tutorial slugs are hardcoded
locally; hoist that array into a shared exported constant (e.g., TUTORIAL_SLUGS)
in a common module (or a new constants file) and export a corresponding
TutorialSlug type (union of literals). Replace the local validSlugs with an
import of TUTORIAL_SLUGS, use it for normalization and any other occurrences
(line 247) so both the component and other code use the same single source of
truth; ensure the constant is lowercased or compare using toLowerCase() as
currently implemented.
| #: src/components/project/ProjectPortalEditor.tsx:763 | ||
| msgid "dashboard.dembrane.concrete.description" | ||
| msgstr "dashboard.dembrane.concrete.description" | ||
|
|
There was a problem hiding this comment.
Don't ship raw js‑lingui IDs as FR “translations”
These explicit‑ID entries still have the key copied into msgstr:
dashboard.dembrane.concrete.descriptiondashboard.dembrane.concrete.topic.selectparticipant.concrete.regenerating.artefact.descriptionparticipant.concrete.loading.artefact.description
If these are rendered anywhere, FR users will see opaque IDs instead of real text. That’s a UX‑breaking regression for the concrete/verify flows.
Recommend:
- Either provide proper French copy for each, mirroring the English source strings from
en-US.po, or - If you intentionally want to fall back to English, set
msgstrto the English sentence, not the technical key.
Until these are translated or explicitly set to a user‑facing fallback, I’d treat this as non‑ship‑ready for FR.
Also applies to: 2931-2934, 3486-3489, 3491-3494
🤖 Prompt for AI Agents
In echo/frontend/src/locales/fr-FR.po around lines 1104-1107 (and also at
2931-2934, 3486-3489, 3491-3494), several msgstr entries currently contain raw
js‑lingui keys (e.g. "dashboard.dembrane.concrete.description") instead of
user‑facing text; replace each msgstr with either the proper French translation
mirroring the English source from en-US.po, or if you intentionally want English
fallbacks set msgstr to the English sentence (not the key); update all listed
entries so French users see readable sentences rather than technical IDs.
| #: src/components/project/ProjectPortalEditor.tsx:543 | ||
| msgid "Advanced (Tips and best practices)" | ||
| msgstr "Geavanceerd (Tips en best practices)" | ||
|
|
There was a problem hiding this comment.
Tutorial option translations are aligned with the new slugs; one minor copy glitch.
The Dutch strings for the tutorial choices all line up with the new semantics:
- Advanced:
"Geavanceerd (Tips en best practices)" - Basic:
"Basis (Alleen essentiële tutorial slides)" - Default/none:
"Standaard - Geen tutorial (Alleen privacy verklaringen)" - Error text for loading the Portal is also wired correctly to the new ParticipantStart route.
Only nit: "Datenschutzkarte overslaan (Organisatie beheert toestemming)" mixes a German noun into the Dutch sentence. Might be worth tightening that to a fully Dutch phrase so it doesn’t stand out in production.
Also applies to: 553-556, 944-947, 2200-2203, 301-304
🤖 Prompt for AI Agents
In echo/frontend/src/locales/nl-NL.po around lines 244-247 (and also update
occurrences at 553-556, 944-947, 2200-2203, 301-304), a German noun
"Datenschutzkarte" is present in Dutch strings; replace it with the proper Dutch
term (e.g., "privacykaart") and keep the rest of the sentence intact so it reads
fully Dutch ("privacykaart overslaan (Organisatie beheert toestemming)"),
updating each listed location consistently.
Summary by CodeRabbit
New Features
Improvements
Localization
✏️ Tip: You can customize this high-level summary in your review settings.