simplify: zakat yes/no per campaign, remove 5 fund types, add I've Donated button for external pledges

- Event.zakatEligible (boolean) replaces Organization.zakatEnabled + 5 fund types
- Pledge.isZakat (boolean) replaces Pledge.fundType enum
- Removed fundAllocation from Event (campaign IS the allocation)
- Identity step: simple checkbox instead of 5-option grid
- Campaign creation: Zakat toggle + optional external URL for self-payment
- External redirect step: 'I've Donated' button calls /api/pledges/[id]/mark-initiated
- Landing page: simplified Zakat section (toggle preview, not 5 fund descriptions)
- Settings: removed org-level Zakat toggle (it's per campaign now)
- Migration: ALTER TABLE adds zakatEligible/isZakat, drops fundAllocation
This commit is contained in:
2026-03-03 07:19:52 +08:00
parent f87aec7beb
commit fc80a43a89
14 changed files with 131 additions and 179 deletions

View File

@@ -90,10 +90,21 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
</CardContent>
</Card>
{/* Re-open link */}
<Button onClick={() => window.open(externalUrl, "_blank")} className="w-full" variant="outline">
<ExternalLink className="h-4 w-4 mr-2" /> Open {platform.name} again
</Button>
{/* Confirm donation + re-open */}
<div className="space-y-2">
<Button
onClick={() => {
fetch(`/api/pledges/${pledge.id}/mark-initiated`, { method: "POST" }).catch(() => {})
if (navigator.vibrate) navigator.vibrate([10, 50, 10])
}}
className="w-full bg-success-green hover:bg-success-green/90 text-white"
>
<Check className="h-4 w-4 mr-2" /> I&apos;ve Donated
</Button>
<Button onClick={() => window.open(externalUrl, "_blank")} className="w-full" variant="outline">
<ExternalLink className="h-4 w-4 mr-2" /> Open {platform.name} again
</Button>
</div>
{/* Share */}
<div className="rounded-2xl bg-gradient-to-br from-warm-amber/5 to-orange-50 border border-warm-amber/20 p-5 space-y-3">
@@ -124,7 +135,7 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
</div>
<p className="text-xs text-muted-foreground">
We&apos;ll send you a gentle reminder if we don&apos;t see a donation come through.
Donated already? Tap &quot;I&apos;ve Donated&quot; above, or reply <strong>PAID</strong> to the WhatsApp message.
</p>
</div>
)

View File

@@ -4,33 +4,24 @@ import { useState, useRef, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { Gift, Shield, Sparkles, Phone, Mail } from "lucide-react"
const FUND_TYPES = [
{ id: "general", label: "General Donation", icon: "🤲", desc: "Sadaqah — used where most needed" },
{ id: "zakat", label: "Zakat", icon: "🌙", desc: "Obligatory annual charity (2.5%)" },
{ id: "sadaqah", label: "Sadaqah Jariyah", icon: "🌱", desc: "Ongoing charity — builds, wells, education" },
{ id: "lillah", label: "Lillah", icon: "🕌", desc: "For the mosque / institution itself" },
{ id: "fitrana", label: "Fitrana", icon: "🍽️", desc: "Zakat al-Fitr — given before Eid" },
]
interface Props {
onSubmit: (data: {
donorName: string
donorEmail: string
donorPhone: string
giftAid: boolean
fundType?: string
isZakat?: boolean
}) => void
amount: number
zakatEnabled?: boolean
fundAllocation?: string | null
zakatEligible?: boolean
}
export function IdentityStep({ onSubmit, amount, zakatEnabled, fundAllocation }: Props) {
export function IdentityStep({ onSubmit, amount, zakatEligible }: Props) {
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [phone, setPhone] = useState("")
const [giftAid, setGiftAid] = useState(false)
const [fundType, setFundType] = useState<string>(fundAllocation ? "general" : "general")
const [isZakat, setIsZakat] = useState(false)
const [submitting, setSubmitting] = useState(false)
const [contactMode, setContactMode] = useState<"email" | "phone">("email")
const nameRef = useRef<HTMLInputElement>(null)
@@ -46,7 +37,7 @@ export function IdentityStep({ onSubmit, amount, zakatEnabled, fundAllocation }:
if (!isValid) return
setSubmitting(true)
try {
await onSubmit({ donorName: name, donorEmail: email, donorPhone: phone, giftAid, fundType: zakatEnabled ? fundType : undefined })
await onSubmit({ donorName: name, donorEmail: email, donorPhone: phone, giftAid, isZakat: zakatEligible ? isZakat : false })
} catch {
setSubmitting(false)
}
@@ -134,35 +125,28 @@ export function IdentityStep({ onSubmit, amount, zakatEnabled, fundAllocation }:
)}
</div>
{/* Fund Type — only when org has Zakat enabled */}
{zakatEnabled && (
<div className="space-y-2 animate-fade-in">
<p className="text-sm font-bold text-gray-900">
{fundAllocation ? `Fund: ${fundAllocation}` : "What is this donation for?"}
</p>
<div className="grid grid-cols-2 gap-2">
{FUND_TYPES.map((ft) => (
<button
key={ft.id}
onClick={() => setFundType(ft.id)}
className={`text-left rounded-xl border-2 p-3 transition-all ${
fundType === ft.id
? "border-trust-blue bg-trust-blue/5 shadow-sm"
: "border-gray-100 hover:border-gray-200"
}`}
>
<span className="text-lg">{ft.icon}</span>
<p className={`text-xs font-bold mt-1 ${fundType === ft.id ? "text-trust-blue" : "text-gray-900"}`}>{ft.label}</p>
<p className="text-[10px] text-muted-foreground leading-tight">{ft.desc}</p>
</button>
))}
</div>
{fundType === "zakat" && (
<div className="rounded-xl bg-warm-amber/5 border border-warm-amber/20 p-3 text-xs text-muted-foreground animate-fade-in">
Zakat is distributed according to the eight categories specified in the Quran (9:60). This charity is Zakat-eligible.
{/* Zakat — only when campaign is Zakat-eligible */}
{zakatEligible && (
<button
onClick={() => setIsZakat(!isZakat)}
className={`w-full text-left rounded-2xl border-2 p-4 transition-all ${
isZakat
? "border-trust-blue bg-trust-blue/5 shadow-sm"
: "border-gray-200 bg-white hover:border-trust-blue/40"
}`}
>
<div className="flex items-center gap-3">
<div className={`w-5 h-5 rounded border-2 flex items-center justify-center transition-all ${isZakat ? "bg-trust-blue border-trust-blue" : "border-gray-300"}`}>
{isZakat && <span className="text-white text-xs font-bold"></span>}
</div>
)}
</div>
<div className="flex-1">
<span className="font-bold text-sm">🌙 This is Zakat</span>
<p className="text-xs text-muted-foreground mt-0.5">
Mark this pledge as Zakat (obligatory charity). It will be tracked separately.
</p>
</div>
</div>
</button>
)}
{/* Gift Aid — the hero */}