diff --git a/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx b/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx index f9a7891..8af281e 100644 --- a/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx +++ b/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx @@ -1,12 +1,11 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState } from 'react'; import { motion } from 'framer-motion'; import { Get } from '../../../scripts/pocketbase/Get'; import { Authentication } from '../../../scripts/pocketbase/Authentication'; import toast from 'react-hot-toast'; -import { createPortal } from 'react-dom'; // Define the event request interface -export interface EventRequest { +interface EventRequest { id: string; name: string; location: string; @@ -19,7 +18,7 @@ export interface EventRequest { food_drinks_being_served: boolean; created: string; updated: string; - status: string; // Status field from PocketBase: submitted, pending, completed, declined + status?: string; // Status might not be in the schema yet flyer_type?: string[]; other_flyer_type?: string; flyer_advertising_start_date?: string; @@ -41,15 +40,6 @@ const UserEventRequests: React.FC = ({ eventRequests: in const [selectedRequest, setSelectedRequest] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [isRefreshing, setIsRefreshing] = useState(false); - const [isMounted, setIsMounted] = useState(false); - - // Set mounted state when component mounts - useEffect(() => { - setIsMounted(true); - return () => { - setIsMounted(false); - }; - }, []); // Format date for display const formatDate = (dateString: string) => { @@ -69,39 +59,31 @@ const UserEventRequests: React.FC = ({ eventRequests: in }; // Get status badge class based on status - const getStatusBadge = (status: string) => { - switch (status?.toLowerCase()) { - case 'completed': + const getStatusBadge = (status?: string) => { + if (!status) return 'badge-warning'; + + switch (status.toLowerCase()) { + case 'approved': return 'badge-success'; - case 'declined': + case 'rejected': return 'badge-error'; case 'pending': return 'badge-warning'; - case 'submitted': + case 'in progress': return 'badge-info'; default: - return 'badge-warning'; // Default to warning for unknown status + return 'badge-warning'; } }; - // Format status for display - const formatStatus = (status: string) => { - if (!status) return 'Pending'; - - // Capitalize first letter - return status.charAt(0).toUpperCase() + status.slice(1).toLowerCase(); - }; - // Open modal with event request details const openDetailModal = (request: EventRequest) => { - console.log('Opening modal for request:', request.id, request.name); setSelectedRequest(request); setIsModalOpen(true); }; // Close modal const closeModal = () => { - console.log('Closing modal'); setIsModalOpen(false); setSelectedRequest(null); }; @@ -142,70 +124,129 @@ const UserEventRequests: React.FC = ({ eventRequests: in } }; - // Auto-refresh when component mounts and when refreshSubmissions event is triggered - useEffect(() => { - // Refresh on mount - refreshEventRequests(); + if (eventRequests.length === 0) { + return ( +
+

No Event Requests Found

+

You haven't submitted any event requests yet.

+

Use the form above to submit a new event request.

+ +
+ ); + } - // Listen for refreshSubmissions event - const handleRefreshEvent = () => { - refreshEventRequests(); - }; + return ( + +
+

Your Submissions

+ +
- document.addEventListener('refreshSubmissions', handleRefreshEvent); +
+ + + + + + + + + + + + + + + {eventRequests.map((request) => ( + + + + + + + + + + + ))} + +
Event NameDateLocationPR MaterialsAS FundingSubmittedStatusActions
{request.name}{formatDate(request.start_date_time)}{request.location} + {request.flyers_needed ? ( + Yes + ) : ( + No + )} + + {request.as_funding_required ? ( + Yes + ) : ( + No + )} + {formatDate(request.created)} + + {request.status || 'Pending'} + + + +
+
- // Clean up event listener - return () => { - document.removeEventListener('refreshSubmissions', handleRefreshEvent); - }; - }, []); +
+

About Your Submissions

+
    +
  • Event requests are typically reviewed within 1-2 business days.
  • +
  • You'll receive email notifications when your request status changes.
  • +
  • For urgent inquiries, please contact the PR team or coordinators in the #-events Slack channel.
  • +
+
- // Function to render the modal using a portal - const renderModal = () => { - if (!isModalOpen || !selectedRequest || !isMounted) { - console.log('Modal not rendered - conditions not met:', { isModalOpen, hasSelectedRequest: !!selectedRequest, isMounted }); - return null; - } - - console.log('Rendering modal for:', selectedRequest.name); - - // Check if document.body is available (important for SSR environments) - if (typeof document === 'undefined' || !document.body) { - console.error('document.body is not available'); - - // Fallback to direct rendering if portal can't be used - return ( -
-
-
-

{selectedRequest.name}

- -
-

Event details are available. Please close and try again if content is not displaying properly.

-
-
- ); - } - - // Use try-catch to handle any potential errors during portal creation - try { - return createPortal( -
{ - // Close modal when clicking outside the modal content - if (e.target === e.currentTarget) { - closeModal(); - } - }}> + {/* Event Request Detail Modal */} + {isModalOpen && selectedRequest && ( +
e.stopPropagation()} // Prevent clicks inside the modal from closing it >
@@ -316,46 +357,185 @@ const UserEventRequests: React.FC = ({ eventRequests: in

Vendor

- {(() => { - try { - if (typeof selectedRequest.invoice_data === 'string') { - return JSON.parse(selectedRequest.invoice_data).vendor || 'Not specified'; - } else if (typeof selectedRequest.invoice_data === 'object') { - return selectedRequest.invoice_data.vendor || 'Not specified'; - } - return 'Not specified'; - } catch (error) { - console.error('Error parsing invoice data:', error); - return 'Not specified (Error parsing data)'; - } - })()} + {selectedRequest.invoice_data.vendor || 'Not specified'}

)}

Itemized Invoice

-
-                                                {(() => {
-                                                    try {
-                                                        if (typeof selectedRequest.itemized_invoice === 'object') {
-                                                            return JSON.stringify(selectedRequest.itemized_invoice, null, 2);
-                                                        } else if (typeof selectedRequest.itemized_invoice === 'string') {
-                                                            // Try to parse it as JSON to pretty-print it
-                                                            try {
-                                                                const parsed = JSON.parse(selectedRequest.itemized_invoice);
-                                                                return JSON.stringify(parsed, null, 2);
-                                                            } catch {
-                                                                // If it's not valid JSON, just return the string
-                                                                return selectedRequest.itemized_invoice;
+                                            {(() => {
+                                                try {
+                                                    let invoiceData: any = null;
+
+                                                    // Parse the invoice data if it's a string, or use it directly if it's an object
+                                                    if (typeof selectedRequest.itemized_invoice === 'string') {
+                                                        try {
+                                                            invoiceData = JSON.parse(selectedRequest.itemized_invoice);
+                                                        } catch (e) {
+                                                            console.error('Failed to parse invoice JSON:', e);
+                                                            return (
+                                                                
+                                                                    {selectedRequest.itemized_invoice || 'Not provided'}
+                                                                
+ ); + } + } else if (typeof selectedRequest.itemized_invoice === 'object') { + invoiceData = selectedRequest.itemized_invoice; + } + + // If we have valid invoice data with items + if (invoiceData && Array.isArray(invoiceData.items) && invoiceData.items.length > 0) { + // Calculate total from items if not provided or if NaN + let calculatedTotal = 0; + + // Try to use the provided total first + if (invoiceData.total !== undefined) { + const parsedTotal = typeof invoiceData.total === 'string' + ? parseFloat(invoiceData.total) + : invoiceData.total; + + if (!isNaN(parsedTotal)) { + calculatedTotal = parsedTotal; } } - return 'Not provided'; - } catch (error) { - console.error('Error displaying itemized invoice:', error); - return 'Error displaying invoice data'; + + // If total is NaN or not provided, calculate from items + if (calculatedTotal === 0 || isNaN(calculatedTotal)) { + calculatedTotal = invoiceData.items.reduce((sum: number, item: any) => { + const quantity = typeof item.quantity === 'string' + ? parseFloat(item.quantity) + : (item.quantity || 1); + + const unitPrice = typeof item.unit_price === 'string' + ? parseFloat(item.unit_price) + : (item.unit_price || 0); + + const itemTotal = !isNaN(quantity) && !isNaN(unitPrice) + ? quantity * unitPrice + : 0; + + return sum + itemTotal; + }, 0); + + // Add tax and tip if available + if (invoiceData.tax && !isNaN(parseFloat(invoiceData.tax))) { + calculatedTotal += parseFloat(invoiceData.tax); + } + + if (invoiceData.tip && !isNaN(parseFloat(invoiceData.tip))) { + calculatedTotal += parseFloat(invoiceData.tip); + } + } + + return ( +
+ + + + + + + + + + + {invoiceData.items.map((item: any, index: number) => { + const quantity = typeof item.quantity === 'string' + ? parseFloat(item.quantity) + : (item.quantity || 1); + + const unitPrice = typeof item.unit_price === 'string' + ? parseFloat(item.unit_price) + : (item.unit_price || 0); + + const itemTotal = !isNaN(quantity) && !isNaN(unitPrice) + ? quantity * unitPrice + : 0; + + return ( + + + + + + + ); + })} + + + {invoiceData.tax !== undefined && ( + + + + + )} + {invoiceData.tip !== undefined && ( + + + + + )} + + + + + +
ItemQtyPriceTotal
{item.item || 'Unnamed item'}{!isNaN(quantity) ? quantity : 1}${!isNaN(unitPrice) ? unitPrice.toFixed(2) : '0.00'}${!isNaN(itemTotal) ? itemTotal.toFixed(2) : '0.00'}
Tax: + ${typeof invoiceData.tax === 'string' + ? (parseFloat(invoiceData.tax) || 0).toFixed(2) + : (invoiceData.tax || 0).toFixed(2)} +
Tip: + ${typeof invoiceData.tip === 'string' + ? (parseFloat(invoiceData.tip) || 0).toFixed(2) + : (invoiceData.tip || 0).toFixed(2)} +
Total: + ${!isNaN(calculatedTotal) ? calculatedTotal.toFixed(2) : '0.00'} +
+ {invoiceData.vendor && ( +
+ Vendor: {invoiceData.vendor} +
+ )} +
+ ); + } else if (invoiceData && typeof invoiceData.total !== 'undefined') { + // If we have a total but no items, show a simplified view + const total = typeof invoiceData.total === 'string' + ? parseFloat(invoiceData.total) + : invoiceData.total; + + return ( +
+
+ Total Amount: + ${!isNaN(total) ? total.toFixed(2) : '0.00'} +
+ {invoiceData.vendor && ( +
+ Vendor: {invoiceData.vendor} +
+ )} +
+ ); + } else { + // Fallback to display the JSON in a readable format + return ( +
+                                                                {typeof selectedRequest.itemized_invoice === 'object'
+                                                                    ? JSON.stringify(selectedRequest.itemized_invoice, null, 2)
+                                                                    : (selectedRequest.itemized_invoice || 'Not provided')}
+                                                            
+ ); } - })()} -
+ } catch (error) { + console.error('Error rendering invoice:', error); + return ( +
+                                                            Error displaying invoice. Please check the console for details.
+                                                        
+ ); + } + })()}
@@ -370,170 +550,13 @@ const UserEventRequests: React.FC = ({ eventRequests: in

Status

- {formatStatus(selectedRequest.status)} + {selectedRequest.status || 'Pending'}
- , - document.body - ); - } catch (error) { - console.error('Error rendering modal:', error); - return ( -
-

Error rendering modal. Please try again.

-
- ); - } - }; - - if (eventRequests.length === 0) { - return ( -
-

No Event Requests Found

-

You haven't submitted any event requests yet.

-

Use the form above to submit a new event request.

- -
- ); - } - - return ( - -
-

Your Submissions

- -
- -
- - - - - - - - - - - - - - - {eventRequests.map((request) => ( - - - - - - - - - - - ))} - -
Event NameDateLocationPR MaterialsAS FundingSubmittedStatusActions
{request.name}{formatDate(request.start_date_time)}{request.location} - {request.flyers_needed ? ( - Yes - ) : ( - No - )} - - {request.as_funding_required ? ( - Yes - ) : ( - No - )} - {formatDate(request.created)} - - {formatStatus(request.status)} - - - -
-
- -
-

About Your Submissions

-
    -
  • Event requests are typically reviewed within 1-2 business days.
  • -
  • You'll receive email notifications when your request status changes.
  • -
  • For urgent inquiries, please contact the PR team or coordinators in the #-events Slack channel.
  • -
-
- - {/* Render the modal using the portal */} - {renderModal()} - - {/* Fallback modal implementation (rendered directly in the DOM) */} - {isModalOpen && selectedRequest && ( -
-
-
-
-

{selectedRequest.name}

- -
-
-

Event: {selectedRequest.name}

-

Location: {selectedRequest.location}

-

Date: {formatDate(selectedRequest.start_date_time)}

-

Status: {formatStatus(selectedRequest.status)}

-

This is a simplified view. Please check the console for any errors if the detailed view is not working.

-
-
-
)}