Ship all P0/P1/P2 gaps + 11 AI features
P0 Critical (7): - STOP/UNSUBSCRIBE keyword → CANCEL (PECR compliance) - Rate limiting on pledge creation (10/IP/5min) - Terms of Service + Privacy Policy pages - WhatsApp onboarding gate (persistent dashboard banner) - Demo account seeding (demo@pnpl.app) - Footer legal links - Basic accessibility (aria labels on donor flow) P1 Within 2 Weeks (8): - Pledge editing by staff (PATCH amount, name, email, phone, rail) - Donor self-cancel page (/p/cancel) + API - Donor 'My Pledges' lookup page (/p/my-pledges) - Bulk QR code download (print-ready HTML) - Public event progress bar (/e/[slug]/progress) - Email-only donor handling (honest status + WhatsApp fallback) - Email verification (format + disposable domain blocking) - Organisations page rewrite (multi-campaign, not multi-org) P2 Within First Month (10): - Event cloning with QR sources - Account deletion (GDPR Article 17) - Daily digest cron via WhatsApp - AI-6 Smart reminder timing (due date anchoring, cultural sensitivity) - H1 Duplicate donor detection (email, phone, Jaro-Winkler name) - H5 Bank CSV format presets (10 UK banks) - H16 Partial payment matching (underpay, overpay, instalment) - H10 Activity logging (audit trail for staff actions) - AI nudge endpoint + AI column mapping + AI event setup wizard - AI anomaly detection wired into daily digest AI Features (11): smart reconciliation, social proof, auto column mapper, daily digest, impact storyteller, smart timing, nudge composer, event wizard, NLU concierge, anomaly detection, bank presets 22 new files, 15 modified files, 0 TypeScript errors, clean build.
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { useSession, signOut } from "next-auth/react"
|
||||
import { LayoutDashboard, Megaphone, FileBarChart, Upload, Download, Settings, Plus, ExternalLink, LogOut, Shield } from "lucide-react"
|
||||
import { useState, useEffect } from "react"
|
||||
import { LayoutDashboard, Megaphone, FileBarChart, Upload, Download, Settings, Plus, ExternalLink, LogOut, Shield, MessageCircle, AlertTriangle } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const navItems = [
|
||||
@@ -132,9 +133,53 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
|
||||
{/* Main content */}
|
||||
<main className="flex-1 p-4 md:p-6 pb-20 md:pb-6 max-w-6xl">
|
||||
<WhatsAppBanner />
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Persistent WhatsApp connection banner — shows until connected */
|
||||
function WhatsAppBanner() {
|
||||
const [status, setStatus] = useState<string | null>(null)
|
||||
const [dismissed, setDismissed] = useState(false)
|
||||
const pathname = usePathname()
|
||||
|
||||
useEffect(() => {
|
||||
// Don't show on settings page (they're already there)
|
||||
if (pathname === "/dashboard/settings") { setStatus("skip"); return }
|
||||
fetch("/api/whatsapp/send")
|
||||
.then(r => r.json())
|
||||
.then(data => setStatus(data.connected ? "CONNECTED" : "OFFLINE"))
|
||||
.catch(() => setStatus("OFFLINE"))
|
||||
}, [pathname])
|
||||
|
||||
if (status === "CONNECTED" || status === "skip" || status === null || dismissed) return null
|
||||
|
||||
return (
|
||||
<div className="mb-4 rounded-lg border-2 border-amber-200 bg-amber-50 p-4 flex items-start gap-3">
|
||||
<div className="w-9 h-9 rounded-lg bg-amber-100 flex items-center justify-center shrink-0 mt-0.5">
|
||||
<AlertTriangle className="h-5 w-5 text-amber-600" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-bold text-gray-900">WhatsApp not connected — reminders won't send</p>
|
||||
<p className="text-xs text-gray-600 mt-0.5">
|
||||
Connect your WhatsApp to auto-send pledge receipts and payment reminders to donors. Takes 60 seconds.
|
||||
</p>
|
||||
<div className="flex items-center gap-3 mt-2">
|
||||
<Link
|
||||
href="/dashboard/settings"
|
||||
className="inline-flex items-center gap-1.5 bg-[#25D366] px-3 py-1.5 text-xs font-bold text-white hover:bg-[#25D366]/90 transition-colors rounded"
|
||||
>
|
||||
<MessageCircle className="h-3.5 w-3.5" /> Connect WhatsApp
|
||||
</Link>
|
||||
<button onClick={() => setDismissed(true)} className="text-xs text-gray-400 hover:text-gray-600">
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user