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 && (
+
+ )}
{alertMessage && (
-
-
-
- {alertMessage}
-
-
+
)}
+
{
onSubmitSuccess={() => {
setAlertMessage('Volunteer added.');
setSubmitSuccess(true);
- setTimeout(() => setAlertMessage(''), 3000);
}}
onSubmitFail={() => {
setAlertMessage('Volunteer could not be added.');
setSubmitSuccess(false);
- setTimeout(() => setAlertMessage(''), 3000);
}}
/>