SOURCE UX, Notifications, Settings, Security Hardening

This release closes the top PRD gaps for the SOURCE (influencer/publisher) role, ships a full in-app notification system with real-time delivery, rebuilds the Settings page, and hardens auth + error handling.

2nd release that day

This release closes the top PRD gaps for the SOURCE (influencer/publisher) role, ships a full in-app notification system with real-time delivery, rebuilds the Settings page, and hardens auth + error handling.

Role-Gating & Security

  • Fixed 4 role-leaks: SOURCE can no longer call POST /briefs, GET /influencers/search, GET /advertiser/overview, GET /analytics/performance (all return 403)
  • Suspended-workspace middleware get_current_user now blocks API calls from users whose workspace has is_active=false (SA bypasses). Fixes a real authorization hole.
  • Error boundaries (dashboard)/error.tsx and root error.tsx catch unhandled React errors with friendly UI (previously blank screens on crashes)
  • Fixed /briefs/{id}/recipients 500 caused by SQLAlchemy misuse of User.full_name property

SOURCE Earnings Dashboard

  • New GET /source/earnings-summary aggregates real data from scheduling deals, marketplace deals, forward listings, availability slots, brief recipients
  • Replaces hardcoded demo data in influencer/dashboard and publisher/dashboard
  • Shared SourceEarningsDashboard component with theme parameter (influencer pink/purple vs publisher teal/cyan)
  • Sections: hero stats, KPIs, deals breakdown by source, inventory overview, recent earnings table, empty-state CTAs

Property Verification Pipeline

  • Migration c3d4e5f7a8b9 — adds verification_method, verification_code, verification_attempted_at, last_synced_at, sync_status, sync_error to influencer_properties
  • 4 new endpoints: verify/init, verify/check, sync, verify/status
  • Working methods : DNS-TXT (challenge via adnet-verify=<code> TXT record) and URL-Probe (HTTP GET of profile URL)
  • OAuth scaffolded and returns 501 "OAuth verification not yet configured" — ready to wire when Instagram/YouTube app keys are available
  • New dep: dnspython==2.8.0
  • Verify modal UI on /properties with 3-step flow (method pick → code + instructions → verify)

Media Kit PDF + Public URL

  • GET /marketplace/influencers/{id}/media-kit/pdf — owner/SA downloads A4 PDF via WeasyPrint + Jinja2 template
  • GET /public/media-kit/{slug-or-id} — unauthenticated public route (new layout in frontend/src/app/public/)
  • PDF template: gradient hero, stats, per-property rate cards, red restriction chips, reviews, "Powered by" footer
  • "Download PDF" + "Copy Public Link" buttons on media-kit management page

Weekly Opportunity Digest

  • New service backend/services/scheduling/digest_service.py
  • GET /scheduling/digest/opportunities (user on-demand) + POST /scheduling/admin/digest/run (SA batch, cron-ready)
  • Personalized digest includes upcoming seasonal moments (14-day window), active forward listings, open availability slots
  • In-app notification + HTML+text email template with gradient branding
  • Celery not required — SA endpoint designed for external cron or systemd timer

Notifications System (Phases 1 + 2 + 3)

Phase 1 — Foundation:

  • Fixed latent NotificationPreference.category bug that silently dropped notifications
  • New NotificationService.emit_event(event_key, user_id, context, group_key=...) — templated event emitter with preference gating and multi-channel dispatch
  • 10 seeded NotificationTemplate rows via seed_notification_templates.py (re-runnable): deal lifecycle, bids, briefs, property events, digests, payment failures

Phase 2 — UI:

  • New NotificationBell component in header — unread badge, 360px dropdown, polls /unread-count every 10s, EventSource subscription to SSE, category dots, hover archive, mark-all-read
  • /notifications page rewritten: All/Unread tabs with live counts, category dropdown, 7/30/90-day filter, pagination, per-row archive + navigate + mark-read
  • New endpoints: POST /{id}/mark-read, POST /mark-all-read, DELETE /{id} (archive), GET /notifications?category=&include_archived=

Phase 3 — Real-time + grouping:

  • SSE endpoint /notifications/stream pushes new notifications in real time
  • Auth via Authorization header (primary) or ?token=<jwt> query fallback (EventSource limitation)
  • Grouping mechanism: emit_event(group_key='bids.{listing_id}', group_window_minutes=60) collapses repeats into a single notification with (N updates) suffix

Wired into business actions:

  • book_forward_listing, submit_eoi, respond_to_eoi, update_deal_status (scheduling)
  • create_bid, accept_bid, reject_bid, submit_delivery, mark_complete, dispute, pay (marketplace)
  • Existing _notify() callers in briefs.py left unchanged (already working)

Vendor Dashboard Rewrite

  • Removed misplaced Listings/Bids/Deals sections (vendors are service providers, not marketplace sellers)
  • New GET /vendors/me/dashboard-summary endpoint
  • KPI cards: Active Deliverables (with overdue pill), Pending Invoices, Paid This Month, On-Time Delivery %
  • 2-column main: Work Queue (top 5 urgency-sorted) + Earnings (6-month revenue chart + recent invoices)
  • Empty-state CTA when no Vendor profile exists

Settings Page — All 6 Tabs Now Functional

  • Profile — fixed phone field silent-drop bug (schema was missing the field)
  • Workspace — expanded from 1 field to Identity (name, slug, logo URL) + Branding (colors with live preview) + Commerce (default currency) + Brand Category
  • Notifications — master channel toggles + 9×3 category × channel matrix + quiet hours (timezone-aware)
  • Security — built from scratch: change password, 2FA enable/disable (QR code + backup codes), active sessions with per-session sign-out
  • Billing — summary card (methods count, pending invoices amount) + deep link to /billing
  • Team — clean redirect card to /team
  • New backend: POST /auth/mfa/enable (authenticated TOTP verification, distinct from login-flow /mfa/verify)

API/Product Gap Closures

  • Brief "Send Brief" button added to both influencer detail pages (/marketplace/influencers/[id] and /influencers/[id]) — extracted reusable SendBriefModal component
  • Property dropdown for collaboration types (multi-select from master list)
  • Pill sub-tabs on Discover/My Bookings/My Listings for advertiser side (mirrors publisher side)
  • Restriction display on /influencers/[id] detail page (advertiser view)

Data Model Changes (this release)

  • influencer_properties — 6 verification/sync columns (migration c3d4e5f7a8b9)
  • deals table — workflow columns already added in prior release
  • users.phone — existed but was silently dropped by request schema; now actually saves
  • New templates: notification_templates seeded with 10 rows

Documentation

  • CHANGELOG.md — daily entries through 2026-04-21
  • CLAUDE.md — ongoing router registry + gotchas updates
  • This file — consolidated release notes

Known Gaps (unchanged)

  • Payment webhooks still skeleton only
  • External integrations (Meta/Google/TikTok Ads, OpenAI, DocuSign, Twilio, Firebase, S3) not wired
  • No automated test suite
  • Email provider not configured (digest/notifications log in dev mode)
  • Celery not set up (digest runs on-demand; weekly cron not scheduled)

---

Want to see it in your own workspace?

Bring planning, discovery, delivery, and measurement into one platform.