From f641ee722a3389cf7ef4103b57c0047791d06f0e Mon Sep 17 00:00:00 2001 From: chark1es Date: Sat, 8 Mar 2025 00:30:11 -0800 Subject: [PATCH] fixed a bug --- .../dashboard/Officer_EventRequestForm.astro | 419 +++++++----- .../EventRequestForm.tsx | 2 +- .../EventRequestFormPreview.tsx | 627 +++++++++++------- .../UserEventRequests.tsx | 89 ++- 4 files changed, 718 insertions(+), 419 deletions(-) diff --git a/src/components/dashboard/Officer_EventRequestForm.astro b/src/components/dashboard/Officer_EventRequestForm.astro index f5a115b..7fc73c4 100644 --- a/src/components/dashboard/Officer_EventRequestForm.astro +++ b/src/components/dashboard/Officer_EventRequestForm.astro @@ -4,6 +4,8 @@ import { Get } from "../../scripts/pocketbase/Get"; import EventRequestForm from "./Officer_EventRequestForm/EventRequestForm"; import UserEventRequests from "./Officer_EventRequestForm/UserEventRequests"; import { Collections } from "../../schemas/pocketbase/schema"; +import { DataSyncService } from "../../scripts/database/DataSyncService"; +import { EventRequestFormPreviewModal } from "./Officer_EventRequestForm/EventRequestFormPreview"; // Import the EventRequest type from UserEventRequests to ensure consistency import type { EventRequest as UserEventRequest } from "./Officer_EventRequestForm/UserEventRequests"; @@ -23,196 +25,267 @@ let error: string | null = null; // This provides initial data for server-side rendering // Client-side will use IndexedDB for data management if (auth.isAuthenticated()) { - try { - const userId = auth.getUserId(); - if (userId) { - userEventRequests = await get.getAll( - Collections.EVENT_REQUESTS, - `requested_user="${userId}"`, - "-created" - ); - } - } catch (err) { - console.error("Failed to fetch user event requests:", err); - error = "Failed to load your event requests. Please try again later."; + try { + const userId = auth.getUserId(); + if (userId) { + userEventRequests = await get.getAll( + Collections.EVENT_REQUESTS, + `requested_user="${userId}"`, + "-created", + ); } + } catch (err) { + console.error("Failed to fetch user event requests:", err); + error = "Failed to load your event requests. Please try again later."; + } } ---
-
-

Event Request Form

-

- Submit your event request at least 6 weeks before your event. After - submitting, please notify PR and/or Coordinators in the #-events - Slack channel. -

-
-

This form includes sections for:

-
    -
  • PR Materials (if needed)
  • -
  • Event Details
  • -
  • TAP Form Information
  • -
  • AS Funding (if needed)
  • -
-

- Your progress is automatically saved as you fill out the form. -

-
+
+

Event Request Form

+

+ Submit your event request at least 6 weeks before your event. After + submitting, please notify PR and/or Coordinators in the #-events Slack + channel. +

+
+

This form includes sections for:

+
    +
  • PR Materials (if needed)
  • +
  • Event Details
  • +
  • TAP Form Information
  • +
  • AS Funding (if needed)
  • +
+

+ Your progress is automatically saved as you fill out the form. +

+
- -
- Submit Event Request - View Your Submissions + + + + +
+
+
+
- -
-
- -
-
- - - + + + + + - + + diff --git a/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx b/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx index a1cece1..99ef977 100644 --- a/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx +++ b/src/components/dashboard/Officer_EventRequestForm/EventRequestForm.tsx @@ -720,7 +720,7 @@ const EventRequestForm: React.FC = () => {
- +
Ready to Submit?
diff --git a/src/components/dashboard/Officer_EventRequestForm/EventRequestFormPreview.tsx b/src/components/dashboard/Officer_EventRequestForm/EventRequestFormPreview.tsx index ac29a63..aba4b9f 100644 --- a/src/components/dashboard/Officer_EventRequestForm/EventRequestFormPreview.tsx +++ b/src/components/dashboard/Officer_EventRequestForm/EventRequestFormPreview.tsx @@ -1,15 +1,104 @@ import React, { useState, useEffect } from 'react'; -import { motion } from 'framer-motion'; +import { motion, AnimatePresence } from 'framer-motion'; import type { EventRequestFormData } from './EventRequestForm'; import type { InvoiceItem } from './InvoiceBuilder'; import type { EventRequest } from '../../../schemas/pocketbase'; import { FlyerTypes, LogoOptions, EventRequestStatus } from '../../../schemas/pocketbase'; +// Create a standalone component that can be used to show the preview as a modal +export const EventRequestFormPreviewModal: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + const [formData, setFormData] = useState(null); + + // Function to handle showing the modal + const showModal = (data: any) => { + console.log('showModal called with data', data); + setFormData(data); + setIsOpen(true); + }; + + // Add the global function to the window object directly from the component + useEffect(() => { + // Store the original function if it exists + const originalFunction = window.showEventRequestFormPreview; + + // Define the global function + window.showEventRequestFormPreview = (data: any) => { + console.log('Global showEventRequestFormPreview called with data', data); + showModal(data); + }; + + // Listen for the custom event as a fallback + const handleShowModal = (event: CustomEvent) => { + console.log('Received showEventRequestPreviewModal event', event.detail); + if (event.detail && event.detail.formData) { + showModal(event.detail.formData); + } else { + console.error('Event detail or formData is missing', event.detail); + } + }; + + // Add event listener + document.addEventListener('showEventRequestPreviewModal', handleShowModal as EventListener); + console.log('Event listener for showEventRequestPreviewModal added'); + + // Clean up + return () => { + // Restore the original function if it existed + if (originalFunction) { + window.showEventRequestFormPreview = originalFunction; + } else { + // Otherwise delete our function + delete window.showEventRequestFormPreview; + } + + document.removeEventListener('showEventRequestPreviewModal', handleShowModal as EventListener); + console.log('Event listener for showEventRequestPreviewModal removed'); + }; + }, []); // Empty dependency array - only run once on mount + + const handleClose = () => { + console.log('Modal closed'); + setIsOpen(false); + }; + + // Force the modal to be in the document body to avoid nesting issues + return ( +
+ +
+ ); +}; + interface EventRequestFormPreviewProps { formData?: EventRequestFormData; // Optional prop to directly pass form data + isOpen?: boolean; // Control whether the modal is open + onClose?: () => void; // Callback when modal is closed + isModal?: boolean; // Whether to render as a modal or inline component } -const EventRequestFormPreview: React.FC = ({ formData: propFormData }) => { +const EventRequestFormPreview: React.FC = ({ + formData: propFormData, + isOpen = true, + onClose = () => { }, + isModal = false +}) => { const [formData, setFormData] = useState(propFormData || null); const [loading, setLoading] = useState(!propFormData); @@ -55,23 +144,6 @@ const EventRequestFormPreview: React.FC = ({ formD }; }, [propFormData]); - if (loading) { - return ( -
-
-
- ); - } - - if (!formData) { - return ( -
-

No Form Data Available

-

Please fill out the form to see a preview.

-
- ); - } - // Format date and time for display const formatDateTime = (dateTimeString: string) => { if (!dateTimeString) return 'Not specified'; @@ -84,242 +156,335 @@ const EventRequestFormPreview: React.FC = ({ formD } }; - return ( - -
-

Event Request Preview

-

- This is a preview of your event request. Please review all information before submitting. -

+ // Handle click on the backdrop to close the modal + const handleBackdropClick = (e: React.MouseEvent) => { + if (e.target === e.currentTarget) { + onClose(); + } + }; - {/* Event Details Section */} -
-

- Event Details -

-
-
-

Event Name

-

{formData.name || 'Not specified'}

-
-
-

Location

-

{formData.location || 'Not specified'}

-
-
-

Event Description

-

{formData.event_description || 'Not specified'}

-
-
-

Start Date & Time

-

{formatDateTime(formData.start_date_time)}

-
-
-

End Date & Time

-

{formatDateTime(formData.end_date_time)}

-
-
-

Room Booking

-

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

-
-
-

Expected Attendance

-

{formData.expected_attendance || 'Not specified'}

-
-
+ // Render the content of the preview + const renderContent = () => { + if (loading) { + return ( +
+
+ ); + } - {/* PR Materials Section */} - {formData.flyers_needed && ( + if (!formData) { + return ( +
+

No Form Data Available

+

Please fill out the form to see a preview.

+
+ ); + } + + return ( +
+
+ {isModal && ( + <> +

Event Request Preview

+

+ This is a preview of your event request. Please review all information before submitting. +

+ + )} + + {/* Event Details Section */}

- PR Materials + Event Details

-

Flyer Types

-
    - {formData.flyer_type.map((type, index) => ( -
  • - {type === 'digital_with_social' && 'Digital flyer with social media advertising'} - {type === 'digital_no_social' && 'Digital flyer without social media advertising'} - {type === 'physical_with_advertising' && 'Physical flyer with advertising'} - {type === 'physical_no_advertising' && 'Physical flyer without advertising'} - {type === 'newsletter' && 'Newsletter'} - {type === 'other' && 'Other: ' + formData.other_flyer_type} -
  • - ))} -
+

Event Name

+

{formData.name || 'Not specified'}

-

Advertising Start Date

-

{formData.flyer_advertising_start_date || 'Not specified'}

+

Location

+

{formData.location || 'Not specified'}

-

Required Logos

-
- {formData.required_logos.map((logo, index) => ( - {logo} - ))} - {formData.required_logos.length === 0 &&

None specified

} -
+

Event Description

+

{formData.event_description || 'Not specified'}

-

Advertising Format

+

Start Date & Time

+

{formatDateTime(formData.start_date_time)}

+
+
+

End Date & Time

+

{formatDateTime(formData.end_date_time)}

+
+
+

Room Booking

+

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

+
+
+

Expected Attendance

+

{formData.expected_attendance || 'Not specified'}

+
+
+
+ + {/* PR Materials Section */} + {formData.flyers_needed && ( +
+

+ PR Materials +

+
+
+

Flyer Types

+
    + {formData.flyer_type.map((type, index) => ( +
  • + {type === 'digital_with_social' && 'Digital flyer with social media advertising'} + {type === 'digital_no_social' && 'Digital flyer without social media advertising'} + {type === 'physical_with_advertising' && 'Physical flyer with advertising'} + {type === 'physical_no_advertising' && 'Physical flyer without advertising'} + {type === 'newsletter' && 'Newsletter'} + {type === 'other' && 'Other: ' + formData.other_flyer_type} +
  • + ))} +
+
+
+

Advertising Start Date

+

{formData.flyer_advertising_start_date || 'Not specified'}

+
+
+

Required Logos

+
+ {formData.required_logos.map((logo, index) => ( + {logo} + ))} + {formData.required_logos.length === 0 &&

None specified

} +
+
+
+

Advertising Format

+

+ {formData.advertising_format === 'pdf' && 'PDF'} + {formData.advertising_format === 'jpeg' && 'JPEG'} + {formData.advertising_format === 'png' && 'PNG'} + {formData.advertising_format === 'does_not_matter' && 'Does not matter'} + {!formData.advertising_format && 'Not specified'} +

+
+
+

Photography Needed

+

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

+
+ {formData.flyer_additional_requests && ( +
+

Additional Requests

+

{formData.flyer_additional_requests}

+
+ )} +
+
+ )} + + {/* TAP Form Section */} +
+

+ TAP Form Details +

+
+
+

Expected Attendance

+

{formData.expected_attendance || 'Not specified'}

+
+
+

Room Booking

- {formData.advertising_format === 'pdf' && 'PDF'} - {formData.advertising_format === 'jpeg' && 'JPEG'} - {formData.advertising_format === 'png' && 'PNG'} - {formData.advertising_format === 'does_not_matter' && 'Does not matter'} - {!formData.advertising_format && 'Not specified'} + {formData.room_booking ? formData.room_booking.name : 'No file uploaded'}

-

Photography Needed

-

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

+

AS Funding Required

+

{formData.as_funding_required ? 'Yes' : 'No'}

- {formData.flyer_additional_requests && ( -
-

Additional Requests

-

{formData.flyer_additional_requests}

-
- )} -
-
- )} - - {/* TAP Form Section */} -
-

- TAP Form Details -

-
-
-

Expected Attendance

-

{formData.expected_attendance || 'Not specified'}

-
-
-

Room Booking

-

- {formData.room_booking ? formData.room_booking.name : 'No file uploaded'} -

-
-
-

AS Funding Required

-

{formData.as_funding_required ? 'Yes' : 'No'}

-
-
-

Food/Drinks Being Served

-

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

-
-
-
- - {/* AS Funding Section */} - {formData.as_funding_required && ( -
-

- AS Funding Details -

- -
-

Vendor

-

{formData.invoiceData.vendor || 'Not specified'}

+

Food/Drinks Being Served

+

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

- - {formData.invoiceData.items.length > 0 ? ( -
- - - - - - - - - - - {formData.invoiceData.items.map((item: InvoiceItem) => ( - - - - - - - ))} - - - - - - - - - - - - - - - - - - - -
DescriptionQtyUnit PriceAmount
{item.description}{item.quantity}${item.unitPrice.toFixed(2)}${item.amount.toFixed(2)}
Subtotal:${formData.invoiceData.subtotal.toFixed(2)}
Tax ({formData.invoiceData.taxRate}%):${formData.invoiceData.taxAmount.toFixed(2)}
Tip ({formData.invoiceData.tipPercentage}%):${formData.invoiceData.tipAmount.toFixed(2)}
Total:${formData.invoiceData.total.toFixed(2)}
-
- ) : ( -
-
No invoice items have been added yet.
-
- )} - -
-

JSON Format (For Submission):

-
-                                {JSON.stringify({
-                                    items: formData.invoiceData.items.map(item => ({
-                                        item: item.description,
-                                        quantity: item.quantity,
-                                        unit_price: item.unitPrice
-                                    })),
-                                    tax: formData.invoiceData.taxAmount,
-                                    tip: formData.invoiceData.tipAmount,
-                                    total: formData.invoiceData.total,
-                                    vendor: formData.invoiceData.vendor
-                                }, null, 2)}
-                            
-

- This is the structured format that will be submitted to our database. - It ensures that your invoice data is properly organized and can be - processed correctly by our system. -

-
- -
-

Invoice Files

- {formData.invoice_files && formData.invoice_files.length > 0 ? ( -
- {formData.invoice_files.map((file, index) => ( -

{file.name}

- ))} -
- ) : formData.invoice ? ( -

{formData.invoice.name}

- ) : ( -

No files uploaded

- )} -
- )} + + {/* AS Funding Section */} + {formData.as_funding_required && ( +
+

+ AS Funding Details +

+ +
+
+

Vendor

+

{formData.invoiceData.vendor || 'Not specified'}

+
+
+ + {formData.invoiceData.items.length > 0 ? ( +
+ + + + + + + + + + + {formData.invoiceData.items.map((item: InvoiceItem) => ( + + + + + + + ))} + + + + + + + + + + + + + + + + + + + +
DescriptionQtyUnit PriceAmount
{item.description}{item.quantity}${item.unitPrice.toFixed(2)}${item.amount.toFixed(2)}
Subtotal:${formData.invoiceData.subtotal.toFixed(2)}
Tax ({formData.invoiceData.taxRate}%):${formData.invoiceData.taxAmount.toFixed(2)}
Tip ({formData.invoiceData.tipPercentage}%):${formData.invoiceData.tipAmount.toFixed(2)}
Total:${formData.invoiceData.total.toFixed(2)}
+
+ ) : ( +
+
No invoice items have been added yet.
+
+ )} + +
+

JSON Format (For Submission):

+
+                                    {JSON.stringify({
+                                        items: formData.invoiceData.items.map(item => ({
+                                            item: item.description,
+                                            quantity: item.quantity,
+                                            unit_price: item.unitPrice
+                                        })),
+                                        tax: formData.invoiceData.taxAmount,
+                                        tip: formData.invoiceData.tipAmount,
+                                        total: formData.invoiceData.total,
+                                        vendor: formData.invoiceData.vendor
+                                    }, null, 2)}
+                                
+

+ This is the structured format that will be submitted to our database. + It ensures that your invoice data is properly organized and can be + processed correctly by our system. +

+
+ +
+

Invoice Files

+ {formData.invoice_files && formData.invoice_files.length > 0 ? ( +
+ {formData.invoice_files.map((file, index) => ( +

{file.name}

+ ))} +
+ ) : formData.invoice ? ( +

{formData.invoice.name}

+ ) : ( +

No files uploaded

+ )} +
+
+ )} +
- + ); + }; + + // If not a modal, render the content directly + if (!isModal) { + return ( + + {renderContent()} + + ); + } + + // If it's a modal, render with the modal wrapper + return ( + + {isOpen && ( + + e.stopPropagation()} + style={{ + position: 'relative', + zIndex: 100000 + }} + > +
+

Event Request Preview

+ +
+
+ {renderContent()} +
+
+
+ )} +
); }; diff --git a/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx b/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx index fb7d4bf..380b96c 100644 --- a/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx +++ b/src/components/dashboard/Officer_EventRequestForm/UserEventRequests.tsx @@ -5,6 +5,13 @@ import { DataSyncService } from '../../../scripts/database/DataSyncService'; import { Collections } from '../../../schemas/pocketbase/schema'; import type { EventRequest as SchemaEventRequest } from '../../../schemas/pocketbase'; +// 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; @@ -98,15 +105,37 @@ const UserEventRequests: React.FC = ({ eventRequests: in switch (status.toLowerCase()) { case 'approved': - return 'badge-success'; + case 'completed': + return 'badge-success text-white'; case 'rejected': - return 'badge-error'; + case 'declined': + return 'badge-error text-white'; case 'pending': - return 'badge-warning'; + return 'badge-warning text-black'; case 'submitted': - return 'badge-info'; + return 'badge-info text-white'; default: - return 'badge-warning'; + 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'; } }; @@ -226,7 +255,7 @@ const UserEventRequests: React.FC = ({ eventRequests: in {eventRequests.map((request) => ( - + {request.name} {formatDate(request.start_date_time)} {request.location} @@ -274,7 +303,7 @@ const UserEventRequests: React.FC = ({ eventRequests: in 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" + className={`card bg-base-200 shadow-sm hover:shadow-md transition-shadow border-l-4 ${getCardBorderClass(request.status)}`} >
@@ -371,13 +400,45 @@ const UserEventRequests: React.FC = ({ eventRequests: in onClick={(e) => e.stopPropagation()} >
-

{selectedRequest.name}

- +
+

{selectedRequest.name}

+ + {selectedRequest.status || 'Pending'} + +
+
+ + +