From 58dc0d79bcbe0e7ce07fed46922f06d279365005 Mon Sep 17 00:00:00 2001 From: Abraham123-dev Date: Fri, 5 Jun 2026 13:08:12 +0100 Subject: [PATCH 1/2] fixed routing for modal manual issue --- .../org/my-event/[my-eventId]/page.jsx | 48 ++++++++- .../dashboard/user/events/[event_id]/page.jsx | 11 ++- .../checkout/payment/[booking_id]/page.jsx | 8 ++ .../events/[event_id]/EventDetailsClient.jsx | 59 ++++++++--- .../organizer/BulkBookForAttendeeModal.jsx | 98 ++++++------------- 5 files changed, 134 insertions(+), 90 deletions(-) diff --git a/src/app/(protected)/dashboard/org/my-event/[my-eventId]/page.jsx b/src/app/(protected)/dashboard/org/my-event/[my-eventId]/page.jsx index 9f9c711..b39aacc 100644 --- a/src/app/(protected)/dashboard/org/my-event/[my-eventId]/page.jsx +++ b/src/app/(protected)/dashboard/org/my-event/[my-eventId]/page.jsx @@ -28,7 +28,8 @@ import { Minus, Plus, Megaphone, - Tag + Tag, + Landmark } from "lucide-react"; import toast from "react-hot-toast"; import { getImageUrl, getErrorMessage } from "../../../../../../lib/utils"; @@ -46,6 +47,7 @@ import useTempBookingStore from "@/store/tempBookingStore"; function BookForAttendeeModal({ isOpen, onClose, event, eventId, onSuccess, onManualPaymentRequired }) { const router = useRouter(); const [loading, setLoading] = useState(false); + const [paymentMethod, setPaymentMethod] = useState('paystack'); const [formData, setFormData] = useState({ firstname: '', lastname: '', @@ -112,14 +114,14 @@ function BookForAttendeeModal({ isOpen, onClose, event, eventId, onSuccess, onMa ] }; if (isPaidEvent) { - payload.payment_method = 'paystack'; + payload.payment_method = paymentMethod; } const response = await api.post('/tickets/organizer/book-for-attendee/', payload); const result = response.data; // Paid event: store booking with base subtotal so checkout page adds platform + Paystack fees - if (isPaidEvent && result.booking_id != null && result.payment_url != null) { + if (isPaidEvent && result.booking_id != null) { const price = selectedCategory?.price ?? 0; const subtotal = price * formData.quantity; const items = [{ @@ -139,6 +141,7 @@ function BookForAttendeeModal({ isOpen, onClose, event, eventId, onSuccess, onMa payment_url: result.payment_url || null, payment_reference: result.payment_reference || null, tickets: result.tickets || [], + payment_method: paymentMethod, created_at: new Date().toISOString(), organizer_booking: { returnUrl: window.location.pathname, @@ -150,7 +153,10 @@ function BookForAttendeeModal({ isOpen, onClose, event, eventId, onSuccess, onMa onSuccess?.(); onClose(); toast.success('Proceeding to checkout...'); - router.push(`/checkout/payment/${result.booking_id}`); + const checkoutUrl = paymentMethod === 'manual_bank_transfer' + ? `/checkout/payment/${result.booking_id}?method=bank_transfer` + : `/checkout/payment/${result.booking_id}`; + router.push(checkoutUrl); return; } @@ -287,6 +293,40 @@ function BookForAttendeeModal({ isOpen, onClose, event, eventId, onSuccess, onMa + {/* Payment Method Selection */} + {isPaidEvent && ( +
+
+ + Payment Method +
+
+ + +
+
+ )} + {/* Ticket Selection Card */}
diff --git a/src/app/(protected)/dashboard/user/events/[event_id]/page.jsx b/src/app/(protected)/dashboard/user/events/[event_id]/page.jsx index bf4fd3b..d028914 100644 --- a/src/app/(protected)/dashboard/user/events/[event_id]/page.jsx +++ b/src/app/(protected)/dashboard/user/events/[event_id]/page.jsx @@ -377,13 +377,18 @@ const EventDetailsPage = () => { if (validReferral) clearReferral(); - toast.success("Booking created! Redirecting to checkout...", { id: toastId }); - router.push(`/checkout/payment/${bookingId}`); + if (checkoutPaymentMethod === "manual_bank_transfer") { + toast.success("Booking created! Please complete the bank transfer.", { id: toastId }); + router.push(`/checkout/payment/${bookingId}?method=bank_transfer`); + } else { + toast.success("Booking created! Redirecting to checkout...", { id: toastId }); + router.push(`/checkout/payment/${bookingId}`); + } return; } // Fallback: If there's a direct payment URL, redirect to it - if (response.data.payment_url) { + if (response.data.payment_url && checkoutPaymentMethod !== "manual_bank_transfer") { toast.success("Redirecting to payment...", { id: toastId }); window.location.href = response.data.payment_url; return; diff --git a/src/app/checkout/payment/[booking_id]/page.jsx b/src/app/checkout/payment/[booking_id]/page.jsx index 2e30df7..5bba2af 100644 --- a/src/app/checkout/payment/[booking_id]/page.jsx +++ b/src/app/checkout/payment/[booking_id]/page.jsx @@ -49,6 +49,14 @@ export default function CheckoutPaymentPage() { const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState("paystack"); + // Sync active tab with URL parameters for direct redirection (e.g. from organizer dashboard) + useEffect(() => { + const method = searchParams.get('method'); + if (method === 'bank_transfer') { + setActiveTab('manual_bank_transfer'); + } + }, [searchParams]); + useEffect(() => { const fetchBookingData = async () => { if (!booking_id) { diff --git a/src/app/events/[event_id]/EventDetailsClient.jsx b/src/app/events/[event_id]/EventDetailsClient.jsx index 25b4476..3edf6cd 100644 --- a/src/app/events/[event_id]/EventDetailsClient.jsx +++ b/src/app/events/[event_id]/EventDetailsClient.jsx @@ -8,7 +8,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; -import { Loader2, MapPin, Calendar, Clock, Ticket, Info, Share2, Copy, Check, X, Maximize2, Plus, Minus, ShoppingCart } from "lucide-react"; +import { Loader2, MapPin, Calendar, Clock, Ticket, Info, Share2, Copy, Check, X, Maximize2, Plus, Minus, ShoppingCart, CreditCard, Landmark, Landmark as Bank } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import toast from "react-hot-toast"; import useAuthStore from "@/store/authStore"; @@ -35,6 +35,7 @@ const EventDetailsClient = ({ event_id, initialEvent }) => { const [event, setEvent] = useState(initialEvent || null); const [loading, setLoading] = useState(!initialEvent); const [bookingLoading, setBookingLoading] = useState(false); + const [paymentMethod, setPaymentMethod] = useState("paystack"); // 'paystack' or 'manual_bank_transfer' //Getting booking id for tracking tickets const [bookingID,setBookingID] = useState(null) @@ -276,6 +277,7 @@ const EventDetailsClient = ({ event_id, initialEvent }) => { const payload = { event_id: eventIdToUse, items: items, + payment_method: paymentMethod, // Scoped referral source for event:TO-56363 ...(eventIdToUse === "event:TO-56363" && { referral: refUsername, // Pass referee username as 'referral' to backend @@ -314,22 +316,15 @@ const EventDetailsClient = ({ event_id, initialEvent }) => { }; localStorage.setItem(`booking_${bookingId}`, JSON.stringify(bookingDataForCheckout)); - toast.success("Booking created! Redirecting to payment...", { id: toastId }); - router.push(`/checkout/payment/${bookingId}`); - return; - } - - // Fallback for cases where booking_id isn't directly returned but payment_url is - if (response.data.payment_url) { - toast.success("Redirecting to payment...", { id: toastId }); - window.location.href = response.data.payment_url; + if (paymentMethod === "manual_bank_transfer") { + toast.success("Booking created! Please complete the bank transfer.", { id: toastId }); + router.push(`/checkout/payment/${bookingId}?method=bank_transfer`); + } else { + toast.success("Booking created! Redirecting to payment...", { id: toastId }); + router.push(`/checkout/payment/${bookingId}`); + } return; } - - toast.success("Ticket booked successfully!", { id: toastId }); - if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent("tickets-updated")); - router.push("/dashboard/user/my-tickets"); - } catch (error) { console.error("Booking error:", error); let errorMessage = error.response?.data?.error || "Failed to book ticket"; @@ -410,6 +405,40 @@ const EventDetailsClient = ({ event_id, initialEvent }) => {
+ + {/* Payment Method Selection */} + {event.pricing_type === 'paid' && orderSummary.totalQuantity > 0 && ( +
+

Select Payment Method

+
+ + +
+
+ )} + +
+
{/* Referral Badge */} diff --git a/src/components/organizer/BulkBookForAttendeeModal.jsx b/src/components/organizer/BulkBookForAttendeeModal.jsx index 5a5c6d8..7ab0cf3 100644 --- a/src/components/organizer/BulkBookForAttendeeModal.jsx +++ b/src/components/organizer/BulkBookForAttendeeModal.jsx @@ -380,18 +380,12 @@ export default function BulkBookForAttendeeModal({ ); const result = response.data; - // Paid + manual bank transfer — route to checkout with manual transfer tab - if ( - isPaidEvent && - paymentMethod === "manual_bank_transfer" && - result.booking_id != null && - result.total_amount != null - ) { + if (isPaidEvent && result.booking_id != null) { const categoryTotals = {}; attendees.forEach((a) => { const catName = a.category_name || defaultCategoryName; const cat = categories.find((c) => c.name === catName); - const price = cat?.price ? Number(cat.price) : 0; + const price = cat?.price ? Number(cat.price) : 0; // Ensure price is a number if (!categoryTotals[catName]) categoryTotals[catName] = { name: catName, price, quantity: 0 }; categoryTotals[catName].quantity += 1; @@ -401,54 +395,7 @@ export default function BulkBookForAttendeeModal({ (sum, item) => sum + item.price * item.quantity, 0, ); - const bookingData = { - booking_id: result.booking_id, - event_name: event?.name || "Event", - event_id: decodedEventId, - event_image: event?.image, - items, - total_quantity: result.ticket_count ?? ticketCount, - subtotal, - payment_method: "manual_bank_transfer", - total_manual_amount: result.total_amount || (subtotal + 80), - payment_reference: result.payment_reference || null, - tickets: result.tickets || [], - created_at: new Date().toISOString(), - organizer_booking: { returnUrl: window.location.pathname }, - }; - localStorage.setItem( - `booking_${result.booking_id}`, - JSON.stringify(bookingData), - ); - resetModal(); - onSuccess?.(); - onClose(); - toast.success("Proceeding to manual transfer..."); - router.push(`/checkout/payment/${result.booking_id}`); - return; - } - // Paid + Paystack: store booking with base subtotal so checkout page adds platform + Paystack fees - if ( - isPaidEvent && - paymentMethod !== "manual_bank_transfer" && - result.booking_id != null && - result.payment_url != null - ) { - const categoryTotals = {}; - attendees.forEach((a) => { - const catName = a.category_name || defaultCategoryName; - const cat = categories.find((c) => c.name === catName); - const price = cat?.price ? Number(cat.price) : 0; - if (!categoryTotals[catName]) - categoryTotals[catName] = { name: catName, price, quantity: 0 }; - categoryTotals[catName].quantity += 1; - }); - const items = Object.values(categoryTotals); - const subtotal = items.reduce( - (sum, item) => sum + item.price * item.quantity, - 0, - ); const bookingData = { booking_id: result.booking_id, event_name: event?.name || "Event", @@ -457,26 +404,41 @@ export default function BulkBookForAttendeeModal({ items, total_quantity: result.ticket_count ?? ticketCount, subtotal, - payment_url: result.payment_url || null, + payment_url: result.payment_url || null, // Only for online payments payment_reference: result.payment_reference || null, - payment_method: result.payment_method || "paystack", + payment_method: paymentMethod, // Use the selected payment method + total_manual_amount: result.total_amount || (subtotal + 80), // Only for manual transfer, assuming 80 is platform fee tickets: result.tickets || [], created_at: new Date().toISOString(), - organizer_booking: { returnUrl: window.location.pathname }, + organizer_booking: { + returnUrl: window.location.pathname, + attendeeEmail: attendees[0]?.email, + attendeeName: `${attendees[0]?.firstname} ${attendees[0]?.lastname}`, + }, }; - localStorage.setItem( - `booking_${result.booking_id}`, - JSON.stringify(bookingData), - ); - resetModal(); - onSuccess?.(); - onClose(); - toast.success("Proceeding to checkout..."); - router.push(`/checkout/payment/${result.booking_id}`); + + if (paymentMethod === "manual_bank_transfer") { + // Redirect to the standard checkout page with bank transfer active + // This ensures the user sees the account info (ManualTransferTab) first + localStorage.setItem(`booking_${result.booking_id}`, JSON.stringify(bookingData)); + toast.success("Proceeding to manual transfer..."); + router.push(`/checkout/payment/${result.booking_id}?method=bank_transfer`); + resetModal(); + onSuccess?.(); + onClose(); + } else { + // Handle Paystack/Online payment redirection + localStorage.setItem(`booking_${result.booking_id}`, JSON.stringify(bookingData)); + toast.success("Proceeding to checkout..."); + router.push(`/checkout/payment/${result.booking_id}`); + resetModal(); + onSuccess?.(); + onClose(); + } return; } - // Free event + // Handle Free Event (Booking is immediate and doesn't require payment routing) toast.success( `Successfully booked ${result.ticket_count} ticket(s) for ${result.unique_attendees ?? result.attendees?.length ?? ticketCount} attendee(s)`, ); From ceafa01de4373ecb899a3c9ce4a8f9cc5ee0bd1c Mon Sep 17 00:00:00 2001 From: Abraham123-dev Date: Fri, 5 Jun 2026 13:30:24 +0100 Subject: [PATCH 2/2] fixed event details cliiet issue --- .../events/[event_id]/EventDetailsClient.jsx | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/src/app/events/[event_id]/EventDetailsClient.jsx b/src/app/events/[event_id]/EventDetailsClient.jsx index 3edf6cd..66ccaf2 100644 --- a/src/app/events/[event_id]/EventDetailsClient.jsx +++ b/src/app/events/[event_id]/EventDetailsClient.jsx @@ -184,13 +184,15 @@ const EventDetailsClient = ({ event_id, initialEvent }) => { } }); - // Paystack calculates fee on total amount INCLUDING platform service fee - const paystackFee = subtotal > 0 ? calculatePaystackFee(subtotal + PLATFORM_FEE) : 0; - const platformFee = subtotal > 0 ? PLATFORM_FEE + paystackFee : 0; + // Base platform service fee + const baseServiceFee = subtotal > 0 ? PLATFORM_FEE : 0; + // Paystack processing fee only applies if Paystack is the selected method + const paystackFee = (subtotal > 0 && paymentMethod === "paystack") ? calculatePaystackFee(subtotal + PLATFORM_FEE) : 0; + const platformFee = baseServiceFee + paystackFee; const total = subtotal + platformFee; - return { selectedItems, subtotal, platformFee, total, totalQuantity }; - }, [ticketSelections, categories]); + return { selectedItems, subtotal, platformFee, total, totalQuantity, paystackFee }; + }, [ticketSelections, categories, paymentMethod]); // Handle quantity change for a category const handleQuantityChange = (categoryId, delta) => { @@ -406,40 +408,6 @@ const EventDetailsClient = ({ event_id, initialEvent }) => {
- {/* Payment Method Selection */} - {event.pricing_type === 'paid' && orderSummary.totalQuantity > 0 && ( -
-

Select Payment Method

-
- - -
-
- )} - -
-
- {/* Referral Badge */} {refUsername && (