Commit graph

221 commits

Author SHA1 Message Date
Vadym Samoilenko
c25d2423a0 fix(enrichment): handle ObjectId/datetime in persona dict before LLM call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 15:13:38 +01:00
Vadym Samoilenko
604fdb9458 feat(marketplace): full persona page + categories + enrichment script
- GET /api/marketplace/personas/:id — returns all fields of a published persona
- Expand _COPY_FIELDS to include all new schema fields (oceanTraits, scenarios, aiSynthesizedBio, etc.)
- New MarketplacePersonaView page (/marketplace/personas/:id) with full sidebar + tabs
- Preview button navigates to full page instead of opening a summary dialog
- Clicking card body also navigates to persona page
- scripts/enrich_old_personas.py — LLM enrichment for 12 pre-schema personas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:38:42 +01:00
Vadym Samoilenko
a8b0a40e08 fix(personas): expand PERSONA_ALLOWED_FIELDS + add library/clone endpoints
Port fixes from Semblance:
- PERSONA_ALLOWED_FIELDS: 9 → 62 fields — oceanTraits, frustrations,
  motivations, scenarios, aiSynthesizedBio, household/lifestyle fields,
  audience_brief, research_objective, etc. were silently discarded at
  Persona.create(), causing all OCEAN traits to render as 50% on frontend
- GET /personas/library — shared library endpoint (all users, bulk user lookup)
- POST /personas/:id/clone — clone any persona to current user
- GET /personas/:id — remove 403 for non-owners (read is public to auth users)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 14:12:04 +01:00
Vadym Samoilenko
dd816783e7 fix(ui): replace hardcoded colors in persona components with semantic tokens
PersonaPersonality: bg-blue-500/green-500/red-500 → primary/brand-success/destructive
PersonaEditor: bg-gray-200/text-gray-600 → bg-primary/20/text-primary
PersonaGenerationPrompts: text-slate-500/400 → text-muted-foreground
PersonaSidebar: bg-green-500/80 → bg-brand-success/80
PersonaProfile: bg-green-600 → bg-brand-success

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 17:29:23 +01:00
Vadym Samoilenko
92657a1c0c feat(ui): 3D round table with person figures + dark theme polish
- RoundTable3D: replace sphere avatars with head+body person figures,
  add per-persona glow pads, expanding pulse rings for active speaker,
  connection line to table center, ambient particles, pill nameplates
  with occupation, rotating center emblem
- FocusGroupSession: fix Rules-of-Hooks (move useMemo before early
  returns), fix personas not loading (data.personas vs data.participants)
- Dark theme: replace hardcoded Tailwind colors across 20+ components
  with semantic CSS tokens (bg-card, text-primary, text-muted-foreground,
  text-brand-success, text-destructive, etc.)
- Add three + @types/three dependencies for 3D rendering

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 17:26:33 +01:00
Vadym Samoilenko
0f7b8a5a9e fix(ui): fix crashes, dark theme tokens, Invalid Date bug
- FocusGroupSession: add missing DiscussionGuideViewer import (was crashing)
- PersonaProfile: wrap TabsTrigger in TabsList (RovingFocusGroup crash)
- Dashboard: fix Invalid Date bug — tx.ts vs tx.created_at field mismatch
- ParticipantPanel, UserCard, SyntheticUsers: replace hardcoded light-mode
  colors (bg-blue-50, text-slate-*, bg-white) with dark theme tokens
- AutonomousDashboard, NotesPanel, QuickNoteModal: same dark theme sweep

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 15:24:28 +01:00
Vadym Samoilenko
4c70bc8aa6 fix: PDF layout, credit idempotency, more active discussion
- PDF: meta_row redesigned to same-line two-column layout so values
  don't overflow the right edge; explicit cursor reset before each
  multi_cell body() call fixes key themes appearing to the right of
  titles; page numbers now use len(pdf.pages) + pdf.h-12 positioning
- Credits: replace fragile 4h credit_transactions lookup with atomic
  findOneAndUpdate stamp on the focus group doc itself (24h window),
  with rollback on insufficient balance — eliminates double-charging
  on crash/restart; CreditTransaction.record failure is now non-fatal
- Key themes: cap input at 80 messages + max_output_tokens=4096 to
  fix truncated JSON (Unterminated string at char 1580)
- Decision engine: require ≥4 participant responses per question before
  moving on; mandate debate/contrarian seeking after 2 agreements;
  call all participants to each question before anyone speaks 3rd time

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:09:14 +01:00
Vadym Samoilenko
8f068cc81c fix(seo): cache-bust OG image URL — rename to og-image-v2.png
Old URL was cached by Telegram/Slack/etc with Lovable branding.
New URL forces a fresh fetch of the correct Cohorta image.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:55:24 +01:00
Vadym Samoilenko
f746dbee8e fix(ai): silent participants + call_out sent as message + contrarian fix
- _apply_silent_participant_override: new Python-level override that forces
  any participant who hasn't spoken yet into the next turn (fires before
  contrarian check, language-aware Russian/English call-out)
- _execute_participant_respond: now sends call_out as a moderator message
  before generating the participant response (was silently skipped before,
  causing 0-action loops and incoherent conversation flow); uses .get()
  instead of [] to avoid KeyError when LLM omits optional fields
- _apply_contrarian_override: language-aware call-out message (Russian if
  last moderator message contains Cyrillic)
- conversation-decision-engine.md: explicit rules that call_out MUST name
  the participant and topic_context MUST give a specific angle; silent
  participants must be called before repeat speakers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:50:50 +01:00
Vadym Samoilenko
8109fe3768 fix(backend): 4 fixes — await add_message, MODEL_ALIASES, language rule, PDF + silent participants
- focus_group_ai.py: add missing `await` on all FocusGroup.add_message() calls
  (was storing coroutine as task result → JSON serialization 500 error)
- model_pricing.py: remove broken MODEL_ALIASES import from llm_service
  (was ImportError on every LLM usage cost tracking call)
- focus-group-response.md: language detection now based on last AI Moderator
  message in conversation, not abstract topic field (fixes mixed-language responses)
- conversation_context_service.py: analytics now shows per-participant message
  counts, explicit ⚠️ SILENT PARTICIPANTS and DOMINANT SPEAKERS warnings so
  decision engine correctly engages quiet members and asks probing questions
- focus_groups.py PDF report: add full transcript section (new page), fix bar
  chart to cap width within page bounds, add page number footers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:46:39 +01:00
Vadym Samoilenko
7dacb86d00 fix(focus-group): reset moderator_position on restart/duplicate + editable participants
- Restart endpoint now $unsets moderator_position, completion_reason,
  autonomous_ended_at, autonomous_started_at so the AI doesn't
  immediately conclude on the next run (was reading stale "guide complete" position)
- Duplicate endpoint excludes same fields from the copy for the same reason
- ParticipantPanel: add isEditable mode with remove buttons and
  searchable "Add Participant" dialog (only active when status=new)
- FocusGroupSession: wire allPersonas state + add/remove handlers,
  pass isEditable={status === 'new'} to ParticipantPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:27:50 +01:00
Vadym Samoilenko
19f43dbe26 feat(marketplace): add persona preview dialog + fix price save on publish
- Preview button on each persona card opens a dialog with description,
  personality, age/gender/location/category badges, and buy button
- Publish toggle in admin now sends price+category alongside published flag,
  so price is persisted even if admin never blurred the price input
- Fix price default in admin row: 0 instead of 5 to reflect actual DB state
- Fix confirmation dialog: price ?? 0 to avoid blank price display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 18:32:34 +01:00
Vadym Samoilenko
a6ee7cd922 fix(ui): dark theme for PersonaProfile + marketplace price default
- PersonaProfile: bg-background instead of bg-slate-50, dark review banner
- Think/Feel/Do cards: blue/red/green 500/10 opacity instead of -50 light variants
- Progress bar tracks: bg-secondary instead of bg-slate-100
- Scenario + prompt blocks: bg-secondary/40 instead of bg-slate-50
- Marketplace: price defaults to 0 when marketplace.price is undefined

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 18:25:08 +01:00
Vadym Samoilenko
c757cdb5ba chore(scripts): seed 7 developer personas for marketplace testing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:55:37 +01:00
Vadym Samoilenko
e8d2483a84 feat(marketplace): persona marketplace — admin publish + user purchase
- New /api/marketplace/personas GET (list published) + POST /<id>/purchase (deduct credits, copy persona)
- Admin routes: GET /api/admin/marketplace/personas (all originals) + PUT /<id> (price/category/publish)
- Duplicate protection: can't buy same persona twice (409), can't publish purchased copies
- MarketplaceTab.tsx — admin table with inline price/category edit and publish toggle
- PersonaMarketplace.tsx — user-facing grid with search, category filter, buy dialog
- /marketplace route added to App.tsx + nav link in AppLayout
- Translations: en/ru/uk for all marketplace strings
- marketplace field added to PERSONA_ALLOWED_FIELDS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:49:43 +01:00
Vadym Samoilenko
bbbca5bdf8 chore(billing): add backfill script for amount_usd on old purchase transactions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:14:39 +01:00
Vadym Samoilenko
783a89e825 fix(analytics): correct revenue, persona count, and gross margin
- Store amount_usd in Stripe webhook transaction (was only storing credits)
- Analytics endpoint: sum amount_usd for real USD revenue instead of credits
- Persona count: query personas collection directly (not credit transactions
  which counted transactions, not individual personas — batch creation
  of 3 at once appeared as count=1)
- Frontend: Revenue card now shows USD not credits; Gross Margin uses
  USD revenue so calculation is dimensionally correct

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:10:59 +01:00
Vadym Samoilenko
72e8dadb20 feat(personas): structural improvements for realistic focus group dialogue
- Per-persona message history: each persona now sees their own 8 previous
  responses, preventing repetition and enabling position evolution
- OCEAN archetype labels in decision engine context: instead of raw numbers,
  the decision LLM now sees "agreeableness: 72/100 [HIGH] — consensus-seeker"
- P2P interaction context: when participants interact directly, each one now
  knows who they are responding to and what that person last said
- Python-level contrarian override: when agreement ratio in recent messages
  exceeds 6% and a contrarian persona (low agreeableness or high neuroticism)
  hasn't spoken recently, Python overrides moderator/probe action to call them

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:00:01 +01:00
Vadym Samoilenko
e23e52f77d feat(personas): deep OCEAN semantics, missing fields, extraversion + baseline
- Add techSavviness, hasPurchasingPower, hasChildren, description to
  _format_persona_details() — previously ignored despite being in persona model
- Rewrite OCEAN block: each trait now includes its psychological definition,
  full name, and [LOW/MODERATE/HIGH] label so LLM understands the scoring scale
  (e.g. "Agreeableness (cooperation, empathy, conflict avoidance): 25/100 [LOW]")
- Add extraversion behavioral constraint to _generate_behavioral_instructions()
  (was used for response length only, never for personality guidance)
- Add LOW conscientiousness constraint (spontaneous, gut-feel)
- Add BALANCED PERSONALITY baseline for personas with all-moderate OCEAN scores —
  previously these got zero behavioral constraints, behaving generically
- Add optimist/enthusiast personality keyword mapping
- Add OCEAN archetype legend to conversation-decision-engine.md so decision
  engine understands what high/low scores mean when selecting next speaker
- Add +15% Diversity Boost modifier: prefer speakers whose archetype differs
  from the last 2 participants (reduces echo-chamber dynamics)
- Add OCEAN model explanation header to focus-group-response.md prompt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:46:56 +01:00
Vadym Samoilenko
64568b6164 feat(personas): improve individuality, add restart/duplicate, fix language
- Extend _format_persona_details() to include background, traits, values,
  interests, communication_style, brandLoyalty, priceConsciousness, demographics
  (race/ethnicity/culture) — previously these fields were stored but never
  sent to the LLM, causing all personas to give generic homogeneous answers
- Add _generate_behavioral_instructions() that translates OCEAN scores into
  explicit behavioral directives (low agreeableness → must push back,
  high neuroticism → voice of doubt, skeptic keyword → find the overpromise)
- Update focus-group-response.md to render BEHAVIORAL CONSTRAINTS and
  DEMOGRAPHIC CONTEXT sections so LLM uses cultural/financial context
- Add anti-groupthink rule to conversation-decision-engine: on convergence,
  prefer a contrarian persona before asking moderator probe question
- Improve ai-moderator-system.md: active @mention pull-in of silent
  participants, spontaneous follow-up probes on interesting responses,
  engagement tracking
- Add LANGUAGE RULE to all three response prompts: always match the language
  of the question (fixes mixed Russian/English responses)
- Add POST /focus-groups/<id>/restart endpoint: clears session collections,
  resets status to 'new', preserves config
- Add POST /focus-groups/<id>/duplicate endpoint: copies config to new group
- Frontend: Duplicate + Restart buttons on focus group cards with confirmation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:34:37 +01:00
Vadym Samoilenko
4e90741d75 fix(billing): fix metadata access for Stripe SDK v5 StripeObject
dict(session.metadata) fails with KeyError:0 — metadata is a StripeObject
in SDK v5, not a plain dict. Use getattr() for all metadata fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 14:08:06 +01:00
Vadym Samoilenko
afd44408e0 fix(billing): use Stripe SDK v5 attribute access in webhook handler
stripe.Webhook.construct_event() now returns StripeObject instances that
do not support .get()/__getitem__ — must use attribute access.
event["type"] → event.type, session.get("x") → session.x, etc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 14:04:58 +01:00
Vadym Samoilenko
1361e42837 fix(auth): clear React Query cache on logout to prevent cross-user data leaks
useMyUsage (and all other queries keyed without user identity) were showing
cached data from the previous user after logout/re-login. queryClient.clear()
in clearAuthData() wipes all in-memory cache on sign-out.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:37:54 +01:00
Vadym Samoilenko
bac7e7bce3 fix(usage): hide real costs from users — show credit balance, MTD spend, transaction history
- /api/usage/me now returns credits_balance, credits_spent (MTD debits), and
  last 30 credit transactions; no dollar amounts or token counts exposed
- MyUsage page redesigned: balance + MTD spent cards, transaction history table,
  Buy Credits button
- Admin: POST /api/billing/test-checkout — $1 Stripe Checkout for any credit
  amount to validate the payment flow end-to-end
- stripe_service: cast unit_amount to int (Stripe rejects float)
- i18n updated in EN/RU/UK

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:32:28 +01:00
Vadym Samoilenko
d92a099ade feat(ai-config): wire admin UI to LLM service — endpoint/key/model from DB
- _get_runtime_config(): reads active provider endpoint, api_key, main/mini
  model from app_settings (60s cache), falls back to env vars
- get_azure_client() now async, accepts cfg dict
- All generate_* methods call _get_runtime_config() per invocation so DB
  changes take effect without restart
- app_settings: _seed_from_env() backfills empty endpoint/api_key from env
  vars on first load so the admin UI shows current values immediately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:10:40 +01:00
Vadym Samoilenko
c98f6da6d2 fix(admin): usage report shows email instead of user ID when grouped by user
Batch-resolves user_id strings to emails after the aggregation via a
single find() on the users collection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:01:43 +01:00
Vadym Samoilenko
b74bd7cf99 fix(report): PDF formatting — no empty lines, strip markdown, fix deprecated fpdf2 API
- sanitize_llm(): strips **bold**, ## headers, converts - bullets → •, collapses 3+ newlines
- body(): splits LLM text into paragraphs at \n\n, joins intra-paragraph lines into single
  line so multi_cell never creates blank gaps from double newlines
- quote_block(): collapse newlines in quote text to single space
- multi_cell(0, ...) → multi_cell(EPW, ...) for theme titles
- cell(0, 5, ..., ln=True) → cell(EPW, 5, ..., new_x=LMARGIN, new_y=NEXT)
- Participation analytics: ln=0/True → new_x/new_y API, track x_after_name correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:50:02 +01:00
Vadym Samoilenko
6d2f59fe74 feat(report): PDF export with Cyrillic, i18n, key decisions + analytics
- PDF via fpdf2 (DejaVu font from fonts-dejavu-core, Cyrillic support)
- Lang param (?lang=ru/en) passed from i18n.language in ThemesPanel
- Prompts updated: executive summary + new key-decisions prompt, both
  write entirely in the requested language
- Analytics section: participation bar chart per participant
- Key themes with quotes rendered as side-bordered blocks
- Filename: ASCII slug for Content-Disposition + UTF-8 filename* fallback
- api.ts: handles PDF blob, decodes UTF-8 filename*, 120s timeout
- AnalyticsPanel, ThemeHighlighter: light-mode colors → dark tokens

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:40:54 +01:00
Vadym Samoilenko
0a419eeee1 fix(ui+billing): dark theme for session page + no double-charge on restart
FocusGroupSession.tsx: replace bg-slate-50 with bg-background so the page
matches the rest of the dark UI; swap remaining text-slate-* for muted tokens.

focus_group_ai.py: before deducting 40 cr for an AI mode run, check if this
focus group already has a charge within the last 4 hours. Skip deduction if
found — prevents double-billing when the server restarts mid-session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:26:21 +01:00
Vadym Samoilenko
56efcc38f5 fix(ui): MentionInput — invisible text on dark theme
bg-white + white foreground = text hidden. Replaced with CSS variable
tokens: bg-background, text-foreground, border-input, placeholder:text-muted-foreground.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:18:33 +01:00
Vadym Samoilenko
f626d2534b fix(ui): dark-theme colors in discussion session — replace light-mode Tailwind classes
ChatMessage, DiscussionPanel, ReasoningPanel, ModeSwitchMarker: swap all
hard-coded light-mode colors (bg-slate-*, text-slate-7xx, bg-amber-50,
bg-gray-*, border-gray-200, bg-white) for dark-theme tokens
(text-foreground, text-muted-foreground, border-border, bg-card, bg-white/5,
bg-amber-500/10) that match the existing dark glass-panel design system.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:13:48 +01:00
Vadym Samoilenko
e156ef897e fix(i18n): translate Personas page — all hardcoded EN strings replaced with t() keys
Adds 74 keys to synthetic_users section in ru/uk/en locales.
Covers: page title/subtitle, buttons, tabs (AI Recruiter / Manual Creation),
AIRecruiterForm fields, UserCreator form labels, FolderTree labels,
InputStrengthIndicator hints, filter/delete/move dialogs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 20:05:39 +01:00
Vadym Samoilenko
faf60b4b71 fix(i18n): popular badge whitespace-nowrap — prevents RU text wrapping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 19:43:48 +01:00
Vadym Samoilenko
c596e47476 fix(i18n): mobile menu sign_out key + nav translations for user section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 19:39:03 +01:00
Vadym Samoilenko
bc4c3afebb fix(ui): mobile menu missing user section + logout, fix OG image
- AppLayout: add user info + 'My account' + 'Sign out' to mobile hamburger menu
- Header: add 'Sign out' button to authenticated mobile menu (landing page)
- dist/og-image.png: sync with public/ (Cohorta-branded, replaces Lovable image)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 19:37:49 +01:00
Vadym Samoilenko
84dcfc46b9 fix(auth): registration bypasses email gate — add setAuth() to properly init React state
Previously Register.tsx wrote auth_token directly to localStorage without
calling setToken/setUser in AuthContext. This made isAuthenticated=true
(computed from localStorage) while user=null (React state), so ProtectedRoute's
user.email_verified check was never reached.

- AuthContext: add setAuth(token, userData) that syncs both localStorage and React state
- Register: use setAuth() instead of direct localStorage write
- Register: add !registered guard to auto-redirect effect to prevent bypassing check-inbox screen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 19:32:29 +01:00
Vadym Samoilenko
2ae08f6d0a fix(mobile): admin CreditSettings + AIConfig form grids responsive 2026-05-24 19:08:35 +01:00
Vadym Samoilenko
8edc291b59 fix(mobile): responsive tabs grid-cols, FocusGroupSession fixed height, MyUsage table scroll 2026-05-24 19:07:44 +01:00
Vadym Samoilenko
aac5764999 feat(auth): hard-gate routes behind email verification — VerifyRequired page + ProtectedRoute guard 2026-05-24 18:54:36 +01:00
Vadym Samoilenko
9659c58286 fix(auth): banner persists after email verification — add refreshUser() to flush sessionStorage cache 2026-05-24 18:49:35 +01:00
Vadym Samoilenko
3f7f6fb621 fix(backend): get_settings fills missing DEFAULTS keys for stale config documents 2026-05-24 18:08:02 +01:00
Vadym Samoilenko
9eca0fbd52 fix(i18n): dashboard welcome greeting — remove {{name}} interpolation dupe 2026-05-24 18:07:59 +01:00
Vadym Samoilenko
0a90a285e7 feat(i18n): UserCreator + FocusGroupModerator tab labels — EN/UK/RU 2026-05-24 18:07:56 +01:00
Vadym Samoilenko
8ec9a123b0 feat(i18n): landing LivePreview + UseCases sections — EN/UK/RU 2026-05-24 18:07:53 +01:00
Vadym Samoilenko
8df1e6c5b1 fix(landing): credit tooltip — Tooltip → Popover for click-triggered behaviour 2026-05-24 18:07:49 +01:00
Vadym Samoilenko
769e4323a2 feat(ui): language switcher — active label + dropdown for others 2026-05-24 18:07:46 +01:00
Vadym Samoilenko
ab28ebd765 feat(i18n): login, register, focus group setup, persona editor — EN/UK/RU
- Login.tsx: auth.login_heading, login_subtitle, email_or_username_label,
  password_label, signing_in, sign_in_button, no_account_q, create_one_free
- Register.tsx: full form labels, check-inbox screen, toast messages,
  data processing consent, plan badge credits (all 3 locales)
- SetupTab.tsx: session name, research brief, topics, duration, model,
  thinking depth, verbosity, materials — new focus_group_setup namespace
- PersonaEditor.tsx: all form labels, section headings, OCEAN traits,
  goals/frustrations/motivations, think-feel-do, scenarios, toast messages —
  new persona_editor namespace
- Locale files: 80+ new keys across EN/UK/RU

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 17:39:44 +01:00
Vadym Samoilenko
802c004ca4 feat(i18n): full EN/UK/RU coverage — app pages, landing, AppLayout switcher
- Add LanguageSwitcher to AppLayout header (all authenticated pages)
- Fix Pricing tooltip: remove nested TooltipProvider (broken hover popup)
- Landing: FAQ, HowItWorks, Comparison, Testimonials, FeatureGrid, Footer
- App pages: Dashboard, Admin, MyUsage, Billing
- Toast messages: FocusGroups, SyntheticUsers, FocusGroupSession (28 toasts)
- New namespaces: faq, how_it_works, comparison, testimonials, features,
  footer, dashboard, admin, usage, billing, focus_groups, synthetic_users,
  focus_group_session — 130+ keys across EN/UK/RU

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 16:23:36 +01:00
Vadym Samoilenko
8e763cca75 fix: add .dockerignore to exclude node_modules from build context
Without .dockerignore, COPY . . was overwriting the npm ci install
with the server's stale node_modules, causing Rollup to pick up the
wrong @radix-ui/react-slot version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:34:18 +01:00
Vadym Samoilenko
8cc643b331 fix: npm overrides for @radix-ui/react-slot — dedupe all nested to 1.2.4
Fixes Docker build failure: react-progress brought react-primitive@2.1.4
which needs createSlot from react-slot >=1.2.0. npm overrides ensure
single 1.2.4 copy is used everywhere, no nested conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:32:17 +01:00