import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import EventRequestDetails from './EventRequestDetails'; import EventRequestManagementTable from './EventRequestManagementTable'; import { Update } from '../../../scripts/pocketbase/Update'; import { Collections, EventRequestStatus } from '../../../schemas/pocketbase/schema'; import { DataSyncService } from '../../../scripts/database/DataSyncService'; import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Get } from '../../../scripts/pocketbase/Get'; import { toast } from 'react-hot-toast'; import type { EventRequest } from '../../../schemas/pocketbase/schema'; // Extended EventRequest interface to include expanded fields that might come from the API interface ExtendedEventRequest extends Omit { status: "submitted" | "pending" | "completed" | "declined"; requested_user_expand?: { name: string; email: string; }; expand?: { requested_user?: { id: string; name: string; email: string; emailVisibility?: boolean; [key: string]: any; }; [key: string]: any; }; } interface EventRequestModalProps { eventRequests: ExtendedEventRequest[]; } // Helper to refresh user data in request objects const refreshUserData = async (requests: ExtendedEventRequest[]): Promise => { try { const get = Get.getInstance(); const updatedRequests = [...requests]; const userCache: Record = {}; // Cache to avoid fetching the same user multiple times for (let i = 0; i < updatedRequests.length; i++) { const request = updatedRequests[i]; if (request.requested_user) { try { // Check if we've already fetched this user let typedUserData; if (userCache[request.requested_user]) { typedUserData = userCache[request.requested_user]; } else { // Fetch full user details for each request with expanded options const userData = await get.getOne('users', request.requested_user); // Type assertion to ensure we have the correct user data properties typedUserData = userData as { id: string; name: string; email: string; [key: string]: any; }; // Store in cache userCache[request.requested_user] = typedUserData; } // Update expand object with user data if (!request.expand) request.expand = {}; request.expand.requested_user = { ...typedUserData, emailVisibility: true // Force this to be true for UI purposes }; // Update the requested_user_expand property request.requested_user_expand = { name: typedUserData.name || 'Unknown', email: typedUserData.email || '(No email available)' }; } catch (err) { console.error(`Error fetching user data for request ${request.id}:`, err); // Ensure we have fallback values even if the API call fails if (!request.expand) request.expand = {}; if (!request.expand.requested_user) { request.expand.requested_user = { id: request.requested_user, name: 'Unknown', email: 'Unknown', emailVisibility: true }; } if (!request.requested_user_expand) { request.requested_user_expand = { name: 'Unknown', email: 'Unknown' }; } } } } return updatedRequests; } catch (err) { console.error('Error refreshing user data:', err); return requests; } }; // Wrapper component for EventRequestManagementTable that handles string to function conversion const TableWrapper: React.FC<{ eventRequests: ExtendedEventRequest[]; handleSelectRequest: (request: ExtendedEventRequest) => void; handleStatusChange: (id: string, status: "submitted" | "pending" | "completed" | "declined") => Promise; }> = ({ eventRequests, handleSelectRequest, handleStatusChange }) => { return ( ); }; const EventRequestModal: React.FC = ({ eventRequests }) => { // Define animation delay as a constant to keep it consistent const ANIMATION_DELAY = "0.3s"; const [selectedRequest, setSelectedRequest] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [localEventRequests, setLocalEventRequests] = useState(eventRequests); const [isLoadingUserData, setIsLoadingUserData] = useState(true); // Start as true to show loading immediately // Function to refresh user data const refreshUserDataAndUpdate = async (requests: ExtendedEventRequest[] = localEventRequests) => { setIsLoadingUserData(true); try { const updatedRequests = await refreshUserData(requests); setLocalEventRequests(updatedRequests); } catch (err) { console.error('Error refreshing event request data:', err); } finally { setIsLoadingUserData(false); } }; // Immediately load user data on mount useEffect(() => { refreshUserDataAndUpdate(eventRequests); }, []); // Effect to update local state when props change useEffect(() => { // Only update if we have new eventRequests from props if (eventRequests.length > 0) { // First update with what we have from props setLocalEventRequests(prevRequests => { // Only replace if we have different data if (eventRequests.length !== prevRequests.length) { return eventRequests; } return prevRequests; }); // Then refresh user data refreshUserDataAndUpdate(eventRequests); } }, [eventRequests]); // Set up event listeners for communication with the table component useEffect(() => { const handleSelectRequest = (event: CustomEvent) => { setSelectedRequest(event.detail.request); setIsModalOpen(true); }; const handleStatusUpdated = (event: CustomEvent) => { const { id, status } = event.detail; // Update local state setLocalEventRequests(prevRequests => prevRequests.map(req => req.id === id ? { ...req, status } : req ) ); }; // Add event listeners document.addEventListener('event-request-select', handleSelectRequest as EventListener); document.addEventListener('status-updated', handleStatusUpdated as EventListener); // Clean up return () => { document.removeEventListener('event-request-select', handleSelectRequest as EventListener); document.removeEventListener('status-updated', handleStatusUpdated as EventListener); }; }, []); // Listen for dashboardTabVisible event to refresh user data useEffect(() => { const handleTabVisible = async () => { refreshUserDataAndUpdate(); }; document.addEventListener('dashboardTabVisible', handleTabVisible); return () => { document.removeEventListener('dashboardTabVisible', handleTabVisible); }; }, [localEventRequests]); const closeModal = () => { setIsModalOpen(false); setSelectedRequest(null); }; const handleStatusChange = async (id: string, status: "submitted" | "pending" | "completed" | "declined"): Promise => { try { const update = Update.getInstance(); await update.updateField("event_request", id, "status", status); // Force sync to update IndexedDB const dataSync = DataSyncService.getInstance(); await dataSync.syncCollection(Collections.EVENT_REQUESTS); // Update local state setLocalEventRequests(prevRequests => prevRequests.map(req => req.id === id ? { ...req, status } : req ) ); // Find the request to get its name const request = localEventRequests.find((req) => req.id === id); const eventName = request?.name || "Event"; // Notify success toast.success(`"${eventName}" status updated to ${status}`); // Dispatch event for other components document.dispatchEvent( new CustomEvent("status-updated", { detail: { id, status }, }) ); } catch (err) { console.error("Error updating status:", err); toast.error(`Failed to update status`); throw err; } }; // Function to handle request selection const handleSelectRequest = (request: ExtendedEventRequest) => { document.dispatchEvent( new CustomEvent("event-request-select", { detail: { request }, }) ); }; // Expose the functions globally for table component to use useEffect(() => { // @ts-ignore - Adding to window object window.handleSelectRequest = handleSelectRequest; // @ts-ignore - Adding to window object window.handleStatusChange = handleStatusChange; // @ts-ignore - Adding to window object window.refreshUserData = refreshUserDataAndUpdate; return () => { // @ts-ignore - Cleanup delete window.handleSelectRequest; // @ts-ignore - Cleanup delete window.handleStatusChange; // @ts-ignore - Cleanup delete window.refreshUserData; }; }, [localEventRequests]); return ( <> {/* Table component placed here */}
{isLoadingUserData ? (
Loading user data...
) : (
)}
{/* Modal */} {isModalOpen && selectedRequest && (
)}
); }; export default EventRequestModal;