more toast fixes

This commit is contained in:
chark1es 2025-03-01 04:56:51 -08:00
parent ef3e8f38d6
commit 5a453db3a4
10 changed files with 102 additions and 167 deletions

View file

@ -2,9 +2,6 @@
import FilePreview from "./universal/FilePreview"; import FilePreview from "./universal/FilePreview";
import EventCheckIn from "./EventsSection/EventCheckIn"; import EventCheckIn from "./EventsSection/EventCheckIn";
import EventLoad from "./EventsSection/EventLoad"; import EventLoad from "./EventsSection/EventLoad";
import { Icon } from "astro-icon/components";
import { Get } from "../../scripts/pocketbase/Get";
import { toast } from "react-hot-toast";
--- ---
<div id="" class=""> <div id="" class="">

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useState } from "react";
import { Get } from "../../../scripts/pocketbase/Get"; import { Get } from "../../../scripts/pocketbase/Get";
import { Authentication } from "../../../scripts/pocketbase/Authentication"; import { Authentication } from "../../../scripts/pocketbase/Authentication";
import { Update } from "../../../scripts/pocketbase/Update"; import { Update } from "../../../scripts/pocketbase/Update";

View file

@ -4,10 +4,6 @@ import { Get } from "../../scripts/pocketbase/Get";
import EventRequestForm from "./Officer_EventRequestForm/EventRequestForm"; import EventRequestForm from "./Officer_EventRequestForm/EventRequestForm";
import UserEventRequests from "./Officer_EventRequestForm/UserEventRequests"; import UserEventRequests from "./Officer_EventRequestForm/UserEventRequests";
import { Collections } from "../../schemas/pocketbase/schema"; import { Collections } from "../../schemas/pocketbase/schema";
import { Icon } from "astro-icon/components";
import { Create } from "../../scripts/pocketbase/Create";
import { Update } from "../../scripts/pocketbase/Update";
import { toast } from "react-hot-toast";
// Import the EventRequest type from UserEventRequests to ensure consistency // Import the EventRequest type from UserEventRequests to ensure consistency
import type { EventRequest as UserEventRequest } from "./Officer_EventRequestForm/UserEventRequests"; import type { EventRequest as UserEventRequest } from "./Officer_EventRequestForm/UserEventRequests";

View file

@ -4,7 +4,6 @@ import toast from 'react-hot-toast';
import type { EventRequestFormData } from './EventRequestForm'; import type { EventRequestFormData } from './EventRequestForm';
import InvoiceBuilder from './InvoiceBuilder'; import InvoiceBuilder from './InvoiceBuilder';
import type { InvoiceData } from './InvoiceBuilder'; import type { InvoiceData } from './InvoiceBuilder';
import type { EventRequest } from '../../../schemas/pocketbase';
// Animation variants // Animation variants
const itemVariants = { const itemVariants = {

View file

@ -4,10 +4,8 @@ import toast from 'react-hot-toast';
import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { Update } from '../../../scripts/pocketbase/Update'; import { Update } from '../../../scripts/pocketbase/Update';
import { FileManager } from '../../../scripts/pocketbase/FileManager'; import { FileManager } from '../../../scripts/pocketbase/FileManager';
import { Get } from '../../../scripts/pocketbase/Get';
import { DataSyncService } from '../../../scripts/database/DataSyncService'; import { DataSyncService } from '../../../scripts/database/DataSyncService';
import { Collections } from '../../../schemas/pocketbase/schema'; import { Collections } from '../../../schemas/pocketbase/schema';
import type { EventRequest } from '../../../schemas/pocketbase';
import { EventRequestStatus } from '../../../schemas/pocketbase'; import { EventRequestStatus } from '../../../schemas/pocketbase';
// Form sections // Form sections
@ -16,7 +14,6 @@ import EventDetailsSection from './EventDetailsSection';
import TAPFormSection from './TAPFormSection'; import TAPFormSection from './TAPFormSection';
import ASFundingSection from './ASFundingSection'; import ASFundingSection from './ASFundingSection';
import EventRequestFormPreview from './EventRequestFormPreview'; import EventRequestFormPreview from './EventRequestFormPreview';
import InvoiceBuilder from './InvoiceBuilder';
import type { InvoiceData, InvoiceItem } from './InvoiceBuilder'; import type { InvoiceData, InvoiceItem } from './InvoiceBuilder';
// Animation variants // Animation variants
@ -173,6 +170,50 @@ const EventRequestForm: React.FC = () => {
})); }));
}; };
// Add this function before the handleSubmit function
const resetForm = () => {
setFormData({
name: '',
location: '',
start_date_time: '',
end_date_time: '',
event_description: '',
flyers_needed: false,
flyer_type: [],
other_flyer_type: '',
flyer_advertising_start_date: '',
flyer_additional_requests: '',
photography_needed: false,
required_logos: [],
other_logos: [],
advertising_format: '',
will_or_have_room_booking: false,
expected_attendance: 0,
room_booking: null,
as_funding_required: false,
food_drinks_being_served: false,
itemized_invoice: '',
invoice: null,
invoice_files: [], // Reset multiple invoice files
needs_graphics: null,
needs_as_funding: false,
invoiceData: {
items: [],
subtotal: 0,
taxRate: 7.75, // Default tax rate for San Diego
taxAmount: 0,
tipPercentage: 15, // Default tip percentage
tipAmount: 0,
total: 0,
vendor: ''
},
formReviewed: false // Reset review status
});
// Reset to first step
setCurrentStep(1);
};
// Handle form submission // Handle form submission
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@ -186,9 +227,6 @@ const EventRequestForm: React.FC = () => {
setIsSubmitting(true); setIsSubmitting(true);
setError(null); setError(null);
// Show initial submitting toast
const submittingToast = toast.loading('Preparing to submit your event request...');
try { try {
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
const update = Update.getInstance(); const update = Update.getInstance();
@ -196,14 +234,14 @@ const EventRequestForm: React.FC = () => {
const dataSync = DataSyncService.getInstance(); const dataSync = DataSyncService.getInstance();
if (!auth.isAuthenticated()) { if (!auth.isAuthenticated()) {
toast.error('You must be logged in to submit an event request', { id: submittingToast }); toast.error('You must be logged in to submit an event request');
throw new Error('You must be logged in to submit an event request'); throw new Error('You must be logged in to submit an event request');
} }
// Create the event request record // Create the event request record
const userId = auth.getUserId(); const userId = auth.getUserId();
if (!userId) { if (!userId) {
toast.error('User ID not found', { id: submittingToast }); toast.error('User ID not found');
throw new Error('User ID not found'); throw new Error('User ID not found');
} }
@ -245,102 +283,53 @@ const EventRequestForm: React.FC = () => {
status: EventRequestStatus.SUBMITTED, status: EventRequestStatus.SUBMITTED,
}; };
toast.loading('Creating event request record...', { id: submittingToast }); // Create the record using the Update service
// This will send the data to the server
const record = await update.create('event_request', submissionData);
try { // Force sync the event requests collection to update IndexedDB
// Create the record using the Update service await dataSync.syncCollection(Collections.EVENT_REQUESTS);
// This will send the data to the server
const record = await update.create('event_request', submissionData);
// Force sync the event requests collection to update IndexedDB // Upload files if they exist
await dataSync.syncCollection(Collections.EVENT_REQUESTS); if (formData.other_logos.length > 0) {
await fileManager.uploadFiles('event_request', record.id, 'other_logos', formData.other_logos);
// Upload files if they exist
if (formData.other_logos.length > 0) {
toast.loading('Uploading logo files...', { id: submittingToast });
await fileManager.uploadFiles('event_request', record.id, 'other_logos', formData.other_logos);
}
if (formData.room_booking) {
toast.loading('Uploading room booking confirmation...', { id: submittingToast });
await fileManager.uploadFile('event_request', record.id, 'room_booking', formData.room_booking);
}
// Upload multiple invoice files
if (formData.invoice_files && formData.invoice_files.length > 0) {
toast.loading('Uploading invoice files...', { id: submittingToast });
// Use appendFiles instead of uploadFiles to ensure we're adding files, not replacing them
await fileManager.appendFiles('event_request', record.id, 'invoice_files', formData.invoice_files);
// For backward compatibility, also upload the first file as the main invoice
if (formData.invoice || formData.invoice_files[0]) {
const mainInvoice = formData.invoice || formData.invoice_files[0];
await fileManager.uploadFile('event_request', record.id, 'invoice', mainInvoice);
}
} else if (formData.invoice) {
// If there are no invoice_files but there is a main invoice, upload it
toast.loading('Uploading invoice file...', { id: submittingToast });
await fileManager.uploadFile('event_request', record.id, 'invoice', formData.invoice);
}
// Clear form data from localStorage
localStorage.removeItem('eventRequestFormData');
// Show success message
toast.success('Event request submitted successfully!', { id: submittingToast });
// Reset form
setFormData({
name: '',
location: '',
start_date_time: '',
end_date_time: '',
event_description: '',
flyers_needed: false,
flyer_type: [],
other_flyer_type: '',
flyer_advertising_start_date: '',
flyer_additional_requests: '',
photography_needed: false,
required_logos: [],
other_logos: [],
advertising_format: '',
will_or_have_room_booking: false,
expected_attendance: 0,
room_booking: null,
as_funding_required: false,
food_drinks_being_served: false,
itemized_invoice: '',
invoice: null,
invoice_files: [], // Reset multiple invoice files
needs_graphics: null,
needs_as_funding: false,
invoiceData: {
items: [],
subtotal: 0,
taxRate: 7.75, // Default tax rate for San Diego
taxAmount: 0,
tipPercentage: 15, // Default tip percentage
tipAmount: 0,
total: 0,
vendor: ''
},
formReviewed: false // Reset review status
});
// Reset to first step
setCurrentStep(1);
} catch (uploadErr: any) {
console.error('Error during file upload:', uploadErr);
toast.error(`Error during file upload: ${uploadErr.message || 'Unknown error'}`, { id: submittingToast });
throw uploadErr;
} }
} catch (err: any) { if (formData.room_booking) {
console.error('Error submitting event request:', err); await fileManager.uploadFile('event_request', record.id, 'room_booking', formData.room_booking);
setError(err.message || 'An error occurred while submitting your request'); }
toast.error(err.message || 'An error occurred while submitting your request', { id: submittingToast });
// Upload multiple invoice files
if (formData.invoice_files && formData.invoice_files.length > 0) {
await fileManager.appendFiles('event_request', record.id, 'invoice_files', formData.invoice_files);
// For backward compatibility, also upload the first file as the main invoice
if (formData.invoice || formData.invoice_files[0]) {
const mainInvoice = formData.invoice || formData.invoice_files[0];
await fileManager.uploadFile('event_request', record.id, 'invoice', mainInvoice);
}
} else if (formData.invoice) {
await fileManager.uploadFile('event_request', record.id, 'invoice', formData.invoice);
}
// Clear form data from localStorage
localStorage.removeItem('eventRequestFormData');
// Keep success toast for form submission since it's a user action
toast.success('Event request submitted successfully!');
// Reset form
resetForm();
// Switch to the submissions tab
const submissionsTab = document.getElementById('submissions-tab');
if (submissionsTab) {
submissionsTab.click();
}
} catch (error) {
console.error('Error submitting event request:', error);
toast.error('Failed to submit event request. Please try again.');
setError('Failed to submit event request. Please try again.');
} finally { } finally {
setIsSubmitting(false); setIsSubmitting(false);
} }

View file

@ -1,10 +1,8 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { Get } from '../../../scripts/pocketbase/Get';
import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { DataSyncService } from '../../../scripts/database/DataSyncService'; import { DataSyncService } from '../../../scripts/database/DataSyncService';
import { Collections } from '../../../schemas/pocketbase/schema'; import { Collections } from '../../../schemas/pocketbase/schema';
import toast from 'react-hot-toast';
import type { EventRequest as SchemaEventRequest } from '../../../schemas/pocketbase'; import type { EventRequest as SchemaEventRequest } from '../../../schemas/pocketbase';
// Extended EventRequest interface with additional properties needed for this component // Extended EventRequest interface with additional properties needed for this component
@ -27,19 +25,16 @@ const UserEventRequests: React.FC<UserEventRequestsProps> = ({ eventRequests: in
// Refresh event requests // Refresh event requests
const refreshEventRequests = async () => { const refreshEventRequests = async () => {
setIsRefreshing(true); setIsRefreshing(true);
const refreshToast = toast.loading('Refreshing submissions...');
try { try {
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
if (!auth.isAuthenticated()) { if (!auth.isAuthenticated()) {
toast.error('You must be logged in to refresh submissions', { id: refreshToast });
return; return;
} }
const userId = auth.getUserId(); const userId = auth.getUserId();
if (!userId) { if (!userId) {
toast.error('User ID not found', { id: refreshToast });
return; return;
} }
@ -52,10 +47,8 @@ const UserEventRequests: React.FC<UserEventRequestsProps> = ({ eventRequests: in
); );
setEventRequests(updatedRequests); setEventRequests(updatedRequests);
toast.success('Submissions refreshed successfully', { id: refreshToast });
} catch (err) { } catch (err) {
console.error('Failed to refresh event requests:', err); console.error('Failed to refresh event requests:', err);
toast.error('Failed to refresh submissions. Please try again.', { id: refreshToast });
} finally { } finally {
setIsRefreshing(false); setIsRefreshing(false);
} }

View file

@ -48,8 +48,6 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Refresh event requests // Refresh event requests
const refreshEventRequests = async () => { const refreshEventRequests = async () => {
setIsRefreshing(true); setIsRefreshing(true);
const refreshToast = toast.loading('Refreshing event requests...');
try { try {
const auth = Authentication.getInstance(); const auth = Authentication.getInstance();
@ -71,22 +69,9 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
setEventRequests(updatedRequests); setEventRequests(updatedRequests);
applyFilters(updatedRequests); applyFilters(updatedRequests);
toast.success('Event requests refreshed successfully', { id: refreshToast }); } catch (error) {
} catch (err) { console.error('Error refreshing event requests:', error);
console.error('Failed to refresh event requests:', err); toast.error('Failed to refresh event requests');
// Check if it's an authentication error
if (err instanceof Error &&
(err.message.includes('authentication') ||
err.message.includes('auth') ||
err.message.includes('logged in'))) {
toast.error('Authentication error. Please log in again.', { id: refreshToast });
setTimeout(() => {
window.location.href = "/login";
}, 2000);
} else {
toast.error('Failed to refresh event requests. Please try again.', { id: refreshToast });
}
} finally { } finally {
setIsRefreshing(false); setIsRefreshing(false);
} }
@ -146,8 +131,6 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Update event request status // Update event request status
const updateEventRequestStatus = async (id: string, status: "submitted" | "pending" | "completed" | "declined") => { const updateEventRequestStatus = async (id: string, status: "submitted" | "pending" | "completed" | "declined") => {
const updateToast = toast.loading(`Updating status to ${status}...`);
try { try {
const update = Update.getInstance(); const update = Update.getInstance();
const result = await update.updateField('event_request', id, 'status', status); const result = await update.updateField('event_request', id, 'status', status);
@ -173,17 +156,15 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Force sync to update IndexedDB // Force sync to update IndexedDB
await dataSync.syncCollection<ExtendedEventRequest>(Collections.EVENT_REQUESTS); await dataSync.syncCollection<ExtendedEventRequest>(Collections.EVENT_REQUESTS);
toast.success(`Status updated to ${status}`, { id: updateToast }); // Remove success toast for updating status
} catch (err) { } catch (error) {
console.error('Failed to update event request status:', err); console.error('Error updating status:', error);
toast.error('Failed to update status. Please try again.', { id: updateToast }); toast.error('Failed to update status');
} }
}; };
// Add feedback to event request // Add feedback to event request
const addFeedback = async (id: string, feedback: string) => { const addFeedback = async (id: string, feedback: string) => {
const feedbackToast = toast.loading('Saving feedback...');
try { try {
const update = Update.getInstance(); const update = Update.getInstance();
const result = await update.updateField('event_request', id, 'feedback', feedback); const result = await update.updateField('event_request', id, 'feedback', feedback);
@ -204,11 +185,11 @@ const EventRequestManagementTable = ({ eventRequests: initialEventRequests }: Ev
// Force sync to update IndexedDB // Force sync to update IndexedDB
await dataSync.syncCollection<ExtendedEventRequest>(Collections.EVENT_REQUESTS); await dataSync.syncCollection<ExtendedEventRequest>(Collections.EVENT_REQUESTS);
toast.success('Feedback saved successfully', { id: feedbackToast }); // Remove success toast for saving feedback
return true; return true;
} catch (err) { } catch (error) {
console.error('Failed to save feedback:', err); console.error('Error saving feedback:', error);
toast.error('Failed to save feedback. Please try again.', { id: feedbackToast }); toast.error('Failed to save feedback');
return false; return false;
} }
}; };

View file

@ -180,8 +180,6 @@ export default function ReimbursementForm() {
throw new Error('User not authenticated'); throw new Error('User not authenticated');
} }
toast.loading('Adding receipt...');
// Create receipt record // Create receipt record
const formData = new FormData(); const formData = new FormData();
formData.append('field', receiptData.field); formData.append('field', receiptData.field);
@ -211,11 +209,9 @@ export default function ReimbursementForm() {
})); }));
setShowReceiptForm(false); setShowReceiptForm(false);
toast.dismiss();
toast.success('Receipt added successfully'); toast.success('Receipt added successfully');
} catch (error) { } catch (error) {
console.error('Error creating receipt:', error); console.error('Error creating receipt:', error);
toast.dismiss();
toast.error('Failed to add receipt'); toast.error('Failed to add receipt');
setError('Failed to add receipt. Please try again.'); setError('Failed to add receipt. Please try again.');
} }
@ -252,8 +248,6 @@ export default function ReimbursementForm() {
throw new Error('User not authenticated'); throw new Error('User not authenticated');
} }
const loadingToast = toast.loading('Submitting reimbursement request...');
// Create reimbursement record // Create reimbursement record
const formData = new FormData(); const formData = new FormData();
formData.append('title', request.title); formData.append('title', request.title);
@ -286,8 +280,6 @@ export default function ReimbursementForm() {
setReceipts([]); setReceipts([]);
setError(''); setError('');
// Dismiss loading toast and show success
toast.dismiss(loadingToast);
toast.success('🎉 Reimbursement request submitted successfully! Check "My Requests" to view it.', { toast.success('🎉 Reimbursement request submitted successfully! Check "My Requests" to view it.', {
duration: 5000, duration: 5000,
position: 'top-center', position: 'top-center',

View file

@ -4,7 +4,6 @@ import { Get } from '../../../scripts/pocketbase/Get';
import { Authentication } from '../../../scripts/pocketbase/Authentication'; import { Authentication } from '../../../scripts/pocketbase/Authentication';
import { FileManager } from '../../../scripts/pocketbase/FileManager'; import { FileManager } from '../../../scripts/pocketbase/FileManager';
import FilePreview from '../universal/FilePreview'; import FilePreview from '../universal/FilePreview';
import { toast } from 'react-hot-toast';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import type { ItemizedExpense, Reimbursement, Receipt } from '../../../schemas/pocketbase'; import type { ItemizedExpense, Reimbursement, Receipt } from '../../../schemas/pocketbase';
import { DataSyncService } from '../../../scripts/database/DataSyncService'; import { DataSyncService } from '../../../scripts/database/DataSyncService';
@ -133,8 +132,6 @@ export default function ReimbursementList() {
throw new Error('User not authenticated'); throw new Error('User not authenticated');
} }
const loadingToast = toast.loading('Loading reimbursements...');
// Use DataSyncService to get data from IndexedDB with forced sync // Use DataSyncService to get data from IndexedDB with forced sync
const dataSync = DataSyncService.getInstance(); const dataSync = DataSyncService.getInstance();
@ -181,7 +178,6 @@ export default function ReimbursementList() {
}); });
setRequests(processedRecords); setRequests(processedRecords);
toast.success('Reimbursements loaded successfully', { id: loadingToast });
// Fetch receipt details for each reimbursement // Fetch receipt details for each reimbursement
for (const record of processedRecords) { for (const record of processedRecords) {
@ -237,7 +233,6 @@ export default function ReimbursementList() {
} catch (err) { } catch (err) {
console.error('Error fetching reimbursements:', err); console.error('Error fetching reimbursements:', err);
setError('Failed to load reimbursements. Please try again.'); setError('Failed to load reimbursements. Please try again.');
toast.error('Failed to load reimbursements');
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -245,7 +240,6 @@ export default function ReimbursementList() {
const handlePreviewFile = async (request: ReimbursementRequest, receiptId: string) => { const handlePreviewFile = async (request: ReimbursementRequest, receiptId: string) => {
try { try {
const loadingToast = toast.loading('Loading receipt...');
const pb = auth.getPocketBase(); const pb = auth.getPocketBase();
// Check if we already have the receipt details in our map // Check if we already have the receipt details in our map
@ -258,8 +252,6 @@ export default function ReimbursementList() {
setPreviewUrl(url); setPreviewUrl(url);
setPreviewFilename(receiptDetailsMap[receiptId].field); setPreviewFilename(receiptDetailsMap[receiptId].field);
setShowPreview(true); setShowPreview(true);
toast.dismiss(loadingToast);
toast.success('Receipt loaded successfully');
return; return;
} }
@ -302,14 +294,11 @@ export default function ReimbursementList() {
setPreviewUrl(url); setPreviewUrl(url);
setPreviewFilename(receiptRecord.field); setPreviewFilename(receiptRecord.field);
setShowPreview(true); setShowPreview(true);
toast.dismiss(loadingToast);
toast.success('Receipt loaded successfully');
} else { } else {
throw new Error('Receipt not found'); throw new Error('Receipt not found');
} }
} catch (error) { } catch (error) {
console.error('Error loading receipt:', error); console.error('Error loading receipt:', error);
toast.error('Failed to load receipt');
} }
}; };

View file

@ -1,11 +1,10 @@
import React from 'react';
import { Toaster } from 'react-hot-toast'; import { Toaster } from 'react-hot-toast';
// Centralized toast provider to ensure consistent rendering // Centralized toast provider to ensure consistent rendering
export default function ToastProvider() { export default function ToastProvider() {
return ( return (
<Toaster <Toaster
position="bottom-right" position="top-center"
toastOptions={{ toastOptions={{
duration: 4000, duration: 4000,
style: { style: {