Add CRM activity enrichment, notification bridging, crash-safe scheduled jobs, and quick wins

Workstream A — CRM & Notifications:
- Add fire-and-forget CRM activity helper (api/src/utils/crm-activity.ts) hooked into
  campaign email, canvass visit, donation, and purchase write sites
- Add 5 operational NotificationType enum values (shift_signup_confirmed, shift_reminder,
  shift_cancelled, canvass_session_summary, reengagement) via Prisma migration
- Bridge notification email queue to in-app notifications for volunteer-facing events
- Extend TYPE_TO_PREF map and NotificationsPage labels for new types

Workstream B — Quick Wins:
- Extract shared role constants (11 roles) to admin/src/utils/role-constants.ts,
  update 4 consuming pages
- Add Ad Analytics sidebar entry in payments submenu
- Gate 6 calendar routes with enableSocialCalendar feature flag
- Add GET /series/:id/count endpoint and fix hardcoded shiftsCount={0} in ShiftsPage
- Add influenceCampaignId to Order model for donation-campaign attribution,
  wire through Stripe checkout metadata

Workstream C — Crash-Safe Scheduled Jobs:
- Create BullMQ scheduled-jobs queue with 10 repeatable job types replacing
  setInterval blocks in server.ts (dynamic imports, concurrency: 2)
- Keep presenceService (1min) and challengeScoringService (5min) as setInterval

Bunker Admin
This commit is contained in:
2026-03-09 14:15:30 -06:00
parent c192c04c79
commit 900a0affe5
24 changed files with 438 additions and 110 deletions

View File

@@ -0,0 +1,16 @@
-- AlterEnum
-- Add operational notification types for shift/canvass/reengagement notifications
ALTER TYPE "NotificationType" ADD VALUE 'shift_signup_confirmed';
ALTER TYPE "NotificationType" ADD VALUE 'shift_reminder';
ALTER TYPE "NotificationType" ADD VALUE 'shift_cancelled';
ALTER TYPE "NotificationType" ADD VALUE 'canvass_session_summary';
ALTER TYPE "NotificationType" ADD VALUE 'reengagement';
-- AlterTable: Add campaign attribution to donation orders
ALTER TABLE "orders" ADD COLUMN "influence_campaign_id" TEXT;
-- CreateIndex
CREATE INDEX "idx_orders_influence_campaign" ON "orders"("influence_campaign_id");
-- AddForeignKey
ALTER TABLE "orders" ADD CONSTRAINT "orders_influence_campaign_id_fkey" FOREIGN KEY ("influence_campaign_id") REFERENCES "campaigns"("id") ON DELETE SET NULL ON UPDATE CASCADE;