diff --git a/apps/frontend/src/components/floatingAlert.tsx b/apps/frontend/src/components/floatingAlert.tsx
new file mode 100644
index 000000000..0257b0b36
--- /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 e33792c73..389fd2537 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;
@@ -24,6 +25,8 @@ const DonationDetailsModal: React.FC = ({
}) => {
const [items, setItems] = useState([]);
+ const [alertMessage, setAlertMessage] = useState('');
+
const donationId = donation.donationId;
useEffect(() => {
@@ -37,7 +40,7 @@ const DonationDetailsModal: React.FC = ({
setItems(itemsData);
} catch (err) {
- alert('Error fetching donation details: ' + err);
+ setAlertMessage('Error fetching donation details: ' + err);
}
};
@@ -60,6 +63,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 309e15998..ce5e22f11 100644
--- a/apps/frontend/src/components/forms/orderDetailsModal.tsx
+++ b/apps/frontend/src/components/forms/orderDetailsModal.tsx
@@ -3,6 +3,7 @@ import { Text, Dialog, CloseButton, Textarea, Field } from '@chakra-ui/react';
import ApiClient from '@api/apiClient';
import { FoodRequest, OrderSummary } from 'types/types';
import { formatDate } from '@utils/utils';
+import { FloatingAlert } from '@components/floatingAlert';
import { TagGroup } from './tagGroup';
interface OrderDetailsModalProps {
@@ -18,6 +19,8 @@ const OrderDetailsModal: React.FC = ({
}) => {
const [foodRequest, setFoodRequest] = useState(null);
+ const [alertMessage, setAlertMessage] = useState('');
+
useEffect(() => {
if (isOpen) {
const fetchData = async () => {
@@ -27,7 +30,7 @@ const OrderDetailsModal: React.FC = ({
);
setFoodRequest(foodRequestData);
} catch (error) {
- alert('Error fetching food request details:' + error);
+ setAlertMessage('Error fetching food request details:' + error);
}
};
@@ -44,6 +47,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 04d8cfb67..4d27bfdea 100644
--- a/apps/frontend/src/components/forms/requestFormModal.tsx
+++ b/apps/frontend/src/components/forms/requestFormModal.tsx
@@ -17,6 +17,7 @@ import {
RequestSize,
} from '../../types/types';
import { ChevronDownIcon } from 'lucide-react';
+import { FloatingAlert } from '@components/floatingAlert';
import apiClient from '@api/apiClient';
import { TagGroup } from './tagGroup';
@@ -38,8 +39,13 @@ const FoodRequestFormModal: React.FC = ({
const [selectedItems, setSelectedItems] = useState([]);
const [requestedSize, setRequestedSize] = useState('');
const [additionalNotes, setAdditionalNotes] = useState('');
-
- const [alertMessage, setAlertMessage] = useState('');
+ const [alert, setAlert] = useState<{
+ isError: boolean;
+ message: string;
+ }>({
+ isError: true,
+ message: '',
+ });
const isFormValid = requestedSize !== '' && selectedItems.length > 0;
@@ -67,10 +73,11 @@ const FoodRequestFormModal: React.FC = ({
try {
await apiClient.createFoodRequest(foodRequestData);
+ setAlert({ isError: false, message: 'Request Submitted' });
onClose();
onSuccess();
} catch {
- setAlertMessage('Failed to submit food request');
+ setAlert({ isError: true, message: 'Request could not be submitted.' });
}
};
@@ -83,10 +90,11 @@ const FoodRequestFormModal: React.FC = ({
}}
closeOnInteractOutside
>
- {alertMessage && (
- // TODO: add Justin's alert component/uncomment below out and remove text component
- //
- {alertMessage}
+ {alert.message && alert.isError && (
+
+ )}
+ {alert.message && !alert.isError && (
+
)}
@@ -263,7 +271,10 @@ const FoodRequestFormModal: React.FC = ({
if (words.length <= 250) {
setAdditionalNotes(e.target.value);
} else {
- alert('Exceeded word limit');
+ setAlert({
+ isError: true,
+ message: 'Exceeded word limit',
+ });
}
}}
/>
diff --git a/apps/frontend/src/components/forms/resetPasswordModal.tsx b/apps/frontend/src/components/forms/resetPasswordModal.tsx
index fa11904af..8649a9174 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 c444803bb..9c9a761f3 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);
}}
/>