Commit graph

46 commits

Author SHA1 Message Date
Vadym Samoilenko
a3680f6c64 Fix logo URL path: ensure slash between BASE_URL and filename
BASE_URL may not have a trailing slash (e.g. /barclays-banner-builder),
causing the logo src to be /barclays-banner-buildercopygen-barclays-logo.png.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:21:16 +01:00
Vadym Samoilenko
b17e507b7a Fix logo: convert JPEG-disguised-as-PNG to proper PNG
File had .png extension but was JPEG data — browser rejected it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:15:28 +01:00
Vadym Samoilenko
3305a5e8f4 Disable auth for testing (AUTH_BYPASS=True)
Frontend: ProtectedRoute always passes through.
Backend: get_current_user returns first admin user when AUTH_BYPASS=True.
Revert both files before go-live.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:13:31 +01:00
Vadym Samoilenko
4759e3325b Fix UX flow bugs, enforce FCA character limits, UI cleanup
- Fix initSelection race: pass messages directly to loadBannerSet, not via stale closure
- Fix hooks order in ExportPage: move pdfError useState to top
- Restore step-1 variant selection in VariantsGrid from journey store
- ConversationLanding: show error on API failure instead of hanging
- Replace window.confirm delete with inline confirm/cancel buttons
- Add "← Back to brief" link in VariantsGrid (fresh sessions only)
- PDF: add Regenerate button after contact sheet is ready
- CSV download gated behind approval status (FCA compliance)
- Add hasLimitViolation() helper in copyLimits.ts
- Over-limit variants highlighted in orange in VariantsGrid cards
- Export button blocked when any selected variant exceeds FCA limits
- ExportPage shows per-variant limit warnings in copy summary
- Remove BriefEditor page and /brief route (replaced by chat flow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 15:50:16 +01:00
Vadym Samoilenko
24713ce5e6 Add Oliver rebrand, character counts, delete variants, 6-format spec validation
- Rebrand LoginPage logo to Oliver design system
- Move Admin nav link to bottom-left user area
- Remove "Ask AI to improve" button from step 2 (VariantsGrid)
- Add CharCount component with copy limits across prompt, edit, and export views
- Add delete variant with confirmation dialog on step 2 cards
- Add theme palette migration (0007_theme_palette.py)
- Add copyLimits.ts and themes.ts libs
- Remove unused BannerEditor.tsx page and old logo PNG
- Add AGENTS.md project entry point
- Add docs/ directory

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 15:35:25 +01:00
Vadym Samoilenko
0f66fab43a Rebrand product shell to Oliver design system
Replace Barclays-branded UI with Oliver tokens, Modcomms-style dark
sidebar layout, and Arial typography across all screens. Adds theme
colour picker so users can change banner copy/background colour directly
from the variants grid and banner editor.

- tailwind.config.ts: oliver-* tokens replace barclays-* tokens, Arial font, 10px radii
- index.css: :root HSL vars updated to Oliver palette
- Layout.tsx: horizontal top nav → 220px dark sidebar with sticky stepper
- stepper.tsx: Oliver Green/Azure/Grey states, 10px circles, opacity-40 locked steps
- All 8 page files: oliver-* classes, pill buttons, 10px card radii
- BannerEditor + VariantsGrid: theme colour swatches (navy/sky-blue/yellow/lime/teal)
- public/CopyGenBannerAgent_RFA.png: placeholder logo added

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 22:56:33 +01:00
Vadym Samoilenko
a3bbd9dc52 Remove tsconfig.tsbuildinfo from tracking, add to .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:37:24 +01:00
Vadym Samoilenko
ff8a7d7cfc Fix remaining AC gaps: PDF renders actual banners, CSV complete, approval flow, history page
PDF contact sheet (exporter.py):
- Renders each variant as a branded HTML banner (correct bg colour, white pill
  CTA, photo-left/right/top layouts, centred icon for LG1, link CTA for SM2/MD2/MD3)
- Uses actual format dimensions (SM2 109px tall through LG2 333px) scaled to fit A4

CSV (exporter.py):
- Added CTA Secondary column
- Added Icon Name column (looked up from Icon table)
- Added Format + Size (px) columns
- Renamed CTA → CTA Primary for clarity

Approval flow:
- PATCH /api/banner-sets/:id/status endpoint (draft/approved/exported)
- ExportPage shows approval status badge + "Mark as approved" button
- CSV download auto-marks banner set as exported

History page:
- Now shows conversations from /api/conversations instead of briefs
- Correctly links to /conversations/:id
- "New campaign" → /conversations/new

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:34:56 +01:00
Vadym Samoilenko
2fe3eb75c7 Implement 6-format banner spec, fix CTA colours, add custom sizes
- BannerPreview: 6 real formats (SM2/MD1/MD2/MD3/LG1/LG2) all 312px wide;
  white-pill CTA with theme-colour text (was inverted); navy bg #000063 per
  Barclays spec; link-style CTA for SM2/MD2/MD3; centred icon for LG1;
  photo layouts: photo-left (SM2/MD2), photo-right (MD3), photo-top (LG2)
- Custom size: POST /api/banner-sets/:id/custom-size creates Custom variants
  from each pair's LG copy at user-specified px dimensions; BannerEditor has
  +Custom size form with W×H inputs
- Format limits updated in copy_generation.py (SM2 no body, MD 32/64, LG 64/128)
- tasks.py _persist_banner_set generates all 6 standard formats per copy set
- Migration 0006 adds custom_width/custom_height columns to banner_variants
- VariantsGrid/ChatBrief canProceed uses SMALL/LARGE format sets (not "Medium"/"Large")
- tsc --noEmit clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:28:56 +01:00
Vadym Samoilenko
099f6ca81d Fix refine intent: pass dict to refine_variant_copy, not BannerCopy
refine_variant_copy expects current_copy as a plain dict with
short_title/long_body/cta keys; was incorrectly passing a BannerCopy
dataclass which caused the worker to hang on attribute access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:10:29 +01:00
Vadym Samoilenko
44a2109d42 Fix LoginPage redirect to land on chat interface
Was hardcoding /brief; now navigates to / which the index route
redirects to /conversations/new.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:01:16 +01:00
Vadym Samoilenko
7e82a535a9 Add conversational brief interface (AC1 chat-style)
Replaces the one-shot form with a Copygen-style chat at
/conversations/:id. Each turn is classified as generate / refine /
clarify by a lightweight LLM intent router; generate and refine intents
enqueue an RQ job that produces a new BannerSet, while clarify returns
an inline reply without touching banners.

New backend:
- Conversation + ConversationMessage models + migration 0005
- intent_router service (chat_structured, 3-intent schema)
- chat_turn RQ task with _persist_banner_set helper extracted from
  _generate_copy_async for reuse
- /api/conversations CRUD + POST /messages endpoint
- JobType.CHAT_TURN added

New frontend:
- ChatBrief page: message bubbles, inline BannerPreview cards with
  checkbox selection and "Open banner editor" CTA (same Medium+Large
  validation rule as VariantsGrid)
- ConversationLanding: /conversations/new creates and redirects
- conversationId added to journey store
- "New Brief" nav now points to /conversations/new
- Default route redirects to /conversations/new

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 21:53:17 +01:00
Vadym Samoilenko
d9456d80be Show character counts in variant card display mode (AC4)
Title, Body, CTA and secondary-CTA counts are now visible before
entering edit mode, using the existing CharCount component and
LIMITS/CTA_MAX constants. Counts turn red when a field exceeds its
per-banner-type limit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 20:55:30 +01:00
Vadym Samoilenko
b795240503 Require at least one Medium and one Large before proceeding
canProceed flag gates both the banner editor and export buttons;
amber helper text explains the rule when disabled; selected card
border upgraded to ring-1 for stronger contrast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 20:52:11 +01:00
Vadym Samoilenko
153df3806c AC3 in-situ banner visualisation — Medium/Large model, icons, themes, BannerPreview
- Fix icon 404s: mount StaticFiles at /api/illustrations in FastAPI so Vite
  proxy covers dev and Apache /api/ Location covers prod; no new Apache rules
- Fix icon URL construction in icons.py, banner_sets.py, banner_variants.py to
  use /api/illustrations/ prefix
- Rename assets/illustrations/Infrastruture → Infrastructure (API was already
  correct; disk folder had a typo causing the category to return zero icons)
- Migrate brief model to Medium/Large only (drop display-ad pixel-size defaults)
- Add theme (navy/sky-blue/yellow/lime/teal) and pair_id columns to
  banner_variants with Alembic migration 0004
- Expand short_title column from VARCHAR(32) to VARCHAR(64) for Large banner titles
- Redesign LLM schema: each CopyVariant is now a Medium+Large pair with
  separate copy respecting per-size character limits (32/64 vs 64/128)
- LLM also picks icon_keyword and brand theme per variant
- Add icon_matcher.py: pgvector cosine similarity on icon embeddings to
  auto-select the best icon for each copy variant at generation time
- Add recommend_for_brief() to DAM client; worker pre-selects one DAM image
  shared between Medium and Large rows via pair_id
- Fix _validate_limits() bug: now called with correct banner_type so per-size
  char limits are actually enforced
- Build BannerPreview.tsx: shared in-situ component with brand theme colours,
  correct aspect ratios (Medium 1:1 174×174, Large 16:9 312×175), DAM image,
  icon, copy, and CTA(s)
- Wire BannerPreview into VariantsGrid (thumbnail atop each card) and
  BannerEditor (sidebar thumbnails + main preview)
- BannerEditor: fix icon_url resolution (falls back to icons list when
  icon_url missing from API response); sync DAM image change to paired variant
- Add Barclays brand colour tokens to Tailwind: barclays-yellow, barclays-lime,
  barclays-teal, barclays-navy
- Extract shared Variant/DamAsset/Icon types to frontend/src/types/banner.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 20:30:43 +01:00
Vadym Samoilenko
3d2893a795 Fix export to respect selected variants — PDF and CSV now honour selection
contact-sheet POST accepts {variant_ids} list; worker passes it through to
render_contact_sheet_pdf; CSV endpoint reads ?variant_ids= query param.
ExportPage reads selectedVariantIds from journey store and sends them with
both requests. Export page shows how many variants are included.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:30:46 +01:00
Vadym Samoilenko
29f1a874e5 Fix stale journey store serving old banner-set links after session gap
Added lastUpdatedAt timestamp to journey store. Journey state older than
2 hours is treated as stale: stepper backward-navigation links are hidden
and clickable=false, preventing accidental navigation to a previous
session's banner set. Each meaningful store mutation now refreshes the
timestamp, so active sessions remain unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:24:48 +01:00
Vadym Samoilenko
4a2f160e90 Add AI copy refine, icon preview, variant selection, and PDF layout
- VariantsGrid: AI-assisted copy refinement per variant (feedback textarea
  + Generate with AI calls POST /api/banner-variants/{id}/refine); variant
  selection checkboxes so users continue with only chosen variants
- BannerEditor: icon now renders in preview (top-right overlay); filters
  variant list to only selected variants from previous step
- Backend: POST /api/banner-variants/{id}/refine endpoint (sync OpenAI call);
  icon_url resolved and returned in all variant API responses via DB join
- PDF contact sheet: A4 2-column grid, page-break-inside avoid, ratio section
  headers, icon shown in card corner, Barclays-branded typography

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:17:51 +01:00
Vadym Samoilenko
a12c19158e Add visual progress stepper for campaign creation workflow
Adds a persistent JourneyStepper bar beneath the top nav on all four
creation-flow pages (Prompt → Edit → Banner Editor → Export). Completed
steps are clickable for backward navigation; future steps are grayed out
until reached. Journey state is persisted via a new Zustand store so the
stepper survives page refreshes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:00:36 +01:00
Vadym Samoilenko
7e55caf1bd Align with Barclays banner spec: Medium/Large types, cta_secondary, icon fix
Per Marketing Banner Specs PDF:
- Aspect ratios changed from IAB web sizes to 'Medium' (4:3) and 'Large' (16:9)
- Medium limits: title ≤32, body ≤64; Large limits: title ≤64, body ≤128
- Add cta_secondary field: Large banners support dual CTA (Primary + Secondary)
- Migration 0003: ALTER TABLE banner_variants ADD COLUMN cta_secondary
- copy_generation: per-type limits in prompt, cta_secondary in JSON schema
- BannerEditor: correct preview dimensions (312x234 Medium, 312x175 Large)
- BannerEditor preview shows Secondary CTA button
- VariantsGrid: shows correct limits per type, editable cta_secondary for Large
- Fix icon update: convert icon_id string → UUID before setattr

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:14:29 +01:00
Vadym Samoilenko
e53892013f Replace TOV checkboxes with free-text field (tov_text)
- Add tov_text (nullable Text) column to system_prompts table
- Migration 0002: ALTER TABLE ADD COLUMN tov_text
- AdminPage: checkboxes replaced with textarea — write TOV rules like a prompt
- copy_generation: use tov_text verbatim instead of building from boolean flags
- Legacy boolean flags kept in DB for backward compat, no longer in UI/schemas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:05:51 +01:00
Vadym Samoilenko
cc51b1941f Fix 422 on DAM empty search, add favicon, fix email autocomplete
- DAM search: allow empty q param (min_length=0) so BannerEditor pre-loads all assets
- LoginPage: add autoComplete='username' to email field
- Add Barclays-branded SVG favicon (dark blue + eagle blue bar + B)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:02:22 +01:00
Vadym Samoilenko
440d52ab97 Fix authenticated file downloads: use fetch+blob instead of window.open
window.open() can't send Authorization header — JWT not passed.
New apiClient.download() fetches with auth header, creates blob URL,
triggers <a> click, then revokes the object URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:33:04 +01:00
Vadym Samoilenko
3ae979b6f1 Fix ExportPage: prepend VITE_BASE_PATH to PDF and CSV download URLs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:31:09 +01:00
Vadym Samoilenko
1efe4e3107 BannerEditor: pre-load DAM assets on open (no search needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:23:06 +01:00
Vadym Samoilenko
531e16207b Fix 502: remove EmailStr dep; expand mock DAM to 20 banking assets
- Replace EmailStr with str in UserCreate/UserUpdate (email-validator not installed)
- Remove unused sqlalchemy delete import
- Expand MockDamClient from 8 to 20 assets with keyword metadata
  (savings, mobile, card, mortgage, travel, retirement etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:22:25 +01:00
Vadym Samoilenko
090191cb8d Add user management to admin + prominent TOV display
Backend:
- GET/POST /api/admin/users — list and create users
- PATCH /api/admin/users/{id} — change role or reset password
- DELETE /api/admin/users/{id} — delete (guards: can't delete self or last admin)

Frontend:
- AdminPage refactored into tabs: System Prompt & TOV | Users & Roles
- Active prompt shows TOV toggles, char limits, banned phrases at a glance
- Users tab: table with inline edit (role + password reset) and delete with confirm
- apiClient gets patch() and delete() methods

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:19:47 +01:00
Vadym Samoilenko
1fa21e2f66 Add generation history page and fix split/undefined crashes
- Add GET /api/briefs list endpoint (last 50 briefs with banner sets)
- Add HistoryPage showing all past generations with direct links back to variants
- Add History nav item in Layout
- Guard aspect_ratio.split() in BannerEditor against null/undefined
- Guard CharCount value.length against undefined in VariantsGrid
- Guard bannerSetId in BriefEditor before navigating (shows error if job produced no banner set)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:10:49 +01:00
Vadym Samoilenko
25799265a4 Fix copy_generation: max_tokens → max_completion_tokens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:02:57 +01:00
Vadym Samoilenko
5ab3000da0 Fix OpenAI API: use max_completion_tokens instead of max_tokens
gpt-5.4-mini does not support max_tokens — must use max_completion_tokens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:01:38 +01:00
Vadym Samoilenko
35c6fafcab Add autocomplete=current-password to login form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 13:00:35 +01:00
Vadym Samoilenko
24d9c4f4c3 Fix deploy.sh: target sites-enabled, not sites-available
Apache reads from sites-enabled/ which is a separate file on this server
(not a symlink to sites-available). Previous patches went to the wrong file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:57:52 +01:00
Vadym Samoilenko
14e35efd57 Fix Apache config: use <Location> for ProxyPass, FallbackResource for SPA
Alias directive was intercepting requests before top-level ProxyPass could
match. <Location> blocks are evaluated after Alias and always take priority,
so the API proxy now works correctly. FallbackResource replaces RewriteRule
for simpler React Router fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:53:11 +01:00
Vadym Samoilenko
b856c89b41 Fix deploy.sh: insert Apache Include before other project Includes
Our ProxyPass rules must come before any catch-all rules from other
projects. Insert before the first 'Include /opt/' line, not at end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:43:35 +01:00
Vadym Samoilenko
d8c6dd49a0 Fix login: use apiClient so base path prefix is applied
Hard-coded fetch('/api/auth/token') bypassed API_PREFIX. Added postForm()
to apiClient for OAuth2 form-encoded requests, updated LoginPage to use it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:32:02 +01:00
Vadym Samoilenko
08e2751633 Fix TypeScript build errors: vite-env types and unused variable
Add vite-env.d.ts so import.meta.env is typed correctly.
Prefix unused saving state var with _ to satisfy noUnusedLocals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:29:16 +01:00
Vadym Samoilenko
c253365bb5 Add frontend package-lock.json for npm ci on server
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:28:03 +01:00
Vadym Samoilenko
d0c2b88d7e Add illustration library to assets/illustrations/
1.3MB of PNG icons across 10 categories (Abstract, Architecture, Banking,
Cards, Devices, Documents, Infrastructure, Nature, Objects, World).
Mounted at /assets/illustrations inside the API container for icon indexing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:23:42 +01:00
Vadym Samoilenko
942742181b Mount rag-corpus/ into api and worker containers
ingest_rag.py resolves path as /rag-corpus (3 levels up from /app/scripts/).
Without the mount the container sees an empty /rag-corpus directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:22:03 +01:00
Vadym Samoilenko
3f795c0d35 Replace passlib with bcrypt directly — passlib incompatible with bcrypt>=4.0
passlib hasn't been updated for bcrypt>=4.0 which removed __about__.
Use bcrypt library directly: hashpw/checkpw in auth.py and seed_admin.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:15:06 +01:00
Vadym Samoilenko
9280523bc6 Fix migration: nested Column bug and use Vector(1536) for embeddings
Line 65 had sa.Column("embedding", sa.Column(...)) — nested Column is
invalid. Both icons.embedding and rag_chunks.embedding now use the
correct pgvector Vector(1536) type so cosine similarity search works.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:13:26 +01:00
Vadym Samoilenko
6fa82ccfd5 Fix deploy.sh: correct pyproject.toml path and guard md5sum failures
md5sum exits 1 when a file is missing; set -euo pipefail killed the
script silently. Fixed path backend/pyproject.toml and added || echo
fallback so pipefail can't abort the hash check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:09:51 +01:00
Vadym Samoilenko
dcf517d46b Fix Docker build: hatchling can't find app package before COPY
Add [tool.hatch.build.targets.wheel] packages=["app"] so hatchling knows
where the package is. Create stub app/__init__.py in Dockerfile before
pip install so metadata generation succeeds without the full source tree.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:08:40 +01:00
Vadym Samoilenko
8337bab5f7 Add README with setup, deploy, and project structure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:03:04 +01:00
Vadym Samoilenko
735b2ef141 Add full Sprint 0+1 implementation: Docker, FastAPI, React, RAG, deploy
- Backend: FastAPI + SQLAlchemy async, pgvector RAG, RQ workers, OpenAI gpt-5.4-mini structured output
- Frontend: React 18 + Vite + TypeScript + TailwindCSS + shadcn/ui, job polling pattern (no WebSocket)
- Admin panel: editable SystemPrompt with version history for FCA audit trail
- Deploy: idempotent deploy.sh with hash-based cache, Apache Include fragment, alembic migrations
- Docker: dev + prod compose configs, port 8010 (API) to avoid OliVAS conflict on host

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:01:42 +01:00
Vadym Samoilenko
b9063692db Initial commit 2026-04-17 09:11:26 +00:00