ieeeucsd-org/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx
2025-05-01 13:58:14 -07:00

682 lines
No EOL
34 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { DataSyncService } from '../../../scripts/database/DataSyncService';
import { Collections } from '../../../schemas/pocketbase/schema';
import type { EventRequest as SchemaEventRequest } from '../../../schemas/pocketbase';
import { EventRequestFormPreview } from './EventRequestFormPreview';
import type { EventRequestFormData } from './EventRequestForm';
// Declare the global window interface to include our custom function
declare global {
interface Window {
showEventRequestFormPreview?: (formData: any) => void;
}
}
// Extended EventRequest interface with additional properties needed for this component
export interface EventRequest extends SchemaEventRequest {
invoice_data?: any;
}
// Helper function to convert EventRequest to EventRequestFormData
const convertToFormData = (request: EventRequest): EventRequestFormData => {
try {
// Parse itemized_invoice if it's a string
let invoiceData = {};
try {
if (request.itemized_invoice) {
if (typeof request.itemized_invoice === 'string') {
const parsedInvoice = JSON.parse(request.itemized_invoice) as Record<string, any>;
// Get or calculate subtotal from items
const subtotal = parsedInvoice.subtotal ??
(Array.isArray(parsedInvoice.items) ?
parsedInvoice.items.reduce((sum: number, item: any) => {
const amount = typeof item.amount === 'number' ? item.amount : 0;
return sum + amount;
}, 0) : 0);
// Normalize tax and tip amounts
const taxAmount = Number(parsedInvoice.taxAmount ?? parsedInvoice.tax ?? 0);
const tipAmount = Number(parsedInvoice.tipAmount ?? parsedInvoice.tip ?? 0);
// Calculate or get total
const total = parsedInvoice.total ?? (subtotal + taxAmount + tipAmount);
invoiceData = {
...parsedInvoice,
subtotal,
taxAmount,
tipAmount,
total
};
} else {
const parsedInvoice = request.itemized_invoice as Record<string, any>;
// Get or calculate subtotal from items
const subtotal = parsedInvoice.subtotal ??
(Array.isArray(parsedInvoice.items) ?
parsedInvoice.items.reduce((sum: number, item: any) => {
const amount = typeof item.amount === 'number' ? item.amount : 0;
return sum + amount;
}, 0) : 0);
// Normalize tax and tip amounts
const taxAmount = Number(parsedInvoice.taxAmount ?? parsedInvoice.tax ?? 0);
const tipAmount = Number(parsedInvoice.tipAmount ?? parsedInvoice.tip ?? 0);
// Calculate or get total
const total = parsedInvoice.total ?? (subtotal + taxAmount + tipAmount);
invoiceData = {
...parsedInvoice,
subtotal,
taxAmount,
tipAmount,
total
};
}
} else if (request.invoice_data) {
const parsedInvoice = request.invoice_data as Record<string, any>;
// Get or calculate subtotal from items
const subtotal = parsedInvoice.subtotal ??
(Array.isArray(parsedInvoice.items) ?
parsedInvoice.items.reduce((sum: number, item: any) => {
const amount = typeof item.amount === 'number' ? item.amount : 0;
return sum + amount;
}, 0) : 0);
// Normalize tax and tip amounts
const taxAmount = Number(parsedInvoice.taxAmount ?? parsedInvoice.tax ?? 0);
const tipAmount = Number(parsedInvoice.tipAmount ?? parsedInvoice.tip ?? 0);
// Calculate or get total
const total = parsedInvoice.total ?? (subtotal + taxAmount + tipAmount);
invoiceData = {
...parsedInvoice,
subtotal,
taxAmount,
tipAmount,
total
};
}
} catch (e) {
console.error('Error parsing invoice data:', e);
}
// Cast to unknown first, then to EventRequestFormData to avoid type checking
return {
name: request.name,
location: request.location,
start_date_time: request.start_date_time,
end_date_time: request.end_date_time,
event_description: request.event_description || '',
flyers_needed: request.flyers_needed || false,
photography_needed: request.photography_needed || false,
flyer_type: request.flyer_type || [],
other_flyer_type: request.other_flyer_type || '',
flyer_advertising_start_date: request.flyer_advertising_start_date || '',
advertising_format: request.advertising_format || '',
required_logos: request.required_logos || [],
other_logos: [] as File[], // EventRequest doesn't have this as files
flyer_additional_requests: request.flyer_additional_requests || '',
will_or_have_room_booking: request.will_or_have_room_booking || false,
room_booking: null,
room_booking_confirmation: [] as File[], // EventRequest doesn't have this as files
expected_attendance: request.expected_attendance || 0,
food_drinks_being_served: request.food_drinks_being_served || false,
needs_as_funding: request.as_funding_required || false,
as_funding_required: request.as_funding_required || false,
invoice: null,
invoice_files: [],
invoiceData: invoiceData,
needs_graphics: request.flyers_needed || false,
status: request.status || '',
created_by: request.requested_user || '',
id: request.id || '',
created: request.created || '',
updated: request.updated || '',
itemized_invoice: request.itemized_invoice || '',
} as unknown as EventRequestFormData;
} catch (error) {
console.error("Error converting EventRequest to EventRequestFormData:", error);
// Return a minimal valid object to prevent rendering errors
return {
name: request?.name || "Unknown Event",
location: request?.location || "",
start_date_time: request?.start_date_time || new Date().toISOString(),
end_date_time: request?.end_date_time || new Date().toISOString(),
event_description: request?.event_description || "",
flyers_needed: false,
photography_needed: false,
flyer_type: [],
other_flyer_type: "",
flyer_advertising_start_date: "",
advertising_format: "",
required_logos: [],
other_logos: [] as File[],
flyer_additional_requests: "",
will_or_have_room_booking: false,
room_booking: null,
room_booking_confirmation: [] as File[],
expected_attendance: 0,
food_drinks_being_served: false,
needs_as_funding: false,
as_funding_required: false,
invoice: null,
invoice_files: [],
invoiceData: {},
needs_graphics: false,
status: request?.status || "",
created_by: "",
id: request?.id || "",
created: request?.created || "",
updated: request?.updated || "",
itemized_invoice: ""
} as unknown as EventRequestFormData;
}
};
interface UserEventRequestsProps {
eventRequests: EventRequest[];
}
// Create a portal component for the modal to ensure it's rendered at the root level
const EventRequestModal: React.FC<{ isOpen: boolean, onClose: () => void, children: React.ReactNode }> = ({
isOpen,
onClose,
children
}) => {
if (!isOpen) return null;
return (
<div
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-[99999]"
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
width: '100vw',
height: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '1rem',
margin: 0,
overflow: 'auto'
}}
onClick={onClose}
>
<div
className="bg-base-100 rounded-xl shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-y-auto"
style={{
position: 'relative',
margin: 'auto',
zIndex: 100000
}}
onClick={(e) => e.stopPropagation()}
>
{children}
</div>
</div>
);
};
// Add a utility function to truncate text with an ellipsis
const truncateText = (text: string, maxLength: number) => {
if (!text) return '';
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
};
const UserEventRequests: React.FC<UserEventRequestsProps> = ({ eventRequests: initialEventRequests }) => {
const [eventRequests, setEventRequests] = useState<EventRequest[]>(initialEventRequests);
const [selectedRequest, setSelectedRequest] = useState<EventRequest | null>(null);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
const [viewMode, setViewMode] = useState<'table' | 'cards'>('table');
const dataSync = DataSyncService.getInstance();
// Refresh event requests
const refreshEventRequests = async () => {
setIsRefreshing(true);
try {
const auth = Authentication.getInstance();
if (!auth.isAuthenticated()) {
return;
}
const userId = auth.getUserId();
if (!userId) {
return;
}
// Use DataSyncService to get data from IndexedDB with forced sync
const updatedRequests = await dataSync.getData<EventRequest>(
Collections.EVENT_REQUESTS,
true, // Force sync
`requested_user="${userId}"`,
'-created'
);
setEventRequests(updatedRequests);
} catch (err) {
console.error('Failed to refresh event requests:', err);
} finally {
setIsRefreshing(false);
}
};
// Auto refresh on component mount
useEffect(() => {
refreshEventRequests();
}, []);
// Listen for tab visibility changes and refresh data when tab becomes visible
useEffect(() => {
const handleTabVisible = () => {
// console.log("Tab became visible, refreshing event requests...");
refreshEventRequests();
};
// Add event listener for custom dashboardTabVisible event
document.addEventListener("dashboardTabVisible", handleTabVisible);
// Clean up event listener on component unmount
return () => {
document.removeEventListener("dashboardTabVisible", handleTabVisible);
};
}, []);
// Format date for display
const formatDate = (dateString: string) => {
if (!dateString) return 'Not specified';
try {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: '2-digit'
});
} catch (e) {
return dateString;
}
};
// Get status badge class based on status
const getStatusBadge = (status?: string) => {
if (!status) return 'badge-warning';
switch (status.toLowerCase()) {
case 'approved':
case 'completed':
return 'badge-success text-white';
case 'rejected':
case 'declined':
return 'badge-error text-white';
case 'pending':
return 'badge-warning text-black';
case 'submitted':
return 'badge-info text-white';
default:
return 'badge-warning text-black';
}
};
// Get card border class based on status
const getCardBorderClass = (status?: string) => {
if (!status) return 'border-l-warning';
switch (status.toLowerCase()) {
case 'approved':
case 'completed':
return 'border-l-success';
case 'rejected':
case 'declined':
return 'border-l-error';
case 'pending':
return 'border-l-warning';
case 'submitted':
return 'border-l-info';
default:
return 'border-l-warning';
}
};
// Open modal with event request details
const openDetailModal = (request: EventRequest) => {
setSelectedRequest(request);
setIsModalOpen(true);
};
// Close modal
const closeModal = () => {
setIsModalOpen(false);
setSelectedRequest(null);
};
if (eventRequests.length === 0) {
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className="bg-base-200 rounded-xl p-8 text-center shadow-sm"
>
<div className="flex flex-col items-center justify-center py-6">
<svg xmlns="http://www.w3.org/2000/svg" className="h-16 w-16 text-base-content/30 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<h3 className="text-xl font-semibold mb-3">No Event Requests Found</h3>
<p className="text-base-content/60 mb-6 max-w-md">You haven't submitted any event requests yet. Use the form above to submit a new event request.</p>
<button
className="btn btn-outline btn-sm gap-2"
onClick={refreshEventRequests}
disabled={isRefreshing}
>
{isRefreshing ? (
<>
<span className="loading loading-spinner loading-xs"></span>
Refreshing...
</>
) : (
<>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Refresh
</>
)}
</button>
</div>
</motion.div>
);
}
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="space-y-6"
>
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4">
<h3 className="text-lg font-semibold">Your Submissions</h3>
<div className="flex items-center gap-3">
<div className="join">
<button
className={`join-item btn btn-sm ${viewMode === 'table' ? 'btn-active' : ''}`}
onClick={() => setViewMode('table')}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
</button>
<button
className={`join-item btn btn-sm ${viewMode === 'cards' ? 'btn-active' : ''}`}
onClick={() => setViewMode('cards')}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
</button>
</div>
<button
className="btn btn-outline btn-sm gap-2"
onClick={refreshEventRequests}
disabled={isRefreshing}
>
{isRefreshing ? (
<>
<span className="loading loading-spinner loading-xs"></span>
Refreshing...
</>
) : (
<>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Refresh
</>
)}
</button>
</div>
</div>
{viewMode === 'table' ? (
<div className="overflow-x-auto overflow-y-auto rounded-xl shadow-sm max-h-[70vh]">
<table className="table table-zebra w-full text-xs">
<thead className="bg-base-300/50 sticky top-0 z-10">
<tr>
<th className="w-[17%]">Event Name</th>
<th className="w-[16%]">Date</th>
<th className="w-[14%]">Location</th>
<th className="w-[7%] text-center">PR</th>
<th className="w-[7%] text-center">AS</th>
<th className="w-[15%]">Submitted</th>
<th className="w-[14%] text-center">Status</th>
<th className="w-[10%] px-0 text-center">View</th>
</tr>
</thead>
<tbody>
{eventRequests.map((request) => (
<tr key={request.id} className={`hover border-l-4 ${getCardBorderClass(request.status)}`}>
<td className="font-medium">
<div className="tooltip" data-tip={request.name}>
<span className="block truncate max-w-[125px]">
{truncateText(request.name, 18)}
</span>
</div>
</td>
<td>
<div className="tooltip" data-tip={formatDate(request.start_date_time)}>
<span className="block truncate max-w-[110px]">
{formatDate(request.start_date_time)}
</span>
</div>
</td>
<td>
<div className="tooltip" data-tip={request.location}>
<span className="block truncate max-w-[90px]">
{truncateText(request.location, 14)}
</span>
</div>
</td>
<td className="text-center">
{request.flyers_needed ? (
<span className="badge badge-success badge-sm">Yes</span>
) : (
<span className="badge badge-ghost badge-sm">No</span>
)}
</td>
<td className="text-center">
{request.as_funding_required ? (
<span className="badge badge-success badge-sm">Yes</span>
) : (
<span className="badge badge-ghost badge-sm">No</span>
)}
</td>
<td>
<div className="tooltip" data-tip={formatDate(request.created)}>
<span className="block truncate max-w-[100px]">
{formatDate(request.created)}
</span>
</div>
</td>
<td className="text-center">
<span className={`badge ${getStatusBadge(request.status)} badge-sm`}>
{request.status || 'Submitted'}
</span>
</td>
<td className="text-center px-0">
<div className="flex justify-center">
<button
className="btn btn-xs btn-ghost btn-circle tooltip flex items-center justify-center p-0"
data-tip="View Details"
onClick={(e) => {
e.stopPropagation();
openDetailModal(request);
}}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{eventRequests.map((request) => (
<motion.div
key={request.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={`card bg-base-200 shadow-sm hover:shadow-md transition-shadow border-l-4 ${getCardBorderClass(request.status)}`}
>
<div className="card-body p-5">
<div className="flex justify-between items-start">
<h3 className="card-title text-base tooltip" data-tip={request.name}>
<span className="block truncate max-w-[180px]">
{truncateText(request.name, 25)}
</span>
</h3>
<span className={`badge ${getStatusBadge(request.status)}`}>
{request.status || 'Pending'}
</span>
</div>
<div className="space-y-2 mt-2 text-sm">
<div className="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-base-content/60 mt-0.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<span className="truncate">{formatDate(request.start_date_time)}</span>
</div>
<div className="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-base-content/60 mt-0.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span className="truncate tooltip" data-tip={request.location}>
{truncateText(request.location, 25)}
</span>
</div>
</div>
<div className="flex flex-wrap gap-2 mt-3">
{request.flyers_needed && (
<span className="badge badge-outline badge-sm">PR Materials</span>
)}
{request.as_funding_required && (
<span className="badge badge-outline badge-sm">AS Funding</span>
)}
{request.photography_needed && (
<span className="badge badge-outline badge-sm">Photography</span>
)}
</div>
<div className="card-actions justify-end mt-4">
<button
className="btn btn-sm btn-circle btn-ghost text-primary tooltip flex items-center justify-center"
data-tip="View Details"
onClick={(e) => {
e.stopPropagation();
openDetailModal(request);
}}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</button>
</div>
</div>
</motion.div>
))}
</div>
)}
<div className="bg-base-300/30 p-5 rounded-xl text-sm shadow-sm">
<h3 className="font-semibold mb-3 flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
About Your Submissions
</h3>
<ul className="space-y-2 ml-2">
<li className="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-base-content/60 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Event requests are typically reviewed within 1-2 business days.
</li>
<li className="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-base-content/60 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
You'll receive email notifications when your request status changes.
</li>
<li className="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-base-content/60 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
For urgent inquiries, please contact the PR team or coordinators in the #-events Slack channel.
</li>
</ul>
</div>
{/* Use the new portal component for the modal */}
{isModalOpen && selectedRequest && (
<EventRequestModal
isOpen={isModalOpen}
onClose={closeModal}
>
<div className="sticky top-0 z-10 bg-base-100 px-6 py-4 border-b border-base-300 flex justify-between items-center">
<div className="flex items-center gap-2">
<h2 className="text-xl font-bold text-base-content">{selectedRequest.name}</h2>
<span className={`badge ${getStatusBadge(selectedRequest.status)}`}>
{selectedRequest.status || 'Pending'}
</span>
</div>
<div className="flex items-center gap-2">
<button
className="btn btn-sm btn-circle"
onClick={closeModal}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<div className="p-6">
{selectedRequest ? (
<EventRequestFormPreview
formData={convertToFormData(selectedRequest)}
isModal={true}
/>
) : (
<div className="flex items-center justify-center h-64">
<div className="loading loading-spinner loading-lg text-primary"></div>
</div>
)}
</div>
</EventRequestModal>
)}
</motion.div>
);
};
export default UserEventRequests;