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:

Project Structure

The main frontend code resides in the frontend/src/ directory:

  • main.tsx: Application entry point. Wraps App in the OpenFeatureProvider after asynchronous feature flag initialisation.
  • App.tsx: Root component. Assembles the full UI, owns the AuthContext and CollectiveContext, 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.tsx for authentication/session/consent state, CollectiveContext.tsx for collective management state.
  • /hooks: Custom hooks — useChat.ts is the primary chat orchestration hook.
  • /providers: OpenFeature provider — ApiFeatureProvider.ts fetches 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 from VITE_API_URL).

Authentication & Session Management

AuthContext (context/AuthContext.tsx)

A React context that manages the full authentication lifecycle including consent and invitation processing:

  1. Auto-login flow: On mount, the app calls the mock IDP to obtain a token, then exchanges it for an AppSession via POST /auth/login. The request now includes has_consented, invitation_id (if arriving from an invite URL), and age_class. The resulting app_session_id, user profile, consent verification status, and conversation history are stored in context.
  2. Consent-driven onboarding: The response includes a consent_verification block and the session’s scope. Based on scope:
    • ONBOARDING — the user is shown the SettingsPanel to accept TOS and set up their profile.
    • PENDING_CONSENT — the ConsentGate is shown, blocking chat but allowing the teenager to request parental authorization.
    • FULL — unrestricted chat access.
  3. Invitation processing: If the URL contains an invitation_id query parameter, the context passes it to POST /auth/login. The backend processes the invitation, granting implicit parental consent and adding the user to the collective.
  4. Auth retry: If authentication fails (network error, timeout), the context exposes manualRetryMode and manualRetry() function. The UI renders a BrandScreen with a retry option.
  5. Session resumption: The AuthContext exposes a userHistory object (list of past conversations). Components use this to offer session resumption.
  6. Persistence: The app_session_id and last-used user_id are persisted to localStorage via utils/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 via POST /auth/login. Sends all consent and invitation parameters.
  • updateProfile(appSessionId, language, country, ageClass, hasConsented) — calls PATCH /auth/profile during onboarding to update user settings.
  • requestAuthorization() — calls POST /auth/request-authorization to create a parental authorization voucher (teenager only).
  • getAuthorizationRequestInfo(token) — fetches non-sensitive info about an authorization request via GET /auth/authorization-request/{token}.
  • authorizeTeenager(token) — fulfills an authorization request via POST /auth/authorize-teenager/{token} (parent only).
  • Returns an AuthResponse containing app_session_id, application token, profile (with consent verification), and UserHistory.

collectiveService.ts (services/collectiveService.ts)

Client-side service for collective and invitation management:

  • listCollectives()GET /collectives
  • listMembers(collectiveId)GET /collectives/{id}/members
  • listInvitations(collectiveId)GET /collectives/{id}/invitations
  • createInvitation(collectiveId, request)POST /collectives/{id}/invitations
  • revokeInvitation(collectiveId, invitationId)DELETE /collectives/{id}/invitations/{invitationId}
  • updateMemberConsent(collectiveId, memberId, hasParentalConsent)PATCH /collectives/{id}/members/{memberId}/consent
  • authorizeTeenager(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.

AuthorizeTeenagerModal (components/AuthorizeTeenagerModal.tsx)

Shown to parents who navigate to an authorization link (/authorize-child?token=...). Fetches the authorization request info, displays the teenager’s language/status, and provides an “Authorize” button that calls POST /auth/authorize-teenager/{token}. On success, the teenager is added to the parent’s collective and granted implicit consent.

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_management flag is true).

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/transcribe endpoint.
  • 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 settings events 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 rendering BrandScreen, Header, MessageList, ChatInput, SimpleResumePrompt, and modals based on auth and flag state.
  • MessageList.tsx: Renders the conversation. Conditionally renders EmotionSelector or DropdownSelector when 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’s navigator.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/init once per session (guarded against duplicate calls).
  • Sends messages via POST /chat with optional attachments.
  • Handles emotion and dropdown selections.
  • Processes ChatResponse metadata to update monster, language, and interactive flags.
  • Exposes deleteUserSummary() which calls POST /chat/delete and 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.tsx reacts to monster and message updates with pop-in animations and speech bubbles.
  • Update Message Avatars: Triggers the MonsterAvatar 3D flip animation for the latest message if the enable_monster_flip_animation flag 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 MonsterAvatar 3D flip uses a perspective + rotateY keyframe defined in tailwind.config.js.
  • A souffleDeactivate (purple, teenager variant) and souffleDeactivateRed (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

  1. Navigate to the frontend directory:

    cd frontend
  2. Install the dependencies:

    npm install
  3. Run 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).