diff --git a/apps/frontend/src/components/floatingAlert.tsx b/apps/frontend/src/components/floatingAlert.tsx new file mode 100644 index 00000000..0257b0b3 --- /dev/null +++ b/apps/frontend/src/components/floatingAlert.tsx @@ -0,0 +1,57 @@ +import { Alert } from '@chakra-ui/react'; +import { useEffect, useState } from 'react'; + +type FloatingAlertProps = { + message?: string | null; + status?: 'info' | 'error'; + timeout?: number; +}; + +export function FloatingAlert({ + message, + status, + timeout, +}: FloatingAlertProps) { + const [visible, setVisible] = useState(!!message); + + useEffect(() => { + if (!message) { + setVisible(false); + return; + } + + setVisible(true); + + if (!timeout) return; + + const timer = setTimeout(() => { + setVisible(false); + }, timeout); + + return () => clearTimeout(timer); + }, [message, timeout]); + + if (!message || !visible) return null; + + return ( + + + + {message} + + + ); +} diff --git a/apps/frontend/src/components/forms/donationDetailsModal.tsx b/apps/frontend/src/components/forms/donationDetailsModal.tsx index a3aa1731..dfee2bb2 100644 --- a/apps/frontend/src/components/forms/donationDetailsModal.tsx +++ b/apps/frontend/src/components/forms/donationDetailsModal.tsx @@ -10,6 +10,7 @@ import { import ApiClient from '@api/apiClient'; import { Donation, DonationItem, FoodType } from 'types/types'; import { formatDate } from '@utils/utils'; +import { FloatingAlert } from '@components/floatingAlert'; interface DonationDetailsModalProps { donation: Donation; @@ -25,6 +26,8 @@ const DonationDetailsModal: React.FC = ({ const [loadedDonation, setLoadedDonation] = useState(); const [items, setItems] = useState([]); + const [alertMessage, setAlertMessage] = useState(''); + const donationId = donation.donationId; useEffect(() => { @@ -40,7 +43,7 @@ const DonationDetailsModal: React.FC = ({ setLoadedDonation(donationData); setItems(itemsData); } catch (err) { - alert('Error fetching donation details: ' + err); + setAlertMessage('Error fetching donation details: ' + err); } }; @@ -63,6 +66,9 @@ const DonationDetailsModal: React.FC = ({ closeOnInteractOutside scrollBehavior="inside" > + {alertMessage && ( + + )} diff --git a/apps/frontend/src/components/forms/orderDetailsModal.tsx b/apps/frontend/src/components/forms/orderDetailsModal.tsx index 917bc484..6635b9e7 100644 --- a/apps/frontend/src/components/forms/orderDetailsModal.tsx +++ b/apps/frontend/src/components/forms/orderDetailsModal.tsx @@ -11,6 +11,7 @@ import { import ApiClient from '@api/apiClient'; import { FoodRequest, OrderSummary } from 'types/types'; import { formatDate } from '@utils/utils'; +import { FloatingAlert } from '@components/floatingAlert'; interface OrderDetailsModalProps { order: OrderSummary; @@ -25,6 +26,8 @@ const OrderDetailsModal: React.FC = ({ }) => { const [foodRequest, setFoodRequest] = useState(null); + const [alertMessage, setAlertMessage] = useState(''); + useEffect(() => { if (isOpen) { const fetchData = async () => { @@ -34,7 +37,7 @@ const OrderDetailsModal: React.FC = ({ ); setFoodRequest(foodRequestData); } catch (error) { - alert('Error fetching food request details:' + error); + setAlertMessage('Error fetching food request details:' + error); } }; @@ -51,6 +54,9 @@ const OrderDetailsModal: React.FC = ({ }} closeOnInteractOutside > + {alertMessage && ( + + )} diff --git a/apps/frontend/src/components/forms/requestFormModal.tsx b/apps/frontend/src/components/forms/requestFormModal.tsx index db321db3..fe313798 100644 --- a/apps/frontend/src/components/forms/requestFormModal.tsx +++ b/apps/frontend/src/components/forms/requestFormModal.tsx @@ -23,6 +23,7 @@ import { RequestSize, } from '../../types/types'; import { ChevronDownIcon } from 'lucide-react'; +import { FloatingAlert } from '@components/floatingAlert'; import apiClient from '@api/apiClient'; interface FoodRequestFormModalProps { @@ -89,9 +90,7 @@ const FoodRequestFormModal: React.FC = ({ closeOnInteractOutside > {alertMessage && ( - // TODO: add Justin's alert component/uncomment below out and remove text component - // - {alertMessage} + )} @@ -293,7 +292,7 @@ const FoodRequestFormModal: React.FC = ({ if (words.length <= 250) { setAdditionalNotes(e.target.value); } else { - alert('Exceeded word limit'); + setAlertMessage('Exceeded word limit'); } }} /> diff --git a/apps/frontend/src/components/forms/resetPasswordModal.tsx b/apps/frontend/src/components/forms/resetPasswordModal.tsx index fa11904a..8649a917 100644 --- a/apps/frontend/src/components/forms/resetPasswordModal.tsx +++ b/apps/frontend/src/components/forms/resetPasswordModal.tsx @@ -10,6 +10,7 @@ import { Field, } from '@chakra-ui/react'; import { resetPassword, confirmResetPassword } from 'aws-amplify/auth'; +import { FloatingAlert } from '@components/floatingAlert'; const ResetPasswordModal: React.FC = () => { const [email, setEmail] = useState(''); @@ -17,6 +18,7 @@ const ResetPasswordModal: React.FC = () => { const [step, setStep] = useState<'reset' | 'new'>('reset'); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); + const [alertMessage, setAlertMessage] = useState(''); const navigate = useNavigate(); @@ -25,27 +27,26 @@ const ResetPasswordModal: React.FC = () => { await resetPassword({ username: email }); setStep('new'); } catch (error) { - alert(error || 'Failed to send verification code'); + setAlertMessage('Failed to send verification code: ' + error); } }; const handleResendCode = async () => { try { await resetPassword({ username: email }); - alert('Successfully sent verification code'); } catch (error) { - alert(error || 'Failed to send verification code'); + setAlertMessage('Failed to send verification code: ' + error); } }; const handleResetPassword = async () => { if (password !== confirmPassword) { - alert('Passwords need to match'); + setAlertMessage('Passwords need to match'); return; } if (password.length < 8) { - alert('Password needs to be at least 8 characters'); + setAlertMessage('Password needs to be at least 8 characters'); return; } @@ -55,10 +56,9 @@ const ResetPasswordModal: React.FC = () => { confirmationCode: code, newPassword: password, }); - alert('Password reset successful!'); navigate('/login'); } catch (error) { - alert(error || 'Failed to set new password'); + setAlertMessage('Failed to set new password: ' + error); } }; @@ -92,6 +92,9 @@ const ResetPasswordModal: React.FC = () => { borderRadius="xl" boxShadow="xl" > + {alertMessage && ( + + )} diff --git a/apps/frontend/src/containers/adminDonation.tsx b/apps/frontend/src/containers/adminDonation.tsx index c444803b..9c9a761f 100644 --- a/apps/frontend/src/containers/adminDonation.tsx +++ b/apps/frontend/src/containers/adminDonation.tsx @@ -15,6 +15,7 @@ import { Donation } from 'types/types'; import DonationDetailsModal from '@components/forms/donationDetailsModal'; import ApiClient from '@api/apiClient'; import { formatDate } from '@utils/utils'; +import { FloatingAlert } from '@components/floatingAlert'; const AdminDonation: React.FC = () => { const [donations, setDonations] = useState([]); @@ -28,13 +29,15 @@ const AdminDonation: React.FC = () => { null, ); + const [alertMessage, setAlertMessage] = useState(''); + useEffect(() => { const fetchDonations = async () => { try { const data = await ApiClient.getAllDonations(); setDonations(data); } catch (error) { - alert('Error fetching donations: ' + error); + setAlertMessage('Error fetching donations: ' + error); } }; fetchDonations(); @@ -99,6 +102,9 @@ const AdminDonation: React.FC = () => { Donation Management + {alertMessage && ( + + )}