Oversight admins can now create campaigns, upload proofs, and
flag/resolve issues when they have an agency assigned. They retain
all existing cross-agency read access for analytics, auditing, and
user management. Oversight admins without an agency see a read-only
campaigns view.
Changes:
- Add oversight_admin to canWrite permission in UserContext
- Guard readOnly for oversight_admin without agency in App.tsx
- Remove oversight_admin block from require_write_access dependency
- Remove WebSocket oversight_admin upload block in main.py
- Require agency for oversight_admin campaign creation in routes.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace entire Barclays colour palette (navy #1A2142, lime #C3FB5A, violet
#7A0FF9) with Oliver brand tokens: black #1A1A1A, gold #FFCB05, orange
#FF5C00, azure #0487B6, sky #5DF5EA, grey #EFEFEF, green #09821F.
- Switch font from Inter/Barclays Effra to Arial (system font)
- Add new Oliver logo asset (BAR-ModComms-logo-v4.png)
- Sidebar: black background, new logo, azure active state
- Hero: orange "Intelligent Review" text, hide AI-Powered tagline
- Hide ChecksOverview on Home page per Oliver design
- Toast notification: orange background with black text
- All tables: sky headers, alternating white/grey rows
- Campaign badges: gold "In Progress", green "Completed"
- Analytics: grey KPI cards, sky accent on Key Insight, oliver trend colours
- All buttons: azure fill, pill-shaped (rounded-full)
- All tabs/toggles/dropdowns: azure accent colour
- Update HTML title to "Mod Comms - Intelligent Review"
- Default border radius set to 10px
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: thread on_fallback callback through analysis chain
(gemini_service → agents → analysis_service → handlers). The handler
sends a 'model_fallback' WebSocket message exactly once per analysis
when the primary model is unavailable.
Frontend: handle 'model_fallback' WS message and show a dismissible
yellow toast at the bottom of the screen with an 8-second auto-dismiss.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change user-facing strings from American to British English: analyze→analyse,
analyzing→analysing, optimized→optimised, color→colour, analyzes→analyses,
synthesizes→synthesises, optimization→optimisation. Code identifiers, status
enums, and developer-facing messages are intentionally unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New GET /analytics/by-agency endpoint groups review metrics by agency.
The Analytics page now shows a sortable agency performance table with
pass rates, failures, errors, and legal review counts for each agency.
Only visible to super_admin and oversight_admin users. Selected agency
row is highlighted when the AgencyFilterBar is active.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handleNavigateToAuditedItem silently failed because campaignProofs are
loaded lazily (only when viewing a campaign), and errors went to a
setError() state that was never rendered. Now the function fetches and
caches proofs on demand when not already loaded, and uses alert() for
visible error messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Unassigned (no agency) non-admin users previously saw ALL campaigns due to
a truthiness check that treated None agency_id as "no filter". This was a
security bug — they should see NO campaigns and be blocked from creating them.
Backend: Add _NO_AGENCY sentinel to distinguish "no filter" from "no agency",
add early-returns at all 5 list/analytics endpoints, fix _check_campaign_access
to explicitly reject unassigned users, and block campaign creation with 403.
Frontend: Add isUnassigned boolean to UserContext, show informational empty
state on Campaigns view, and reinforce readOnly for defense-in-depth.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add UserRole type and AppUser interface to types.ts
- Create UserContext with useUser() hook providing role-based permission booleans
- Split App into App (auth wrapper) + AppContent (uses UserContext)
- Update Sidebar to filter nav items by UserRole instead of boolean isAdmin
- Add User Management nav item (super_admin only)
- Add AgencyFilterBar component for oversight_admin/super_admin session-level filtering
- Pass agencyId to getCampaigns, getAnalytics, audit endpoints in apiService
- Add getMe, getUsers, updateUser, createAgency to apiService
- Build UserManagement page with user table (role/agency dropdowns) and agency CRUD
- Add readOnly prop to Campaigns (hides create/delete/status-toggle for oversight_admin)
- Add readOnly prop to Settings (disables all ManagementCards, shows view-only banner)
- Pass agencyId to Analytics component for filtered data
- Update urlState with Knowledge Base and User Management views
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Thread resolvedItems from App → Campaigns → ProofDetailView → FeedbackReport
→ SubReviewCard so that issues previously marked as resolved are restored
from the database on page load instead of resetting to actionable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DB-loaded proofs don't have a tempId, so the retry handler's
proof.tempId === tempId check matched all DB proofs (both undefined).
Now the handler uses a matchProof helper that checks both tempId and
_id, and call sites pass proof.tempId || proof._id as the identifier.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Style Proof Name column in Auditing Flags tab as blue clickable link
- Replace browser alert() with in-app success message in flag modal that auto-closes after 2s
- Add filled prop to FlagIcon for solid red variant when flagged
- Thread flaggedItems from App → Campaigns → ProofDetailView → FeedbackReport
- Show solid red flag icon on SubReviewCard and LeadAgentSummary when agent has been flagged
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update Tailwind config with new color tokens (primary-blue, active-blue,
electric-violet, lime, grey-100/300/700/900, success, warning, error)
- Add Inter font from Google Fonts as Barclays Effra alternative
- Update Sidebar with primary-blue background and white active state
- Update Hero with electric-violet accent and pill-shaped buttons
- Update all tables with lime (#C3FB5A) header backgrounds
- Implement alternating row colors (white/grey-100) on tables
- Update status badges: In Progress (amber), Completed (green)
- Update tabs with active-blue underline styling
- Apply 10px border radius to cards and containers
- Update button styling to pill-shaped with active-blue
- Update input/dropdown borders to grey-700 with 2px
- Update selected state highlighting to info-light (#E7F0FB)
- Update FeedbackReport RAG status colors to new design system
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add CampaignDeleteConfirmationModal for campaign deletion confirmation
- Add checkbox selection column to CampaignList with select all/indeterminate state
- Add actions column with trash icon for single campaign deletion
- Add bulk actions bar showing selected count with Clear/Delete buttons
- Add handleDeleteCampaign handler in App.tsx using apiService.deleteCampaign
- Pass onDeleteCampaign prop through Campaigns component chain
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add brand field to AnalyzeProofOptions interface and WebSocket message
- Pass campaign's brandGuidelines to analyzeProof in App.tsx (upload & retry)
- Extract brand from WebSocket message in handlers.py and pass to analysis
- Update AnalysisService.analyze_proof to accept brand parameter
- Refactor BrandAgent to dynamically select brand spec based on brand param
- Add get_barclays_brand_spec() method to ReferenceDocsService (placeholder)
The brand agent now uses the appropriate specification (Barclaycard spec or
Barclays spec when available) based on the campaign's brandGuidelines setting.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add URL utility functions for parsing and building URL state
- Initialize app state from URL parameters on page load
- Sync navigation changes to URL via browser history API
- Handle browser back/forward navigation with popstate listener
- Support deep linking to campaigns and proofs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GET /files/{storage_key:path} endpoint to serve stored files
- Add getFile() method to apiService to fetch files from backend
- Update convertProofToFrontend() to preserve fileStorageKey
- Update handleRetryAnalysis() to fetch file from backend when not in memory
- Update handleDownload() to download original file instead of thumbnail
After page refresh, the retry button now fetches the original file from
backend storage using the fileStorageKey, allowing failed proofs to be
reprocessed. The Download Asset button also now downloads the original
uploaded file rather than the preview thumbnail.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend logging:
- Log channel, sub-channel, and proof type counts in get_all_hierarchical()
- Log Meta proof types specifically
- Log API response for Social.Meta
Frontend logging:
- Log raw API response in apiService
- Log dropdown options in App.tsx when loaded
- Log available proof types in UploadProofModal when channel/subchannel selected
This will help diagnose why Meta proof types are not appearing on staging.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use optimistic update pattern to immediately reflect status changes
in the UI, with rollback on API error.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add agencyLead state and form field to CreateCampaignModal
- Remove disabled attribute and hardcoded value from Agency Lead input
- Pass agency_lead to backend API when creating campaigns
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend:
- Update migration to seed agencies (OLIVER Agency, Barclays, etc.)
- Seed brand guidelines (Barclays, Barclaycard) in dropdown_options
- Seed channel/sub-channel/proof-type hierarchy
- Add /api/agencies endpoint to list all agencies
- Update DropdownOptionsResponse to include brand_guidelines
- Update dropdown repository to return brand guidelines
Frontend:
- Update DropdownOptions interface to include brandGuidelines
- CreateCampaignModal now receives brand guidelines from API
- Settings UsersTab fetches agencies from API instead of hardcoded list
- Add getAgencies() method to apiService
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace all localStorage-based state management with API calls
- Load campaigns, proofs, and audit items from database
- Persist proof analysis results to database via WebSocket
- Add dropdown options CRUD API endpoints (channels, sub-channels, proof types)
- Create DropdownRepository for managing dropdown options
- Update Analytics component to fetch data from API
- Remove demo data and localStorage persistence code
Frontend changes:
- App.tsx: Initialize apiService with MSAL, use API for all CRUD operations
- apiService.ts: Add dropdown options API methods
- Analytics.tsx: Fetch stats from /api/analytics
Backend changes:
- New dropdown_repository.py for dropdown CRUD
- routes.py: Add 7 dropdown endpoints
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add userName and userEmail props to Sidebar component
- Pass user info from MSAL to Sidebar in App.tsx
- Replace hardcoded "Steve O'Donoghue" with actual logged-in user
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Frontend:
- Add @azure/msal-browser and @azure/msal-react packages
- Create authConfig.ts with MSAL configuration for PKCE flow
- Create authService.ts for token acquisition and user info
- Wrap App with MsalProvider in index.tsx
- Replace dummy login with real MSAL loginPopup() in Login.tsx
- Update App.tsx to use useIsAuthenticated/useMsal hooks
- Update Profile.tsx to display real user data from claims
- Update geminiService.ts to include access_token in WebSocket messages
- Update WIPReviewer.tsx to pass msalInstance for auth
Backend:
- Add python-jose and httpx dependencies for JWT verification
- Create auth_service.py with Azure AD JWKS fetching and token verification
- Create auth.py FastAPI dependency for protected REST endpoints
- Update main.py to verify tokens on WebSocket and protect /info endpoint
- Add AZURE_TENANT_ID, AZURE_CLIENT_ID, DISABLE_AUTH to config
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>