diff --git a/.gitignore b/.gitignore index b90a368..0788881 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules .next +*.local +.env.local +.env \ No newline at end of file diff --git a/components/BlogCard.js b/components/BlogCard.js index 9e15665..c51ba19 100644 --- a/components/BlogCard.js +++ b/components/BlogCard.js @@ -1,17 +1,17 @@ -import React from "react" -import { Card } from "./common/Card" -import { blogdata } from "@/assets/data/dummydata" +// import React from "react" +// import { Card } from "./common/Card" +// import { blogdata } from "@/assets/data/dummydata" -const BlogCard = () => { - return ( - <> -
- {blogdata.map((item) => ( - - ))} -
- - ) -} +// const BlogCard = () => { +// return ( +// <> +//
+// {blogdata.map((item) => ( +// +// ))} +//
+// +// ) +// } -export default BlogCard +// export default BlogCard diff --git a/components/HeroPage/BrandCarousel.js b/components/HeroPage/BrandCarousel.js index e346250..0d4e0f9 100644 --- a/components/HeroPage/BrandCarousel.js +++ b/components/HeroPage/BrandCarousel.js @@ -193,13 +193,11 @@ const BrandCarousel = () => {
- Event 1 Image -
diff --git a/components/HeroPage/HeroMain.js b/components/HeroPage/HeroMain.js index df105a1..3119553 100644 --- a/components/HeroPage/HeroMain.js +++ b/components/HeroPage/HeroMain.js @@ -3,6 +3,7 @@ import AOS from "aos"; import Link from "next/link"; function HeroMain() { + const eventId = 'mckrPpdQi0JqD4HF2uvh'; //pelase add eventid here useEffect(() => { AOS.init({ duration: 2500, // Animation duration in milliseconds @@ -28,17 +29,11 @@ function HeroMain() { #CapTech2024
25th - 28th November 2024 - + + +
+ {error &&

{error}

} + +
+
+
+ + ); +}; + +export default AdminLogin; diff --git a/pages/api/send-ticket-email.js b/pages/api/send-ticket-email.js new file mode 100644 index 0000000..e0e8087 --- /dev/null +++ b/pages/api/send-ticket-email.js @@ -0,0 +1,99 @@ +// api/send-ticket-email.js +import nodemailer from 'nodemailer'; + +export default async function handler(req, res) { + console.log('--- Starting email sending process ---'); + console.log('Request method:', req.method); + + if (req.method === 'POST') { + console.log('Request body:', JSON.stringify(req.body, null, 2)); + + const { email, orderId, eventDetails, tickets, totalAmount, transactionFee } = req.body; + + console.log('Environment variables:'); + console.log('ZOHO_USER:', process.env.ZOHO_USER ? `Set (${process.env.ZOHO_USER})` : 'Not set'); + console.log('ZOHO_PASSWORD:', process.env.ZOHO_PASSWORD ? 'Set (length: ' + process.env.ZOHO_PASSWORD.length + ')' : 'Not set'); + + if (!process.env.ZOHO_USER || !process.env.ZOHO_PASSWORD) { + console.error('Environment variables are not set correctly'); + return res.status(500).json({ error: 'Server configuration error: Missing environment variables' }); + } + + // Function to attempt sending email + const attemptSendEmail = async (config) => { + console.log(`Attempting to send email with config:`, JSON.stringify({...config, auth: {...config.auth, pass: '[REDACTED]'}}, null, 2)); + + try { + const transporter = nodemailer.createTransport(config); + + console.log('Verifying transporter connection...'); + await transporter.verify(); + console.log('Transporter connection verified successfully.'); + + const mailOptions = { + from: 'no-reply@sipbn.com.au', + to: email, + subject: `Your Ticket Purchase Confirmation for ${eventDetails.name}`, + text: `Dear customer,\n\nThank you for purchasing tickets to ${eventDetails.name}.\nYour Order ID is: ${orderId}\n\nEvent Details:\nEvent Name: ${eventDetails.name}\nEvent Date: ${eventDetails.date}\n\nPurchased Tickets:\n${tickets.map(t => `${t.name} (Qty: ${t.quantity}) - $${t.price} each - Total: $${t.total}`).join('\n')}\n\nTotal Amount: $${totalAmount}\nTransaction Fee: $${transactionFee}\n\nYou will receive your tickets soon. If you have any questions, feel free to contact us.\n\nBest regards,\nThe Event Team`, + html: `

Thank you for your purchase!

Your Order ID is: ${orderId}

Event Details:

Purchased Tickets:

Total Amount: $${totalAmount}

Transaction Fee: $${transactionFee}

You will receive your tickets soon. If you have any questions, feel free to contact us.

Best regards,

The Event Team

`, + }; + + console.log('Sending email...'); + const info = await transporter.sendMail(mailOptions); + console.log('Email sent successfully:', info.messageId); + console.log('Full info object:', JSON.stringify(info, null, 2)); + + return { success: true, info }; + } catch (error) { + console.error(`Error with config ${JSON.stringify({...config, auth: {...config.auth, pass: '[REDACTED]'}}, null, 2)}:`, error); + return { success: false, error }; + } + }; + + // Attempt with SSL + console.log('Attempting to send email with SSL...'); + let result = await attemptSendEmail({ + host: 'smtp.zoho.in', + port: 465, + secure: true, + auth: { + user: process.env.ZOHO_USER, + pass: process.env.ZOHO_PASSWORD, + }, + debug: true, + logger: true + }); + + // If SSL fails, attempt with TLS + if (!result.success) { + console.log('SSL attempt failed. Trying with TLS...'); + result = await attemptSendEmail({ + host: 'smtp.zoho.in', + port: 587, + secure: false, + auth: { + user: process.env.ZOHO_USER, + pass: process.env.ZOHO_PASSWORD, + }, + debug: true, + logger: true + }); + } + + if (result.success) { + console.log('Email sent successfully'); + return res.status(200).json({ message: 'Email sent successfully', messageId: result.info.messageId }); + } else { + console.error('All email sending attempts failed'); + return res.status(500).json({ + error: 'Failed to send email', + message: result.error.message, + stack: result.error.stack + }); + } + } else { + console.log(`Method ${req.method} not allowed`); + res.setHeader('Allow', ['POST']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } +} \ No newline at end of file diff --git a/pages/attendees-download.js b/pages/attendees-download.js new file mode 100644 index 0000000..9f4435b --- /dev/null +++ b/pages/attendees-download.js @@ -0,0 +1,101 @@ +// pages/attendeeDownload.js +import { useEffect, useState } from "react"; +import withAuth from "../lib/withAuth"; +import { db } from "../lib/firebase"; // Import your Firestore database +import { collection, getDocs } from "firebase/firestore"; +import * as XLSX from "xlsx"; + +const AttendeeDownload = () => { + const [orders, setOrders] = useState([]); + + useEffect(() => { + const fetchOrders = async () => { + try { + const querySnapshot = await getDocs(collection(db, "orders")); + const orderData = querySnapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data(), + })); + setOrders(orderData); + } catch (error) { + console.error("Error fetching orders: ", error); + } + }; + + fetchOrders(); + }, []); + + const downloadGuestList = () => { + const guestList = []; + + orders.forEach((order) => { + // Ensure tickets is an array and exists + if (Array.isArray(order.tickets)) { + order.tickets.forEach((ticket) => { + guestList.push({ + name: order.name || "", // Add name to the guest list + mobile: order.mobileNumber || "", // Add mobile to the guest list + email: order.email || "", // Add email field in the order + ticketType: ticket.name, + quantity: ticket.quantity, + transactionId: order.orderId, + }); + }); + } + }); + + const worksheet = XLSX.utils.json_to_sheet(guestList); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, "Guest List"); + + // Generate Excel file + XLSX.writeFile(workbook, "guest_list.xlsx"); + }; + + return ( +
+

Download Attendee List

+ +
+ + + + + + + + + + + + {orders.map((order) => ( + + + {/* Display mobile or "N/A" */} + {/* Display email */} + + + + ))} + +
NameMobileEmailTransaction IDTickets
+ {order.name || "N/A"} {/* Display name or "N/A" if not available */} + {order.mobileNumber || "N/A"}{order.email || "N/A"}{order.orderId} + {Array.isArray(order.tickets) ? ( + order.tickets.map((ticket) => ( +
+ {ticket.name} (Quantity: {ticket.quantity}) +
+ )) + ) : ( +
No tickets available
+ )} +
+
+
+ ); +}; + +export default withAuth(AttendeeDownload); diff --git a/pages/event-manager.js b/pages/event-manager.js new file mode 100644 index 0000000..ebe113a --- /dev/null +++ b/pages/event-manager.js @@ -0,0 +1,305 @@ +// pages/event-manager.js +import { useState, useEffect } from 'react'; +import { db } from '../lib/firebase'; +import { collection, addDoc, getDocs, doc, deleteDoc, updateDoc, getDoc } from 'firebase/firestore'; +import withAuth from '../lib/withAuth'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; +import Link from 'next/link'; + +const EventManager = () => { + const [eventName, setEventName] = useState(''); + const [eventDate, setEventDate] = useState(''); + const [ticketCategories, setTicketCategories] = useState({}); + const [existingEvents, setExistingEvents] = useState([]); + const [selectedEventId, setSelectedEventId] = useState('add-new'); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + + useEffect(() => { + fetchEvents(); // Fetch existing events on component mount + }, []); + + const fetchEvents = async () => { + const eventsCollection = collection(db, 'events'); + const eventDocs = await getDocs(eventsCollection); + const events = eventDocs.docs.map(doc => ({ id: doc.id, ...doc.data() })); + setExistingEvents(events); + }; + + const handleTicketChange = (ticketName, field, value) => { + const newCategories = { ...ticketCategories }; + if (!newCategories[ticketName]) { + newCategories[ticketName] = { name: ticketName, price: 0, description: '', available: 0, sold: 0 }; + } + + // Ensure that price and available are treated as numbers + if (field === 'price' || field === 'available') { + newCategories[ticketName][field] = parseFloat(value) || 0; // Convert to number or default to 0 + } else { + newCategories[ticketName][field] = value; + } + + setTicketCategories(newCategories); + }; + + const addTicketCategory = () => { + const newTicketName = `ticket-${Object.keys(ticketCategories).length + 1}`; + setTicketCategories({ + ...ticketCategories, + [newTicketName]: { name: newTicketName, price: 0, description: '', available: 0, sold: 0 } // Initialize sold to 0 + }); + }; + + const deleteTicketCategory = (ticketName) => { + const newCategories = { ...ticketCategories }; + delete newCategories[ticketName]; + setTicketCategories(newCategories); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(''); + setSuccess(''); + + // Convert ticketCategories object to an array and include sold as 0 + const ticketsArray = Object.entries(ticketCategories).map(([ticketName, ticket]) => ({ + [ticketName]: { + name: ticket.name, + price: Number(ticket.price), // Ensure price is a number + description: ticket.description, + available: Number(ticket.available), // Ensure available is a number + sold: 0, // Initialize sold to 0 + } + })).reduce((acc, curr) => ({ ...acc, ...curr }), {}); // Flatten the array into an object + + try { + const eventRef = await addDoc(collection(db, 'events'), { + name: eventName, + date: eventDate, + tickets: ticketsArray, // Save as an object with ticket names as keys + }); + setSuccess(`Event added with ID: ${eventRef.id}`); + resetForm(); + } catch (error) { + setError('Error adding event: ' + error.message); + } + }; + + const resetForm = () => { + setEventName(''); + setEventDate(''); + setTicketCategories({}); + setSelectedEventId('add-new'); + }; + + const handleEventSelect = async (eventId) => { + if (eventId === 'add-new') { + resetForm(); + return; + } + + const eventDoc = doc(db, 'events', eventId); + const eventData = await getDoc(eventDoc); + if (eventData.exists()) { + const eventTickets = eventData.data().tickets; + + // Convert the tickets object back to an array for the form + const ticketObj = {}; + Object.entries(eventTickets).forEach(([ticketName, ticket]) => { + ticketObj[ticketName] = { ...ticket }; // Maintain existing ticket data + }); + + setEventName(eventData.data().name); + setEventDate(eventData.data().date); + setTicketCategories(ticketObj); // Set the tickets as an object for the form + setSelectedEventId(eventId); + } + }; + + + const handleUpdate = async (e) => { + e.preventDefault(); + setError(''); + setSuccess(''); + + // Fetch the existing event document first to retain the sold value + const eventDoc = doc(db, 'events', selectedEventId); + const eventData = await getDoc(eventDoc); + if (!eventData.exists()) { + setError('Event not found'); + return; + } + + const existingTickets = eventData.data().tickets; // Get existing tickets + + // Convert ticketCategories object to an array while retaining the sold values + const ticketsArray = Object.entries(ticketCategories).reduce((acc, [ticketName, ticket]) => { + const existingTicket = existingTickets[ticketName] || { sold: 0 }; // Use existing sold value or 0 if not found + acc[ticketName] = { + name: ticket.name, + price: Number(ticket.price), // Ensure price is a number + description: ticket.description, + available: Number(ticket.available), // Ensure available is a number + sold: existingTicket.sold, // Retain sold value + }; + return acc; + }, {}); + + try { + await updateDoc(eventDoc, { + name: eventName, + date: eventDate, + tickets: ticketsArray, // Save as an object with ticket names as keys + }); + setSuccess('Event updated successfully'); + resetForm(); + await fetchEvents(); // Refresh existing events to reflect the updated event + } catch (error) { + setError('Error updating event: ' + error.message); + } + }; + + + const handleDelete = async () => { + if (!selectedEventId || selectedEventId === 'add-new') return; + + try { + const eventDoc = doc(db, 'events', selectedEventId); + await deleteDoc(eventDoc); + setSuccess('Event deleted successfully'); + resetForm(); + setExistingEvents(existingEvents.filter(event => event.id !== selectedEventId)); + } catch (error) { + setError('Error deleting event: ' + error.message); + } + }; + + return ( +
+

Event Manager

+
+

Event Manager

+ + + + Back to Admin Dashboard + + +
+ +
+
+
+ + +
+ +
+ + setEventName(e.target.value)} + required + /> +
+ +
+ + setEventDate(e.target.value)} + required + /> +
+ +
+

Ticket Categories:

+ {Object.entries(ticketCategories).map(([ticketName, ticket]) => ( +
+
+ handleTicketChange(ticketName, 'name', e.target.value)} + className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" + /> + handleTicketChange(ticketName, 'price', e.target.value)} + className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" + /> + handleTicketChange(ticketName, 'available', e.target.value)} + className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white" + /> +