diff --git a/src/components/AdminDashboard/BusinessTable/BusinessTable.tsx b/src/components/AdminDashboard/BusinessTable/BusinessTable.tsx index 0ffa0d3..ba1d95f 100644 --- a/src/components/AdminDashboard/BusinessTable/BusinessTable.tsx +++ b/src/components/AdminDashboard/BusinessTable/BusinessTable.tsx @@ -29,7 +29,7 @@ import { BiCheck, BiEnvelope, BiPlus, BiTimeFive, BiX } from 'react-icons/bi'; import { useNavigate } from 'react-router-dom'; import { useBackend } from '../../../contexts/BackendContext'; -import DownloadCSV from '../../../utils/downloadCSV'; +import downloadCSV from '../../../utils/downloadCSV'; import BusinessTableModal from './BusinessTableModal'; const TABLE_HEADERS = ['Business Name', 'Location', 'Email', 'Form Status', 'Last Submitted']; @@ -149,8 +149,7 @@ export const BusinessTable = () => { }; const handleSendReminders = async () => { - for (const businessId of selectedBusinessIds) { - // ! FIX ME + selectedBusinessIds.forEach(async (businessId) => { try { const requestData = { businessId: businessId, @@ -164,7 +163,8 @@ export const BusinessTable = () => { } catch (error) { console.error('Error sending reminders:', error); } - } + }); + const message = `To ${selectedBusinessIds.size} ${ selectedBusinessIds.size > 1 ? `businesses` : ` business` }.`; @@ -184,7 +184,7 @@ export const BusinessTable = () => { headers.push(TABLE_HEADERS[i].toLowerCase().replace(' ', '_')); } try { - DownloadCSV(ids); + downloadCSV(ids); const message = `For ${ids.length} ${ids.length > 1 ? `businesses` : ` business`}.`; toast({ title: 'Downloaded CSV', @@ -246,10 +246,7 @@ export const BusinessTable = () => { console.log(businessResponse); - if ( - true || - (currentTab === 'All' && search === '' && businessCountResponse.data[0]['count'] === 0) - ) { + if (currentTab === 'All' && search === '' && businessCountResponse.data[0]['count'] === 0) { onOpen(); } diff --git a/src/components/BusinessTablePending/BusinessTablePending.jsx b/src/components/BusinessTablePending/BusinessTablePending.jsx index df08898..967023d 100644 --- a/src/components/BusinessTablePending/BusinessTablePending.jsx +++ b/src/components/BusinessTablePending/BusinessTablePending.jsx @@ -18,7 +18,7 @@ import { FaPlus } from 'react-icons/fa6'; import { useNavigate } from 'react-router-dom'; import { useBackend } from '../../contexts/BackendContext'; -import DownloadCSV from '../../utils/downloadCSV'; +import downloadCSV from '../../utils/downloadCSV'; import { BusinessForm } from '../BusinessForm/BusinessForm.1'; import ViewBusiness from '../ViewBusiness/ViewBusiness'; @@ -120,7 +120,7 @@ const BusinessTablePending = (businessData) => { for (var i = 0; i < TABLE_HEADERS.length; i++) { headers.push(TABLE_HEADERS[i].toLowerCase().replace(' ', '_')); } - DownloadCSV(headers, ids, 'business'); + downloadCSV(headers, ids, 'business'); }; const handleSendReminders = async () => { diff --git a/src/components/DonationTrackingTable/DonationSite.jsx b/src/components/DonationTrackingTable/DonationSite.jsx deleted file mode 100644 index 70883d5..0000000 --- a/src/components/DonationTrackingTable/DonationSite.jsx +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint react/prop-types: 0 */ -import { useEffect, useState } from 'react'; -import { Checkbox, Td, Tr } from '@chakra-ui/react'; -import { useNavigate } from 'react-router-dom'; - -import { useBackend } from '../../contexts/BackendContext'; - -const DonationSite = ({ donation_site, checkSet, setCheck, topCheckBox }) => { - const [individualCheckBox, setIndividualCheckBox] = useState(topCheckBox); - const [donationSiteName, setDonationSiteName] = useState(''); - const navigate = useNavigate(); - const { backend } = useBackend(); - - useEffect(() => { - setIndividualCheckBox(topCheckBox); - }, [topCheckBox]); - - useEffect(() => { - const getDonationSiteName = async () => { - try { - const businessResponse = await backend.get(`business/${donation_site.business_id}`); - setDonationSiteName(businessResponse.data[0].name); - } catch (error) { - console.log(error); - } - }; - - getDonationSiteName(); - }, []); - - const headers = [ - donationSiteName, - donation_site?.donation_id, - donation_site?.food_bank_donation, - donation_site?.reporter, - donation_site?.email, - donation_site?.date ? new Date(donation_site.date).toLocaleDateString() : null, - donation_site?.canned_dog_food_quantity !== null - ? `${donation_site.canned_dog_food_quantity} lb` - : '0 lb', - donation_site?.dry_dog_food_quantity !== null - ? `${donation_site.dry_dog_food_quantity} lb` - : '0 lb', - donation_site?.canned_cat_food_quantity !== null - ? `${donation_site.canned_cat_food_quantity} lb` - : '0 lb', - donation_site?.dry_cat_food_quantity !== null - ? `${donation_site.dry_cat_food_quantity} lb` - : '0 lb', - donation_site?.misc_items, - donation_site?.volunteer_hours !== null ? `${donation_site.volunteer_hours} hrs` : '0 hrs', - ]; - - const handleClick = () => { - const newCheckedState = !individualCheckBox; - setIndividualCheckBox(newCheckedState); - if (checkSet.has(donation_site.donation_id)) { - setCheck((prevState) => { - prevState.delete(donation_site.donation_id); - return prevState; - }); - } else { - setCheck((prevState) => { - prevState.add(donation_site.donation_id); - return prevState; - }); - } - }; - - const handleRowClick = async (id) => { - navigate(`/ViewDonation/${id}`); - }; - - return ( - - - {headers.map((header, index) => ( - handleRowClick(donation_site.donation_id)}> - {header} - - ))} - - ); -}; - -export default DonationSite; diff --git a/src/components/DonationTrackingTable/DonationSite.tsx b/src/components/DonationTrackingTable/DonationSite.tsx new file mode 100644 index 0000000..a0348f1 --- /dev/null +++ b/src/components/DonationTrackingTable/DonationSite.tsx @@ -0,0 +1,92 @@ +import { useEffect, useState } from 'react'; +import { Checkbox, Td, Tr } from '@chakra-ui/react'; +import { useNavigate } from 'react-router-dom'; + +import { useBackend } from '../../contexts/BackendContext'; +import { Donation } from '../../types/donation'; + +interface DonationSiteProps { + donation: Donation; + checkedSet: Set; + allChecked: boolean; + setCheckedSet: (set: Set) => unknown; +} + +export const DonationSite = ({ + donation, + checkedSet, + allChecked, + setCheckedSet, +}: DonationSiteProps) => { + const navigate = useNavigate(); + const { backend } = useBackend(); + + const [isChecked, setIsChecked] = useState(allChecked || checkedSet.has(donation.business_id)); + const [donationSiteName, setDonationSiteName] = useState(''); + + useEffect(() => { + setIsChecked(allChecked); + }, [allChecked]); + + useEffect(() => { + const getDonationSiteName = async () => { + try { + const businessResponse = await backend.get(`business/${donation.business_id}`); + setDonationSiteName(businessResponse.data[0].name); + } catch (error) { + console.log(error); + } + }; + + getDonationSiteName(); + }, []); + + const cells = [ + donationSiteName, + donation?.donation_id, + donation?.food_bank_donation, + donation?.reporter, + donation?.email, + donation?.date ? new Date(donation.date).toLocaleDateString() : null, + donation?.canned_dog_food_quantity !== null + ? `${donation.canned_dog_food_quantity} lb` + : '0 lb', + donation?.dry_dog_food_quantity !== null ? `${donation.dry_dog_food_quantity} lb` : '0 lb', + donation?.canned_cat_food_quantity !== null + ? `${donation.canned_cat_food_quantity} lb` + : '0 lb', + donation?.dry_cat_food_quantity !== null ? `${donation.dry_cat_food_quantity} lb` : '0 lb', + donation?.misc_items, + donation?.volunteer_hours !== null ? `${donation.volunteer_hours} hrs` : '0 hrs', + ]; + + const handleClick = () => { + const newCheckedState = !isChecked; + setIsChecked(newCheckedState); + + const newSet = new Set(checkedSet); + checkedSet.has(donation.donation_id) + ? newSet.delete(donation.donation_id) + : newSet.add(donation.donation_id); + + setCheckedSet(newSet); + }; + + const handleRowClick = async (id: number) => { + navigate(`/ViewDonation/${id}`); + }; + + return ( + + + + + + {cells.map((cell, index) => ( + handleRowClick(donation.donation_id)}> + {cell} + + ))} + + ); +}; diff --git a/src/components/DonationTrackingTable/DonationTrackingTable.module.css b/src/components/DonationTrackingTable/DonationTrackingTable.module.css deleted file mode 100644 index 9839770..0000000 --- a/src/components/DonationTrackingTable/DonationTrackingTable.module.css +++ /dev/null @@ -1,22 +0,0 @@ -.roundedTable { - padding: var(--chakra-space-3); - border-width: 1px; - border-radius: 12px; - overflow-x: auto; - background: var(--white, #fff); -} - -.resultNavigation { - display: flex; - flex-direction: row; - justify-content: right; - align-items: center; -} - -.checkBoxHeader { - display: flex; - flex-direction: row; - align-items: center; - padding: 0 10px; - margin-top: 15px; -} diff --git a/src/components/DonationTrackingTable/DonationTrackingTable.tsx b/src/components/DonationTrackingTable/DonationTrackingTable.tsx index 10ba9fb..1081c2b 100644 --- a/src/components/DonationTrackingTable/DonationTrackingTable.tsx +++ b/src/components/DonationTrackingTable/DonationTrackingTable.tsx @@ -1,10 +1,5 @@ -import { useEffect, useState } from 'react'; -import { - ArrowDownIcon, - ChevronDownIcon, - ChevronLeftIcon, - ChevronRightIcon, -} from '@chakra-ui/icons'; +import { ChangeEvent, useEffect, useState } from 'react'; +import { ArrowDownIcon, ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons'; import { Box, Button, @@ -12,6 +7,7 @@ import { Flex, Heading, HStack, + Icon, IconButton, Input, Tab, @@ -26,82 +22,61 @@ import { Tr, useToast, } from '@chakra-ui/react'; - -import { useBackend } from '../../contexts/BackendContext'; -import DonationSite from './DonationSite'; - -import './DonationTrackingTable.module.css'; - import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; +import { useBackend } from '../../contexts/BackendContext'; import { pageStyle, pageTitleStyle } from '../../styles/sharedStyles'; -import DownloadCSV from '../../utils/downloadCSV'; +import { Donation } from '../../types/donation'; +import downloadCSV from '../../utils/downloadCSV'; import NotificationsDrawer from '../NotificationsDrawer/NotificationsDrawer'; -import classes from './DonationTrackingTable.module.css'; +import { DonationSite } from './DonationSite'; + +const TABLE_HEADERS = [ + 'Donation Site', + 'Donation ID', + 'Food Bank', + 'Person Reporting', + 'Email', + 'Date', + 'Canned Dog Food', + 'Dry Dog Food', + 'Canned Cat Food', + 'Dry Cat Food', + 'Misc Items', + 'Volunteer Hours', +]; + +const TAB_HEADERS = ['month', 'quarter', 'year', 'all']; export const DonationTrackingTable = () => { - const navigate = useNavigate(); const { backend } = useBackend(); const { isAdmin } = useAuth(); const toast = useToast(); + const navigate = useNavigate(); - const [donationTrackingTableData, setDonationTrackingTableData] = useState([]); - const [checkedSet, setCheckedSet] = useState(new Set()); - const [topCheckBox, setTopCheckBox] = useState(false); - const headers = [ - 'Donation Site', - 'Donation ID', - 'Food Bank', - 'Person Reporting', - 'Email', - 'Date', - 'Canned Dog Food', - 'Dry Dog Food', - 'Canned Cat Food', - 'Dry Cat Food', - 'Misc Items', - 'Volunteer Hours', - ]; + const [donationTrackingTableData, setDonationTrackingTableData] = useState([]); + const [checkedSet, setCheckedSet] = useState>(new Set()); + const [allChecked, setAllChecked] = useState(false); - const tabHeaders = ['month', 'quarter', 'year', 'all']; - const [currentTab, setCurrentTab] = useState('month'); //change to all once all page is implemented + const [currentTab, setCurrentTab] = useState('all'); const [currentPageNum, setCurrentPageNum] = useState(1); const [currentDonationsNum, setCurrentDonationsNum] = useState(0); const [pageLimit, setPageLimit] = useState(1); const [searchTerm, setSearchTerm] = useState(''); - const [isAdminUser, setIsAdminUser] = useState(false); const [notifications, setNotifications] = useState([]); - const loadInfo = async () => { - changePage(); - const donationNumResponse = await backend.get( - `/donation/totalDonations/${currentTab}/?searchTerm=${searchTerm}`, - ); - setCurrentDonationsNum(donationNumResponse.data[0]['count']); - setPageLimit(Math.ceil(donationNumResponse.data[0]['count'] / 10)); - }; - - const changeTab = async (tab) => { + const changeTab = async (tab: string) => { setCurrentTab(tab); setCurrentPageNum(1); setCheckedSet(new Set()); - setTopCheckBox(false); - }; - - const changePage = async () => { - const donationResponse = await backend.get( - `/donation/filter/${currentTab}/?pageNum=${currentPageNum}&searchTerm=${searchTerm}`, - ); - setDonationTrackingTableData(donationResponse.data); + setAllChecked(false); }; useEffect(() => { - const getData = async () => { - try { - loadInfo(); - } catch (error) { - console.error('Error fetching data:', error); + const checkIsAdmin = async () => { + if (!(await isAdmin())) { + navigate('/BusinessDashboard'); } }; @@ -114,70 +89,77 @@ export const DonationTrackingTable = () => { } }; - const checkIsAdmin = async () => { - if (!(await isAdmin())) { - navigate('/BusinessDashboard'); - } else { - setIsAdminUser(true); + checkIsAdmin(); + + try { + getNotifications(); + } catch (error) { + console.error('Error fetching data:', error); + } + }, []); + + useEffect(() => { + const getData = async () => { + try { + const donationResponse = await backend.get( + `/donation/filter/${currentTab}/?pageNum=${currentPageNum}&searchTerm=${searchTerm}`, + ); + setDonationTrackingTableData(donationResponse.data); + + const donationNumResponse = await backend.get( + `/donation/totalDonations/${currentTab}/?searchTerm=${searchTerm}`, + ); + setCurrentDonationsNum(donationNumResponse.data[0]['count']); + setPageLimit(Math.ceil(donationNumResponse.data[0]['count'] / 10)); + } catch (error) { + console.error('Error fetching data:', error); } }; - checkIsAdmin(); getData(); - if (notifications.length == 0) { - getNotifications(); - } }, [currentTab, currentPageNum, searchTerm]); - const renderDonationTrackingTableData = () => { - return donationTrackingTableData.map((element, index) => { - return ( - - ); - }); - }; - const handleCheckBoxes = () => { - const newCheckboxValue = !topCheckBox; - setTopCheckBox(newCheckboxValue); + const newCheckboxValue = !allChecked; + setAllChecked(newCheckboxValue); if (newCheckboxValue) { const newCheckedSet = new Set( donationTrackingTableData.map((element) => element.donation_id), ); + setCheckedSet(newCheckedSet); } else { - const newCheckedSet = new Set(); + const newCheckedSet = new Set(); setCheckedSet(newCheckedSet); } }; - const handleDownloadCSV = () => { + const handleDownloadCSV = async () => { const ids = Array.from(checkedSet); + try { if (ids.length === 0) { - toast({ - title: 'Downloaded CSV', + return toast({ + title: 'No Businesses Selected', description: 'Select a business first', status: 'error', duration: 3000, isClosable: true, }); - } else { - DownloadCSV(ids, true); - const message = `For ${ids.length} ${ids.length > 1 ? `businesses` : ` business`}.`; + } + + const ok = await downloadCSV(ids, true, backend); + + if (ok) { toast({ title: 'Downloaded CSV', - description: message, + description: `For ${ids.length} ${ids.length > 1 ? `businesses` : ` business`}.`, status: 'success', duration: 3000, isClosable: true, }); + } else { + throw Error; } } catch (error) { toast({ @@ -190,82 +172,88 @@ export const DonationTrackingTable = () => { } }; - const handleSearch = (event) => { - setSearchTerm(event.target.value.split(' ').join('+')); + const handleSearch = (event: ChangeEvent) => { + setSearchTerm(event.currentTarget.value.split(' ').join('+')); setCurrentPageNum(1); }; return ( - <> - {isAdminUser && ( - - - Donation Tracking - - + + + Donation Tracking + + - - - {tabHeaders.map((header) => ( - changeTab(header)} key={header} sx={{ whiteSpace: 'nowrap' }}> - This  - {header} - - ))} + + + {TAB_HEADERS.map((header) => ( + changeTab(header)} key={header} sx={{ whiteSpace: 'nowrap' }}> + {header} + + ))} + + - changeTab('all')}>All - - + + - - - - + + - - - - -
- - -
- {headers.map((header, index) => { - return ; - })} - - - {renderDonationTrackingTableData()} -
{header}
-
+ + + + + + {TABLE_HEADERS.map((header, index) => { + return ; + })} + + + + {donationTrackingTableData.map((donation) => ( + + ))} + +
+ + {header}
+
-
- - {currentDonationsNum > 0 ? (currentPageNum - 1) * 10 + 1 : 0} to{' '} - {Math.min(currentPageNum * 10, currentDonationsNum)} of {currentDonationsNum} - - } - onClick={() => setCurrentPageNum(currentPageNum - 1)} - /> - = pageLimit} - icon={} - onClick={() => setCurrentPageNum(currentPageNum + 1)} - /> -
-
- )} - + + + {currentDonationsNum > 0 ? (currentPageNum - 1) * 10 + 1 : 0} to{' '} + {Math.min(currentPageNum * 10, currentDonationsNum)} of {currentDonationsNum} + + + } + onClick={() => setCurrentPageNum(currentPageNum - 1)} + /> + = pageLimit} + icon={} + onClick={() => setCurrentPageNum(currentPageNum + 1)} + /> + +
); }; diff --git a/src/components/NotificationsDrawer/NotificationsDrawer.jsx b/src/components/NotificationsDrawer/NotificationsDrawer.jsx index 7a4c063..ad6e078 100644 --- a/src/components/NotificationsDrawer/NotificationsDrawer.jsx +++ b/src/components/NotificationsDrawer/NotificationsDrawer.jsx @@ -26,7 +26,7 @@ import { import PropTypes from 'prop-types'; import { useNavigate } from 'react-router-dom'; -import DownloadCSV from '../../utils/downloadCSV'; +import downloadCSV from '../../utils/downloadCSV'; import 'boxicons'; @@ -60,7 +60,7 @@ const NotificationsDrawer = ({ notificationsData }) => { }; const handleDownloadCSV = (ids) => { - DownloadCSV(ids); + downloadCSV(ids); }; const handleSendReminder = async (businessId, businessName) => { diff --git a/src/components/ViewDonation/ViewDonation.jsx b/src/components/ViewDonation/ViewDonation.jsx index 4a0f555..67c66fe 100644 --- a/src/components/ViewDonation/ViewDonation.jsx +++ b/src/components/ViewDonation/ViewDonation.jsx @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import { useNavigate, useParams } from 'react-router-dom'; import { useBackend } from '../../contexts/BackendContext'; -import DownloadCSV from '../../utils/downloadCSV'; +import downloadCSV from '../../utils/downloadCSV'; import NotificationsDrawer from '../NotificationsDrawer/NotificationsDrawer'; import classes from './ViewDonation.module.css'; @@ -34,7 +34,7 @@ const ViewDonation = () => { const handleDownloadCSV = () => { try { - DownloadCSV([id], true); + downloadCSV([id], true); toast({ title: 'Downloaded CSV', description: `for ${businessName}`, diff --git a/src/utils/downloadCSV.js b/src/utils/downloadCSV.js index 42525e3..bf7b087 100644 --- a/src/utils/downloadCSV.js +++ b/src/utils/downloadCSV.js @@ -1,6 +1,6 @@ -// DownloadCSV.js +// downloadCSV.js -async function DownloadCSV(ids, isDonation = false) { +async function downloadCSV(ids, isDonation = false, backend) { const headers = { 'Business Name': 'name', 'Business Phone': 'primary_phone', @@ -15,26 +15,21 @@ async function DownloadCSV(ids, isDonation = false) { 'Miscellaneous Items': 'miscellaneous_items', 'Volunteer Hours': 'volunteer_hours', }; + try { let response; if (isDonation) { - response = await fetch( - `http://localhost:3001/donation/selectByIdsDonation?ids=${ids.join(',')}`, - { - method: 'GET', - }, - ); + response = await backend.get(`/donation/donation_tracking/selectByIds?ids=${ids.join(',')}`); + console.log('RESPONSE:', response); } else { - response = await fetch(`http://localhost:3001/donation/selectByIds?ids=${ids.join(',')}`, { - method: 'GET', - }); + response = await backend.get(`/donation/selectByIds?ids=${ids.join(',')}`); } - if (!response.ok) { + if (!response.statusText === 'OK') { throw new Error('Failed to fetch data from the server'); } - const data = await response.json(); + const data = await response.data; // Create CSV string const csvRows = []; @@ -62,9 +57,11 @@ async function DownloadCSV(ids, isDonation = false) { document.body.appendChild(link); // Required for Firefox link.click(); document.body.removeChild(link); // Clean up + + return true; // TODO: fix me )= (error handling etc) } catch (error) { - console.error('Error downloading CSV:', error); + console.error(error); } } -export default DownloadCSV; +export default downloadCSV;