Frontend (UI)
Last updated: 2026-05-26
Frontend (UI)
This document describes the frontend user interface architecture, technologies used, component structure, state management, and development guidelines.
Technology Stack
The frontend is a modern single-page application (SPA) built with the following technologies:
- Framework: React 18
- Build Tool: Vite
- Language: TypeScript
- Styling: Tailwind CSS
- Icons: Lucide React
- Markdown Rendering: react-markdown
- Animations: @lottiefiles/dotlottie-react
- Feature Flags: @openfeature/web-sdk with a custom
ApiFeatureProvider
Project Structure
The main frontend code resides in the frontend/src/ directory:
main.tsx: Application entry point. Sets OpenFeature context/provider, then wrapsAppinOpenFeatureProvider,AuthProvider, andCollectiveProvider.App.tsx: Root component. Assembles the full UI and conditionally renders the brand screen, consent gate, onboarding form, resume prompt, or main chat interface based on session scope and consent state./components: Reusable UI components (see Core Components below)./context: React contexts —AuthContext.tsxfor authentication/session/consent state,CollectiveContext.tsxfor collective management state./hooks: Custom hooks —useChat.tsis the primary chat orchestration hook./providers: OpenFeature provider —ApiFeatureProvider.tsfetches flags from the backend./services: Client-side service utilities —authService.ts,chatStreamService.ts,collectiveService.ts,countryService.ts./types: TypeScript types and interfaces —auth.ts,index.ts./utils: Miscellaneous helpers —i18n.ts,userStorage.ts,invitation.ts,clientStreamSimulator.ts.config.ts: Frontend configuration (backend API URL fromVITE_API_URL).
Authentication & Session Management
AuthContext (context/AuthContext.tsx)
A React context that manages the full authentication lifecycle including consent and invitation processing:
- Login flow: On mount,
AuthContextchecks stored token/session state and feature-provider readiness. Ifenable_login_screenis disabled, it auto-logs in by calling the mock IDP and thenPOST /auth/login. If enabled, it waits for explicit user action. The request includeshas_consented,invitation_id(if arriving from an invite URL), andage_class. The resultingapp_session_id, user profile, consent verification status, and conversation history are stored in context. - Consent-driven onboarding: The response includes a
consent_verificationblock and the session’sscope. Based on scope:ONBOARDING— the user is shown theSettingsPanelto accept TOS and set up their profile.PENDING_CONSENT— theConsentGateis shown, blocking chat but allowing the teenager to request parental authorization.FULL— unrestricted chat access.
- Invitation processing: If the URL contains an
invitation_idquery parameter, the context passes it toPOST /auth/login. The backend processes the invitation, granting implicit parental consent and adding the user to the collective. - Auth retry: If authentication fails (network error, timeout), the context exposes
manualRetryModeandmanualRetry()function. The UI renders aBrandScreenwith a retry option. - Session resumption: The
AuthContextexposes auserHistoryobject (list of past conversations). Components use this to offer session resumption. - Persistence: The
app_session_idand last-useduser_idare persisted tolocalStorageviautils/userStorage.ts.
authService.ts (services/authService.ts)
Encapsulates the HTTP calls for authentication and profile management:
authenticateWithMockIdp(idpUserId, userType, language, origin, ageClass, hasConsented, invitationId)— obtains an IDP token from the mock IDP and exchanges it viaPOST /auth/login.origindefaults to'web_react',hasConsenteddefaults tofalse,invitationIdis optional.updateProfile(appSessionId, language, country, ageClass, hasConsented)— callsPATCH /auth/profileduring onboarding to update user settings.createAuthorizationRequest(token)— callsPOST /auth/request-authorizationto create a parental authorization voucher (teenager only). Requires a bearer token.getAuthorizationRequestInfo(token)— fetches non-sensitive info about an authorization request viaGET /auth/authorization-request/{token}.authorizeTeenager(token)— fulfills an authorization request viaPOST /auth/authorize-teenager/{token}(parent only).- Returns an
AuthResponsecontainingapp_session_id, application token, profile (with consent verification), andUserHistory.
collectiveService.ts (services/collectiveService.ts)
Client-side service for collective and invitation management:
listCollectives()—GET /collectiveslistMembers(collectiveId)—GET /collectives/{id}/memberslistInvitations(collectiveId)—GET /collectives/{id}/invitationscreateInvitation(collectiveId, request)—POST /collectives/{id}/invitationsrevokeInvitation(collectiveId, invitationId)—DELETE /collectives/{id}/invitations/{invitationId}updateMemberConsent(collectiveId, memberId, hasParentalConsent)—PATCH /collectives/{id}/members/{memberId}/consentauthorizeTeenager(teenagerId)—POST /collectives/authorize-teenager
countryService.ts (services/countryService.ts)
Provides country list/translation utilities for the SettingsPanel country selector. Backend Geo-IP (User.country) remains the primary source for prefilled country; frontend utilities mainly support display/localization.
Feature Flags (providers/ApiFeatureProvider.ts)
The ApiFeatureProvider implements the OpenFeature Provider interface. On initialisation it calls GET /features (with the current variant and environment query parameters) and populates the OpenFeature in-memory store. After that, any component can read flags synchronously via useFlag from @openfeature/react-sdk.
Flags consumed by the frontend:
| Flag | Effect when true |
|---|---|
enable_monster_flip_animation |
Monster avatar renders with a 3D coin-flip transition on change |
enable_simple_resume_prompt |
Floating SimpleResumePrompt is shown instead of the full history modal |
enable_login_screen |
Manual login screen is displayed before auto-login completes |
collective_management |
Collective administration dashboard and invitations are accessible (parent variant only) |
enable_simple_qr_invitation |
Simplified QR code modal without age-class selection (parent variant only) |
enable_settings_panel |
Full SettingsPanel is rendered instead of the simple language selector |
enforce_consent_gate |
ConsentGate blocks access to chat when parental consent is missing |
Core Components
BrandScreen (components/BrandScreen.tsx)
A full-screen loading/branding sequence displayed while authentication is in progress. Features:
- Floating monster Lottie animations.
- Localized tagline text (
i18n.ts). - Smooth fade-out transition when authentication completes.
MonsterAvatar (components/MonsterAvatar.tsx)
Replaces the previous inline monster display. Key feature: 3D coin-flip animation — when the active monster changes, the component plays a CSS rotateY keyframe animation (enabled via the enable_monster_flip_animation flag). Monster state is synchronised with the backend: the SSE settings event updates the avatar in real time.
SimpleResumePrompt (components/SimpleResumePrompt.tsx)
A floating, non-blocking UI card that appears when a returning user has a previous conversation. Offers a single “Continue” button to resume the last session. Shown when enable_simple_resume_prompt is true; the full ConversationHistoryModal is used otherwise.
ConversationHistoryModal (components/ConversationHistoryModal.tsx)
A modal dialog listing all past conversations for the current user (sourced from AuthContext.userHistory). Allows the user to:
- Preview each conversation (date, monster, summary).
- Resume any previous conversation.
- Delete a conversation from history.
Dates and times are localised to the user’s browser locale.
CollectiveBoardModal (components/CollectiveBoardModal.tsx)
An administrative dashboard for the parent variant (shown when collective_management flag is true). Provides an overview of collective (e.g., school-class) activity. Access is granted via a dedicated button in the Header that is only rendered in the parent variant.
ConsentGate (components/ConsentGate.tsx)
A blocking overlay shown to teenager users who have accepted the TOS but lack parental consent (scope PENDING_CONSENT). When the enforce_consent_gate flag is true, this component replaces the chat interface entirely. The gate offers two paths: - Request Authorization — generates a shareable link for the teenager to send to their parent via POST /auth/request-authorization. - Settings — opens the SettingsPanel to update country or language.
SettingsPanel (components/SettingsPanel.tsx)
A comprehensive onboarding/settings modal replacing the simple language selector. When enable_settings_panel is true, this component is rendered instead. Features: - Language selector — switches UI and AI response language, persisted via POST /chat/settings/change. - Country selector — pre-filled from Geo-IP (if available) or manual selection. Persisted via PATCH /auth/profile. - Age class selector — for finer-grained personalisation. Persisted via PATCH /auth/profile. - Consent toggle — a checkbox for TOS acceptance, persisted via PATCH /auth/profile. - Parental authorization section (teenager only) — when missing parental consent, shows a “Request Authorization” button that calls POST /auth/request-authorization and displays a shareable link/code.
InvitationModal (components/InvitationModal.tsx)
An invitation creation and management dialog for the parent collective board. Features: - Age-class selection — filters available children by age group when creating an invite. - Label field — optional human-readable name for the invitation. - Invite URL generation — calls POST /collectives/{id}/invitations and displays a ready-to-share URL. - Active invitations list — shows pending/active invites with usage counts and revoke controls.
SimpleQRModal (components/SimpleQRModal.tsx)
A simplified QR code generation modal (shown when enable_simple_qr_invitation is true). Creates non-expiring invitations without age-class selection — designed for quick in-person onboarding via QR code scan.
ConfirmationModal (components/ConfirmationModal.tsx)
A reusable modal used across the application for confirm/dismiss dialogs (e.g., revoking invitations, deleting conversations). Presents a title, message, and two action buttons.
Header.tsx (components/Header.tsx)
Renders the application’s top navigation bar. Features:
- Monster Face: A static SVG-based monster face (eyes and mouth) providing visual branding.
- Session Controls: Buttons for starting a new chat (
onNewChat), viewing history (onOpenHistory), and deleting history (onDeleteHistory). - Collective Board: An access button for the administration dashboard (parent variant only, when
collective_managementflag istrue).
ChatInput (components/ChatInput.tsx)
The primary interaction area for the user. Updated features:
- Image Paste & Drag-and-Drop: Supports pasting images from the clipboard or dragging them onto the input area. Files are validated and uploaded via
POST /files/upload. - Voice Input: Supports speech-to-text transcription via the backend
/audio/transcribeendpoint. - Monster Selector: Allows users to change the active monster persona via a dropdown menu. Changes are persisted via
POST /chat/settings/change. - Language Selector: Allows users to switch the UI and AI response language. Changes are persisted via
POST /chat/settings/change.
MonsterDisplay (components/MonsterDisplay.tsx)
A high-visibility animation component that renders large monster avatars and speech bubbles in the center of the screen.
- SSE Driven: Listens for
settingsevents from the SSE stream to trigger animations. - Typing Effects: Implements a typewriter effect for both the monster’s name and its accompanying message.
- Animation Queue: Manages a queue of events to ensure animations play sequentially even if multiple events are received rapidly.
Other Key Components
App.tsx: Orchestrates the full application, conditionally renderingBrandScreen,Header,MessageList,ChatInput,SimpleResumePrompt, and modals based on auth and flag state.MessageList.tsx: Renders the conversation. Conditionally rendersEmotionSelectororDropdownSelectorwhen the agent requires structured input. Supports inline image rendering for pasted attachments.
Frontend Internationalization (i18n)
The frontend has its own i18n system in frontend/src/utils/i18n.ts, separate from the backend’s YAML-based prompt system. All user-facing UI strings (buttons, labels, modal text, onboarding messages) are now centralized in this module across 6 languages (FR, EN, ES, IT, DE, PT).
- Language detection: The user’s language is determined from the
AuthContext(set during login), with fallback to the browser’snavigator.language. - Content categories: Strings are organised by domain —
consent,onboarding,settings,invitations,collectives,brandScreen,chatInput,header,errors. - Runtime switching: When the user changes language in the
SettingsPanel, all visible UI text updates immediately via React re-render.
State Management & Data Flow
The useChat Hook (hooks/useChat.ts)
The central hook managing all chat state:
- Calls
/chat/initonce per session (guarded against duplicate calls). - Sends messages via
POST /chatwith optionalattachments. - Handles
emotionanddropdownselections. - Processes
ChatResponsemetadata to update monster, language, and interactive flags. - Exposes
deleteUserSummary()which callsPOST /chat/deleteand clears local history state.
Real-Time Events with SSE
The SSEProvider establishes a persistent EventSource connection to GET /chat/{session_id}/events/listen. Pushed settings events are used to:
- Trigger Global Animations:
MonsterDisplay.tsxreacts to monster and message updates with pop-in animations and speech bubbles. - Update Message Avatars: Triggers the
MonsterAvatar3D flip animation for the latest message if theenable_monster_flip_animationflag is on. - Synchronize Settings: Updates the current language and monster selectors in
ChatInput.tsx.
Styling
The UI is styled with Tailwind CSS. Notable additions:
- The
MonsterAvatar3D flip uses aperspective+rotateYkeyframe defined intailwind.config.js. - A
souffleDeactivate(purple, teenager variant) andsouffleDeactivateRed(red, parent variant) animation class control variant-specific visual cues. - The brand screen uses its own full-screen overlay with floating monster animation keyframes.
Environment Configuration
The frontend reads environment variables at build time via Vite:
| Variable | Description |
|---|---|
VITE_API_URL |
Backend API base URL (required) |
VITE_CHATBOT_VARIANT |
Variant passed to ApiFeatureProvider (teenager / parent) |
VITE_SITE_VARIANT |
Alias used by Tailwind for variant-specific styling |
In local development create a .env.development file in the repository root (see deployment/deploy.py for the format). For production builds the CI/CD workflow injects these values automatically. The backend now serves two frontend origins — a teenager URL and a parent URL — configured via FRONTEND_TEENAGER_URL and FRONTEND_PARENT_URL in api/config.py. This split is essential for the consent authorization flow where the parent authorization URL needs a distinct origin.
Running in Development
Navigate to the
frontenddirectory:cd frontendInstall the dependencies:
npm installRun the development server:
npm run dev
This starts the Vite dev server, typically at http://localhost:5173. The frontend expects the backend API to be running concurrently (see Local Development).