Frontend (UI)
Last updated: 2026-05-08
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. WrapsAppin theOpenFeatureProviderafter asynchronous feature flag initialisation.App.tsx: Root component. Assembles the full UI, owns theAuthContextandCollectiveContext, 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:
- Auto-login flow: On mount, the app calls the mock IDP to obtain a token, then exchanges it for an
AppSessionviaPOST /auth/login. The request now 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, hasConsented, invitationId, ageClass)— obtains an IDP token from the mock IDP and exchanges it viaPOST /auth/login. Sends all consent and invitation parameters.updateProfile(appSessionId, language, country, ageClass, hasConsented)— callsPATCH /auth/profileduring onboarding to update user settings.requestAuthorization()— callsPOST /auth/request-authorizationto create a parental authorization voucher (teenager only).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)
Provided for cases where the client-side needs to perform country resolution (e.g., for the SettingsPanel country dropdown prefill). The backend’s Geo-IP resolver is the primary source, but the frontend can supplement it. The detected country is stored in localStorage to pre-populate the user’s country selector in SettingsPanel.
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).