import React, { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import toast from 'react-hot-toast'; import { DataSyncService } from '../../../scripts/database/DataSyncService'; import { Collections } from '../../../schemas/pocketbase/schema'; import type { EventRequest as SchemaEventRequest } from '../../../schemas/pocketbase/schema'; // Extended EventRequest interface with additional properties needed for this component interface ExtendedEventRequest extends SchemaEventRequest { requested_user_expand?: { name: string; email: string; }; invoice_data?: string | any; } interface EventRequestDetailsProps { request: ExtendedEventRequest; onClose: () => void; onStatusChange: (id: string, status: "submitted" | "pending" | "completed" | "declined") => Promise; } // Separate component for AS Funding tab to isolate any issues const ASFundingTab: React.FC<{ request: ExtendedEventRequest }> = ({ request }) => { if (!request.as_funding_required) { return (

AS Funding Required

No

); } // Process invoice data for display let invoiceData = request.invoice_data; // If invoice_data is not available, try to parse itemized_invoice if (!invoiceData && request.itemized_invoice) { try { if (typeof request.itemized_invoice === 'string') { invoiceData = JSON.parse(request.itemized_invoice); } else if (typeof request.itemized_invoice === 'object') { invoiceData = request.itemized_invoice; } } catch (e) { console.error('Failed to parse itemized_invoice:', e); } } return (

AS Funding Required

Yes

{request.food_drinks_being_served && (

Food/Drinks Being Served

Yes

)}

Invoice Data

); }; // Separate component for invoice table const InvoiceTable: React.FC<{ invoiceData: any }> = ({ invoiceData }) => { try { // Parse invoice data if it's a string let parsedInvoice = null; if (typeof invoiceData === 'string') { try { parsedInvoice = JSON.parse(invoiceData); } catch (e) { console.error('Failed to parse invoice data string:', e); return (
Invalid invoice data format.
); } } else if (typeof invoiceData === 'object' && invoiceData !== null) { parsedInvoice = invoiceData; } // Check if we have valid invoice data if (!parsedInvoice || typeof parsedInvoice !== 'object') { return (
No structured invoice data available.
); } // Extract items array let items = []; if (parsedInvoice.items && Array.isArray(parsedInvoice.items)) { items = parsedInvoice.items; } else if (Array.isArray(parsedInvoice)) { items = parsedInvoice; } else if (parsedInvoice.items && typeof parsedInvoice.items === 'object') { items = [parsedInvoice.items]; // Wrap single item in array } else { // Try to find any array in the object for (const key in parsedInvoice) { if (Array.isArray(parsedInvoice[key])) { items = parsedInvoice[key]; break; } } } // If we still don't have items, check if the object itself looks like an item if (items.length === 0 && parsedInvoice.item || parsedInvoice.description || parsedInvoice.name) { items = [parsedInvoice]; } // If we still don't have items, show a message if (items.length === 0) { return (
No invoice items found in the data.
); } // Calculate subtotal from items const subtotal = items.reduce((sum: number, item: any) => { const quantity = parseFloat(item?.quantity || 1); const price = parseFloat(item?.unit_price || item?.price || 0); return sum + (quantity * price); }, 0); // Get tax, tip and total const tax = parseFloat(parsedInvoice.tax || parsedInvoice.taxAmount || 0); const tip = parseFloat(parsedInvoice.tip || parsedInvoice.tipAmount || 0); const total = parseFloat(parsedInvoice.total || 0) || (subtotal + tax + tip); // Render the invoice table return (
{items.map((item: any, index: number) => { // Ensure we're not trying to render an object directly const itemName = typeof item?.item === 'object' ? JSON.stringify(item.item) : (item?.item || item?.description || item?.name || 'N/A'); const quantity = parseFloat(item?.quantity || 1); const unitPrice = parseFloat(item?.unit_price || item?.price || 0); const itemTotal = quantity * unitPrice; return ( ); })} {tax > 0 && ( )} {tip > 0 && ( )}
Item Quantity Price Total
{itemName} {quantity} ${unitPrice.toFixed(2)} ${itemTotal.toFixed(2)}
Subtotal: ${subtotal.toFixed(2)}
Tax: ${tax.toFixed(2)}
Tip: ${tip.toFixed(2)}
Total: ${total.toFixed(2)}
{parsedInvoice.vendor && (
Vendor: {parsedInvoice.vendor}
)}
); } catch (error) { console.error('Error rendering invoice table:', error); return (
An unexpected error occurred while processing the invoice.
); } }; const EventRequestDetails = ({ request, onClose, onStatusChange }: EventRequestDetailsProps): React.ReactNode => { const [activeTab, setActiveTab] = useState<'details' | 'pr' | 'funding'>('details'); const [status, setStatus] = useState<"submitted" | "pending" | "completed" | "declined">(request.status); const [isStatusChanging, setIsStatusChanging] = useState(false); // Format date for display const formatDate = (dateString: string) => { if (!dateString) return 'Not specified'; try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return dateString; } }; // Get status badge class based on status const getStatusBadge = (status?: "submitted" | "pending" | "completed" | "declined") => { if (!status) return 'badge-warning'; switch (status) { case 'completed': return 'badge-success'; case 'declined': return 'badge-error'; case 'pending': return 'badge-warning'; case 'submitted': return 'badge-info'; default: return 'badge-warning'; } }; // Handle status change const handleStatusChange = async (newStatus: "submitted" | "pending" | "completed" | "declined") => { setIsStatusChanging(true); await onStatusChange(request.id, newStatus); setStatus(newStatus); setIsStatusChanging(false); }; return (
{/* Header */}

{request.name}

{/* Status and controls */}
Status: {status || 'Pending'}
Requested by: {request.requested_user_expand?.name || request.requested_user || 'Unknown'}
{/* Tabs */}
setActiveTab('details')} > Event Details setActiveTab('pr')} > PR Materials setActiveTab('funding')} > AS Funding
{/* Content */}
{/* Event Details Tab */} {activeTab === 'details' && (

Event Name

{request.name}

Location

{request.location || 'Not specified'}

Start Date & Time

{formatDate(request.start_date_time)}

End Date & Time

{formatDate(request.end_date_time)}

Expected Attendance

{request.expected_attendance || 'Not specified'}

Event Description

{request.event_description || 'No description provided'}

Room Booking

{request.will_or_have_room_booking ? 'Yes' : 'No'}

Food/Drinks Served

{request.food_drinks_being_served ? 'Yes' : 'No'}

Submission Date

{formatDate(request.created)}

)} {/* PR Materials Tab */} {activeTab === 'pr' && (

Flyers Needed

{request.flyers_needed ? 'Yes' : 'No'}

{request.flyers_needed && ( <>

Flyer Types

    {request.flyer_type?.map((type, index) => (
  • {type}
  • ))} {request.other_flyer_type &&
  • {request.other_flyer_type}
  • }

Advertising Start Date

{formatDate(request.flyer_advertising_start_date || '')}

Advertising Format

{request.advertising_format || 'Not specified'}

)}

Photography Needed

{request.photography_needed ? 'Yes' : 'No'}

{request.flyers_needed && ( <>

Required Logos

    {request.required_logos?.map((logo, index) => (
  • {logo}
  • ))} {(!request.required_logos || request.required_logos.length === 0) &&
  • No specific logos required
  • }

Additional Requests

{request.flyer_additional_requests || 'None'}

)}
)} {/* AS Funding Tab */} {activeTab === 'funding' && ( )}
); }; export default EventRequestDetails;