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>
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>
- 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>
- 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>
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>
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>
- 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>
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>
Was hardcoding /brief; now navigates to / which the index route
redirects to /conversations/new.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
- 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>
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>
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>
- 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>
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>
- 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>
- 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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>