Brings the new app to full parity with the original L'Oréal SPA and
beyond. Backend 59/59 tests (was 40, +19). Frontend typecheck/lint/build
clean. Main entry chunk 15.76 KB gz (budget 30 KB).
Backend — new endpoints + services:
- POST /api/deliverable/parse — parse Deliverable Summary CSV/XLSX
- POST /api/projectsummary/parse — parse Project Summary CSV/XLSX
- GET /api/timelog/rows — paginated, searchable, sortable view
over the parsed Zoho upload
- GET /api/forecast — 4-week pipeline + capacity decision
- GET /api/project-types — hours/asset, duration, concentration
per project type + auto-insights
- POST /api/chat — Claude API proxy. 503s gracefully
when ANTHROPIC_API_KEY is unset.
Prompt-cached system prompt;
rate-limited 20/min/IP.
- GET /api/auth/me now returns role.
Backend — services:
- zoho_parse.py: extracts ~20 fields (brand, division, hub, userRole,
projectType, assetCount, projectStatus, project start/end dates,
userAgency, employingCompany, sageJobProfile, …) with back-compat
aliases so existing callers keep working.
- parse_store.py: in-process TTL-cached registry of parsed uploads keyed
by content hash. Lets endpoints reference an upload without re-sending it.
- forecast.py: working-day overlap math, exit-rate, weekly throughput
baseline, capacity decision string mirroring the original wording.
- project_types.py: per-type aggregation + concentration-risk insights.
- timelog_filters.py: server-side filter by brands/divisions/hubs/roles.
- ai_context.py: builds the dashboard context block fed to Claude.
Frontend — new pages + components:
- pages/Forecast.tsx — ComposedChart (stacked bars + line)
+ capacity-decision banner + table
- pages/ProjectTypeSummary.tsx — sortable table + small trend chart
- pages/TimeLogDetail.tsx — virtualised, searchable, sortable
view over all parsed timelog rows
- components/ChatView.tsx — floating side panel with Claude.
6 preset prompts mirroring the
original. Visible only for roles
with chat access.
- components/ChatToggle.tsx — bottom-right FAB.
- components/StatsBar.tsx — always-visible: Time Entries /
People / Projects / Total Hours /
Date Range.
- hooks/useDataContext.tsx — single source of truth for filter
state + parsed upload + filter
dimensions (brands/divs/hubs/
roles derived from uploads).
Frontend — modified:
- App.tsx, Navbar.tsx — 7 tabs + role gating per the
original TAB_ACCESS matrix.
- hooks/useAuth.tsx — role + canAccess(tab).
- lib/filters.ts, FilterBar.tsx — Brand / Division / Hub / Role
multiselects added (additive — keep
Department / Name / Billing).
- pages/Department, Resourcing,
Bookings, Tutorial.tsx — wired into DataContext; tutorial
is now a single 9-step global tour
mirroring the original's narrative.
Config:
- backend/.env.example: ADMIN_ROLE, ANTHROPIC_API_KEY, ANTHROPIC_MODEL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
50 lines
1.9 KiB
Text
50 lines
1.9 KiB
Text
# Copy to .env and fill in. Never commit .env.
|
|
|
|
# --- Airtable ---
|
|
# Personal Access Token (read-only scope on the base below).
|
|
# Generate at https://airtable.com/create/tokens — scopes: data.records:read on base appoByydxIQANKtSh, tables Resource + Booking Resource.
|
|
AIRTABLE_PAT=
|
|
AIRTABLE_BASE_ID=appoByydxIQANKtSh
|
|
|
|
# --- Session / auth ---
|
|
# Random 32+ bytes. Generate with: python -c "import secrets; print(secrets.token_urlsafe(48))"
|
|
SESSION_SECRET=
|
|
|
|
# Local admin (used while AUTH_MODE=local).
|
|
# Generate hash with: python -c "from passlib.hash import bcrypt; print(bcrypt.hash('your-password-here'))"
|
|
# IMPORTANT: wrap the hash in SINGLE QUOTES — bcrypt hashes contain $ characters
|
|
# that docker-compose will otherwise interpret as variable substitutions.
|
|
ADMIN_USERNAME=admin
|
|
ADMIN_PASSWORD_BCRYPT=''
|
|
|
|
# AUTH_MODE: local (v1) or azure (v2 — not yet implemented).
|
|
AUTH_MODE=local
|
|
|
|
# Role for the local-admin user. Drives tab visibility per the RBAC matrix.
|
|
# One of: global-lead | dept-lead | forecast
|
|
# global-lead → full access (all tabs + AI chat)
|
|
# dept-lead → Department + Tutorial + AI chat
|
|
# forecast → Forecast tab only, no AI chat (external/client view)
|
|
ADMIN_ROLE=global-lead
|
|
|
|
# --- Anthropic (AI chat) ---
|
|
# Leave ANTHROPIC_API_KEY empty to disable chat (the /api/chat endpoint
|
|
# returns a friendly 503 in that case). Get a key from console.anthropic.com.
|
|
ANTHROPIC_API_KEY=
|
|
ANTHROPIC_MODEL=claude-sonnet-4-6
|
|
|
|
# Skip auth entirely in local dev. NEVER set true in production.
|
|
DEV_AUTH_BYPASS=false
|
|
|
|
# --- Server ---
|
|
# Host port to bind the container to. Auto-picked by deploy.sh from 8200-8299 and persisted here.
|
|
UTILISATION_DEPT_PORT=8200
|
|
|
|
# Optional Sentry-style overrides for cache TTLs (seconds).
|
|
CACHE_TTL_RESOURCES=600
|
|
CACHE_TTL_BOOKINGS=60
|
|
CACHE_TTL_META=600
|
|
|
|
# --- Azure AD (unused in v1, present for v2 swap) ---
|
|
AZURE_TENANT_ID=
|
|
AZURE_CLIENT_ID=
|