- HelpPage: remove unused navigate import
- AdminClientsPage: align state type with API response (hasCustomTemplate vs hasCustom)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers: overview, sheets, AI commands, brief extraction, export CSV,
export templates, admin (clients, dropdowns, users), and login/emergency
access. Admin-only sections are hidden for regular users.
Accessible via sidebar "Help" link at /help.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Non-JWT tokens (like emergency access tokens) were treated as expired
by isTokenExpired(), triggering a MSAL silent refresh that fails and
clears the token. Fix: non-JWT tokens are treated as never-expired and
skip the 401-retry refresh path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously AuthGate called loginRedirect() immediately when no MSAL
accounts were present, bypassing the LoginPage entirely. Now it stops
and lets the user choose — enabling emergency token login and explicit
SSO sign-in via the LoginPage button.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When EMERGENCY_TOKEN is set in .env, a Bearer token matching it grants
admin access without going through Azure AD / MSAL. Useful when 2FA or
SSO is unavailable. Token is compared in constant-time to prevent timing
attacks. If EMERGENCY_TOKEN is empty (default), the feature is disabled.
Frontend: small "Emergency access" link on login page opens a token input.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Voice: switch CommandBar from PTT (hold) to toggle mode (click), update
to use new useSpeechRecognition toggle/listening API with auto-restart
- Mapping detection: new detect_excel_mapping() reads row 1 headers and
auto-detects name/status/media columns via keyword matching
- Mapping endpoints: POST /api/admin/dropdowns/detect-mapping and
/api/admin/clients/{id}/dropdowns/detect-mapping
- Upload/preview now accept name_col/status_col/media_col form fields to
apply a confirmed mapping override
- Frontend: ColumnMappingStep component shows detected columns + 5-row
sample for confirmation before upload
- AdminDropdownsPage and AdminClientsPage use 3-stage flow:
detect → confirm mapping → preview all → apply
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
clearCache() removes MSAL tokens locally; next visit auto-SSOs silently
if the user is still logged into Microsoft in the browser.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- registerTokenRefresher() lets AuthGate inject acquireTokenSilent into axios
- Request interceptor checks JWT exp, proactively refreshes if within 60s of expiry
- Response interceptor retries on 401 with a fresh token, reloads on double failure
- Previously the idToken was cached once in sessionStorage and never refreshed,
causing all requests to fail after 1 hour with "Token expired"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GEMINI_MODEL for AI commands: gemini-2.0-flash-exp → gemini-3-flash-preview
- Language/Country: handle plain 2-letter codes (EN→Language, UK→Country)
and "EN-UK" split format; previously only split format worked
- Handsontable black screen: add min-h-0 on flex-1 container so height:100%
resolves correctly inside the flexbox chain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BriefReviewPage: call loadSheet() after importDeliverables so store
is refreshed before navigation — fixes 0 items on Sheet page
- Google model: gemini-3.1-pro-preview → gemini-2.5-pro-preview-03-25
(old model name was invalid, caused API errors)
- docker-compose default: gemini-2.0-flash-exp → gemini-2.5-pro-preview-03-25
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend Job interface used snake_case (file_name, file_size, progress_pct,
step_label, provider_updates, result_csv_url, created_at) but backend
returns camelCase (fileName, fileSize, progressPct, stepLabel,
providerUpdates, resultCsvUrl, createdAt) — causing all fields to be
undefined and showing 'NaN MB', broken progress bar, empty labels.
Updated types/index.ts Job, ProviderUpdate, JobSummary interfaces and
JobProgressCard.tsx to use the correct camelCase field names.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three bugs fixed:
1. api/jobs.ts: remove manual Content-Type header on FormData upload.
Setting it without the multipart boundary caused Quart to reject the
request body — the root cause of brief upload failures.
2. progress.py: include full job.to_dict() in job.progress / job.completed
/ job.failed WebSocket messages. Frontend checks msg.job to call
updateJob() — without it, job cards never updated in real-time.
3. AppShell: move useWebSocket() here from BriefUploadPage so the WS
connection persists across all pages, not just the upload page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.css: bump text-secondary #888→#b0b0b0, text-muted #555→#787878,
border slightly lighter for better readability on dark backgrounds
- TopBar: add '← Dashboard' back button on all non-dashboard pages
- Sidebar: make AC logo clickable to navigate home
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
loading starts as true in the store, so the previous condition
`!user && !loading` prevented fetchMe from ever being called after
the Azure AD redirect — causing a permanent Loading spinner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- msal_auth.py: replace verify_signature=False with real JWKS verification
using PyJWKClient; validates RS256 signature, aud=clientId, issuer v2.0
- App.tsx: split DEV bypass from empty-accounts case — in production,
accounts.length === 0 now correctly triggers loginRedirect instead of
calling fetchMe without a token
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>