From f63760d8a49f46805e56ac16a768581638bb26cf Mon Sep 17 00:00:00 2001 From: Madhu22 Date: Mon, 6 May 2024 14:22:01 -0700 Subject: [PATCH 01/65] changed env --- src/utils/firebaseAuthUtil.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/firebaseAuthUtil.js b/src/utils/firebaseAuthUtil.js index 95b1f39..63cc50b 100644 --- a/src/utils/firebaseAuthUtil.js +++ b/src/utils/firebaseAuthUtil.js @@ -7,13 +7,13 @@ import { getAuth } from 'firebase/auth'; // Your web app's Firebase configuration // For Firebase JS SDK v7.20.0 and later, measurementId is optional const firebaseConfig = { - apiKey: import.meta.env.VITE_REACT_APP_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_REACT_APP_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_REACT_APP_FIREBASE_PROJECT_ID, - storageBucket: import.meta.env.VITE_REACT_APP_FIREBASE_STORAGE_BUCKET, - messagingSenderId: import.meta.env.VITE_REACT_APP_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_REACT_APP_FIREBASE_APP_ID, - measurementId: import.meta.env.VITE_REACT_APP_FIREBASE_MEASUREMENT_ID, + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID, + measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID, }; // Initialize Firebase From 06936b4b476f94c34811da8daf8d7a0f81ca1801 Mon Sep 17 00:00:00 2001 From: Justin Liao Date: Mon, 6 May 2024 16:54:40 -0700 Subject: [PATCH 02/65] Update CD.yml --- .github/workflows/CD.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index f5a13bc..f47ee29 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -2,7 +2,7 @@ name: S3 Deploy on: push: branches: - - dev-deployment + - main env: NODE_ENV: ${{ secrets.NODE_ENV }} VITE_FIREBASE_APIKEY: ${{ secrets.VITE_FIREBASE_APIKEY }} @@ -48,4 +48,4 @@ jobs: delete-removed: true no-cache: true private: true - filesToInclude: '.*/*,*/*,**' \ No newline at end of file + filesToInclude: '.*/*,*/*,**' From 617527c0a4002de21ef974827c1400accf851f0d Mon Sep 17 00:00:00 2001 From: Justin Liao Date: Mon, 6 May 2024 16:59:02 -0700 Subject: [PATCH 03/65] Rename ViewDonation.Module.css --- .../ViewDonation/{ViewDonation.Module.css => ViewDonation.css} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/ViewDonation/{ViewDonation.Module.css => ViewDonation.css} (100%) diff --git a/src/components/ViewDonation/ViewDonation.Module.css b/src/components/ViewDonation/ViewDonation.css similarity index 100% rename from src/components/ViewDonation/ViewDonation.Module.css rename to src/components/ViewDonation/ViewDonation.css From 6de400605f63832c1e91a9dbacd8f63888549a1e Mon Sep 17 00:00:00 2001 From: Justin Liao Date: Mon, 6 May 2024 16:59:15 -0700 Subject: [PATCH 04/65] Rename ViewDonation.css --- .../ViewDonation/{ViewDonation.css => ViewDonation.module.css} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/ViewDonation/{ViewDonation.css => ViewDonation.module.css} (100%) diff --git a/src/components/ViewDonation/ViewDonation.css b/src/components/ViewDonation/ViewDonation.module.css similarity index 100% rename from src/components/ViewDonation/ViewDonation.css rename to src/components/ViewDonation/ViewDonation.module.css From 7ffab62db416e17e9c5038f23c98b2e22e4cc3d5 Mon Sep 17 00:00:00 2001 From: Madhu22 Date: Mon, 6 May 2024 18:40:47 -0700 Subject: [PATCH 05/65] changes --- src/utils/firebaseAuthUtil.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/firebaseAuthUtil.js b/src/utils/firebaseAuthUtil.js index 63cc50b..b528c0f 100644 --- a/src/utils/firebaseAuthUtil.js +++ b/src/utils/firebaseAuthUtil.js @@ -7,13 +7,13 @@ import { getAuth } from 'firebase/auth'; // Your web app's Firebase configuration // For Firebase JS SDK v7.20.0 and later, measurementId is optional const firebaseConfig = { - apiKey: import.meta.env.VITE_FIREBASE_API_KEY, - authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, - projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, - storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, - messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, - appId: import.meta.env.VITE_FIREBASE_APP_ID, - measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID, + apiKey: import.meta.env.VITE_FIREBASE_APIKEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECTID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID, + appId: import.meta.env.VITE_FIREBASE_APPID, + measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENTID, }; // Initialize Firebase From 096743196a33091ab6e8e8e1bbe8df5c14919b47 Mon Sep 17 00:00:00 2001 From: Benson Manzano <72283466+benson-fm@users.noreply.github.com> Date: Wed, 15 May 2024 17:51:46 -0700 Subject: [PATCH 06/65] Updated README.md --- README.md | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 8bc027e..232a0fd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# npo-frontend-template +# Feeding Pets of the Homeless Frontend -This template will be used to create all NPO frontend repos +This is the frontend for the Feeding Pets of the Homeless Project. ## Setting up development environment @@ -29,11 +29,6 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\ You will also see any lint errors in the console. -### `yarn storybook` - -Runs Storybook.\ -Open [http://localhost:6006/](http://localhost:6006) to view it in the browser. - ### `yarn format` Formats `.js`, `.jsx`, `.css` files with Prettier.\ @@ -49,20 +44,6 @@ Your app is ready to be deployed! See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. -### `yarn eject` - -**Note: this is a one-way operation. Once you `eject`, you can't go back!** - -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. - -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. - -## Storybook - -This project has Storybook installed to help build UI components and pages in isolation. Read more about Storybook [here](https://storybook.js.org/). - ## ESLint and Prettier This project uses ESLint and Prettier to enforce the [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript). @@ -95,8 +76,8 @@ The configuration for lint-staged is inside the `lint-staged` object inside of ` The configuration for husky is in the `.husky` directory, located in the root of the project. Learn more about husky [here](https://typicode.github.io/husky/). -## Learn More about Create React App and React +## Learn More about Vite and React -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). +You can learn more in the [Vite documentation](https://vitejs.dev/). -To learn React, check out the [React documentation](https://reactjs.org/). \ No newline at end of file +To learn React, check out the [React documentation](https://reactjs.org/). From 710a972af504cb2cc31fda27dbb04c087797da17 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Sat, 25 May 2024 11:45:37 -0700 Subject: [PATCH 07/65] ci: add prod cd workflow --- .github/workflows/CD-prod.yml | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/CD-prod.yml diff --git a/.github/workflows/CD-prod.yml b/.github/workflows/CD-prod.yml new file mode 100644 index 0000000..1fa7660 --- /dev/null +++ b/.github/workflows/CD-prod.yml @@ -0,0 +1,51 @@ +name: S3 Deploy +on: + push: + branches: + - main +env: + NODE_ENV: ${{ secrets.NODE_ENV }} + VITE_FIREBASE_APIKEY: ${{ secrets.VITE_FIREBASE_APIKEY }} + VITE_FIREBASE_AUTHDOMAIN: ${{ secrets.VITE_FIREBASE_AUTHDOMAIN }} + VITE_FIREBASE_PROJECTID: ${{ secrets.VITE_FIREBASE_PROJECTID }} + VITE_FIREBASE_STORAGEBUCKET: ${{ secrets.VITE_FIREBASE_STORAGEBUCKET }} + VITE_FIREBASE_MESSAGINGSENDERID: ${{ secrets.VITE_FIREBASE_MESSAGINGSENDERID }} + VITE_FIREBASE_APPID: ${{ secrets.VITE_FIREBASE_APPID }} + VITE_EMAIL_USERNAME: ${{ secrets.VITE_EMAIL_USERNAME }} + VITE_BACKEND_HOSTNAME: ${{ secrets.PROD_VITE_BACKEND_HOSTNAME }} + +jobs: + run: + runs-on: ubuntu-latest + env: + AWS_ACCESS_KEY_ID: ${{ secrets.PROD_AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.PROD_AWS_SECRET_ACCESS_KEY }} + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + + - name: Install dependencies + uses: borales/actions-yarn@v4 + with: + cmd: install + + - name: Build + uses: borales/actions-yarn@v4 + with: + cmd: build + + - name: Deploy + uses: reggionick/s3-deploy@v4 + with: + folder: dist + bucket: ${{ secrets.PROD_S3_BUCKET }} + bucket-region: ${{ secrets.S3_BUCKET_REGION }} + dist-id: ${{ secrets.PROD_CLOUDFRONT_DISTRIBUTION_ID }} + invalidation: / + delete-removed: true + no-cache: true + private: true + filesToInclude: '.*/*,*/*,**' From 87ceda48eecc7a41f02b2c82ead4e96536dfbf53 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Sat, 25 May 2024 14:20:02 -0700 Subject: [PATCH 08/65] ci (chore): update deployment workflow name --- .github/workflows/CD-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CD-prod.yml b/.github/workflows/CD-prod.yml index 1fa7660..75cb6df 100644 --- a/.github/workflows/CD-prod.yml +++ b/.github/workflows/CD-prod.yml @@ -1,4 +1,4 @@ -name: S3 Deploy +name: S3 Prod Deploy on: push: branches: From 2cba696ca2165632c30ead9e132f309171f1de8c Mon Sep 17 00:00:00 2001 From: Ostend Suryajaya Date: Mon, 17 Jun 2024 19:23:05 -0700 Subject: [PATCH 09/65] Added project description to README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 232a0fd..312982a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ This is the frontend for the Feeding Pets of the Homeless Project. +## Project Description +Feeding Pets of the Homeless® is the first national nonprofit providing pet food and emergency veterinary care to pets that belong to people experiencing homelessness. The organization finds volunteer dondation centers for food to participate. These donation centers will then assist in distributing food to food banks. This project expedites the onboarding and donation reporting processes for donation sites by providing a unified platform where donation sites can register themselves and subsequently submit their donation sheets. Using this data, donation sites can track their contributions with ease.Furthermore, Feeding Pets of the Homeless can access a view of all participating donation sites, monitor their onboarding status and review their latest donation submissions. To streamline communication and reduce operational friction, the website also features an automated reminder system. This mitigates the need for the organization to make frequent calls or dispatch representatives, ensuring a seamless collaboration between itself and participating partners. + + ## Setting up development environment To start working on with this project, follow these steps: From e01ee64325c19e7c5ed576d427c98502641c86e2 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Tue, 23 Jul 2024 23:37:58 -0700 Subject: [PATCH 10/65] refactor: cleanup Sidebar --- src/App.jsx | 8 +- .../Sidebar/ContactInformationModal.jsx | 0 .../Sidebar/ContactInformationModal.tsx | 58 +++++ src/components/Sidebar/Sidebar.jsx | 236 ------------------ src/components/Sidebar/Sidebar.module.css | 0 src/components/Sidebar/Sidebar.tsx | 180 +++++++++++++ src/components/Sidebar/fph-logo.png.png | Bin 86617 -> 0 bytes 7 files changed, 240 insertions(+), 242 deletions(-) delete mode 100644 src/components/Sidebar/ContactInformationModal.jsx create mode 100644 src/components/Sidebar/ContactInformationModal.tsx delete mode 100644 src/components/Sidebar/Sidebar.jsx delete mode 100644 src/components/Sidebar/Sidebar.module.css create mode 100644 src/components/Sidebar/Sidebar.tsx delete mode 100644 src/components/Sidebar/fph-logo.png.png diff --git a/src/App.jsx b/src/App.jsx index 10960db..64c8f00 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ import { Route, Routes, useLocation } from 'react-router-dom'; import { BackendProvider } from './contexts/BackendContext'; -import Sidebar from './components/Sidebar/Sidebar.jsx'; +import Sidebar from './components/Sidebar/Sidebar'; import BusinessDashboard from './components/BusinessDashboard/BusinessDashboard'; import DonationForm from './components/DonationForm/DonationForm'; import BusinessNotificationCenter from './components/BusinessNotificationCenter/BusinessNotificationCenter'; @@ -40,11 +40,7 @@ const App = () => { currentRoute == '/businessform' || }
- } - /> + } /> { + return ( + + + + Contact Information + + + + + + Address + + 710 W Washington St, Carson City, NV 89703 + + + + + + + + Phone Number + + (775) 841-7463 + + + + + + + + Email + + info@petsofthehomeless.org + +
+
+
+
+ ); +}; diff --git a/src/components/Sidebar/Sidebar.jsx b/src/components/Sidebar/Sidebar.jsx deleted file mode 100644 index 4ae33e6..0000000 --- a/src/components/Sidebar/Sidebar.jsx +++ /dev/null @@ -1,236 +0,0 @@ -import { Link, useNavigate } from 'react-router-dom'; -import PropTypes from 'prop-types'; -import { - ChakraProvider, - Button, - VStack, - Divider, - Flex, - Image, - Box, - Text, - HStack, - useDisclosure, - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - Spacer, - Icon, -} from '@chakra-ui/react'; -import 'boxicons'; -import fphLogo from './fph-logo.png.png'; -import { useAuth } from '../../contexts/AuthContext'; -import { useBackend } from '../../contexts/BackendContext'; -import { useEffect, useState } from 'react'; -import { BiBuildingHouse, BiPhone, BiEnvelope } from 'react-icons/bi'; - -function Sidebar() { - const { backend } = useBackend(); - const [isAdminUser, setIsAdminUser] = useState(false); - const { isOpen, onOpen, onClose } = useDisclosure(); // State management for modal - - useEffect(() => { - const checkIsAdmin = async () => { - if (await isAdmin()) { - setIsAdminUser(true); - } - }; - - checkIsAdmin(); - }); - - useEffect(() => { - getBusinessName(); - }, - [backend]); - - const businessList = [ - { name: 'Home', path: '/BusinessDashboard', icon: 'home-smile' }, - { name: 'Donation History', path: '/BusinessDonationHistory', icon: 'heart-circle' }, - { name: 'Supply Request', path: '/ContactUs', icon: 'package' }, - { name: 'Account', path: '/EditContactInformation', icon: 'user' }, - ]; - - const adminList = [ - { name: 'Home', path: '/AdminDashboard', icon: 'home-smile' }, - { name: 'Donation Tracking', path: '/DonationTrackingTable', icon: 'building-house' }, - { name: 'Donation Items', path: '/DonationItemsTable', icon: 'heart-circle' }, - { name: 'Settings', path: '/AdminSettings', icon: 'cog' }, - ]; - - const navigate = useNavigate(); - const { logout, isAdmin, currentUser } = useAuth(); - const [businessName, setBusinessName] = useState(); - - const getBusinessName = async () => { - const businessIdResponse = await backend.get(`/businessUser/${currentUser.uid}`); - const businessContactResponse = await backend.get(`/business/${businessIdResponse.data[0].id}`); - const businessName = businessContactResponse.data[0].name; - setBusinessName(businessName); - }; - - const authLogout = async () => { - try { - logout(); - navigate('/login'); - } catch (error) { - console.log(error.message); - } - }; - - const navList = isAdminUser ? adminList : businessList; - return ( - - <> - - - - - FPH Logo - - {isAdminUser ? 'Feeding Pets of the Homeless' : businessName} - - - - - {navList.map(item => ( - - - - ))} - - - - {!isAdminUser && ( - - - - )} - - - - Laura Brown - - laurabrown@fph.com - - - - - - - - - - - - Contact Information - - - - - - - - Address - {' '} - {/* Added margin left for spacing */} - - 710 W Washington St, Carson City, NV 89703 - - - - - - - Phone Number - {' '} - {/* Added margin left for spacing */} - - (775) 841-7463 - - - - - - - Email - {' '} - {/* Added margin left for spacing */} - - info@petsofthehomeless.org -
-
-
-
- -
- ); -} - -Sidebar.propTypes = { - isAdmin: PropTypes.bool.isRequired, -}; - -export default Sidebar; diff --git a/src/components/Sidebar/Sidebar.module.css b/src/components/Sidebar/Sidebar.module.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..c24d589 --- /dev/null +++ b/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,180 @@ +import { Link, useNavigate } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { + Button, + VStack, + Divider, + Flex, + Image, + Box, + Text, + HStack, + useDisclosure, + Icon, + IconButton, +} from '@chakra-ui/react'; + +// @ts-expect-error this image path does exist +import fphLogo from '/fph-logo.png'; +import { useAuth } from '../../contexts/AuthContext'; +import { useBackend } from '../../contexts/BackendContext'; +import { useEffect, useState } from 'react'; +import { + BiLogOut, + BiPackage, + BiHeartCircle, + BiHomeSmile, + BiUser, + BiHelpCircle, +} from 'react-icons/bi'; +import { ContactInformationModal } from './ContactInformationModal'; +import React from 'react'; + +function Sidebar() { + const { backend } = useBackend(); + const [isAdminUser, setIsAdminUser] = useState(false); + const { isOpen, onOpen, onClose } = useDisclosure(); // State management for modal + + useEffect(() => { + const checkIsAdmin = async () => { + if (await isAdmin()) { + setIsAdminUser(true); + } + }; + + checkIsAdmin(); + }); + + useEffect(() => { + getBusinessName(); + }, [backend]); + + const businessList = [ + { name: 'Home', path: '/BusinessDashboard', icon: BiHomeSmile }, + { name: 'Donation History', path: '/BusinessDonationHistory', icon: BiHeartCircle }, + { name: 'Supply Request', path: '/ContactUs', icon: BiPackage }, + { name: 'Account', path: '/EditContactInformation', icon: BiUser }, + ]; + + const adminList = [ + { name: 'Home', path: '/AdminDashboard', icon: 'home-smile' }, + { name: 'Donation Tracking', path: '/DonationTrackingTable', icon: 'building-house' }, + { name: 'Donation Items', path: '/DonationItemsTable', icon: 'heart-circle' }, + { name: 'Settings', path: '/AdminSettings', icon: 'cog' }, + ]; + + const navigate = useNavigate(); + const { logout, isAdmin, currentUser } = useAuth(); + const [businessName, setBusinessName] = useState(); + + const getBusinessName = async () => { + const businessIdResponse = await backend.get(`/businessUser/${currentUser.uid}`); + const businessContactResponse = await backend.get(`/business/${businessIdResponse.data[0].id}`); + const businessName = businessContactResponse.data[0].name; + setBusinessName(businessName); + }; + + const authLogout = async () => { + try { + logout(); + navigate('/login'); + } catch (error) { + console.log(error.message); + } + }; + + const navList = isAdminUser ? adminList : businessList; + + return ( + <> + + + + + FPH Logo + + {/* fix me */} + + {true || isAdminUser ? 'Feeding Pets of the Homeless' : businessName} + + + + + + + {navList.map(item => { + console.log(item.icon); + return ( + + + + ); + })} + + + + + + {!isAdminUser && ( + + )} + + + + {currentUser?.displayName} + + {currentUser?.email} + + + + } + onClick={authLogout} + /> + + + + + + + + + ); +} + +Sidebar.propTypes = { + isAdmin: PropTypes.bool.isRequired, +}; + +export default Sidebar; diff --git a/src/components/Sidebar/fph-logo.png.png b/src/components/Sidebar/fph-logo.png.png deleted file mode 100644 index b45acde6927eee9d73e241741b3204a6bc0e88d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86617 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 >=#@qw(8s5?{ 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 From 92a5b701c4578d29f6498d8acc0601fa8918f594 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Wed, 24 Jul 2024 00:18:01 -0700 Subject: [PATCH 11/65] refactor: clean up App and routes --- src/App.jsx | 85 +++++++------------ .../Authentication/ForgotPassword.jsx | 5 -- src/components/Authentication/Signup.jsx | 6 +- src/components/CatchAll.tsx | 24 ++++++ .../OnBoarding/BusinessFormMaster.jsx | 15 +--- src/components/OnBoarding/FourthForm.jsx | 15 +--- .../SetUp/BusinessSetupPageMaster.jsx | 2 +- src/components/Sidebar/Sidebar.tsx | 5 -- src/utils/ProtectedRoute.jsx | 2 +- 9 files changed, 63 insertions(+), 96 deletions(-) create mode 100644 src/components/CatchAll.tsx diff --git a/src/App.jsx b/src/App.jsx index 64c8f00..cc59d6b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,14 @@ import { Route, Routes, useLocation } from 'react-router-dom'; -import { BackendProvider } from './contexts/BackendContext'; +import { BackendProvider } from './contexts/BackendContext.jsx'; import Sidebar from './components/Sidebar/Sidebar'; -import BusinessDashboard from './components/BusinessDashboard/BusinessDashboard'; -import DonationForm from './components/DonationForm/DonationForm'; -import BusinessNotificationCenter from './components/BusinessNotificationCenter/BusinessNotificationCenter'; -import AdminDashboard from './components/AdminDashboard/AdminDashboard'; -import EditContactInformation from './components/EditContactInformation'; +import BusinessDashboard from './components/BusinessDashboard/BusinessDashboard.jsx'; +import DonationForm from './components/DonationForm/DonationForm.jsx'; +import BusinessNotificationCenter from './components/BusinessNotificationCenter/BusinessNotificationCenter.jsx'; +import AdminDashboard from './components/AdminDashboard/AdminDashboard.jsx'; +import EditContactInformation from './components/EditContactInformation.jsx'; import BusinessDonationHistory from './components/BusinessDonationHistory/BusinessDonationHistory.jsx'; import ViewDonationHistory from './components/BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory.jsx'; -import ContactUs from './components/ContactUsForm/ContactUs'; +import ContactUs from './components/ContactUsForm/ContactUs.jsx'; import DonationTrackingTable from './components/DonationTrackingTable/DonationTrackingTable.jsx'; import styles from './App.module.css'; import DonationItemsTable from './components/DonationItemsTable/DonationItemsTable.jsx'; @@ -16,7 +16,7 @@ import BusinessSetupPageMaster from './components/SetUp/BusinessSetupPageMaster. import Login from './components/Authentication/Login.jsx'; import BusinessFormMaster from './components/OnBoarding/BusinessFormMaster.jsx'; import ForgotPassword from './components/Authentication/ForgotPassword.jsx'; -import { AuthProvider } from './contexts/AuthContext'; +import { AuthProvider } from './contexts/AuthContext.jsx'; import ProtectedRoute from './utils/ProtectedRoute.jsx'; import ViewBusiness from './components/ViewBusiness/ViewBusiness.jsx'; import { AddBusinessForm, BusinessForm } from './components/BusinessForm/BusinessForm.jsx'; @@ -24,117 +24,96 @@ import Congrats from './components/DonationForm/Congrats.jsx'; import ViewRequest from './components/ViewRequest/ViewRequest.jsx'; import AdminSettingsMaster from './components/AdminSettings/AdminSettingsMaster.jsx'; import ViewDonation from './components/ViewDonation/ViewDonation.jsx'; +import CatchAll from './components/CatchAll'; const App = () => { const location = useLocation(); const currentRoute = location.pathname.toLowerCase(); + + const shouldShowSidebar = !( + currentRoute == '/onboarding' || + currentRoute == '/signupadmin' || + currentRoute == '/signupbusiness' || + currentRoute == '/login' || + currentRoute == '/forgotpassword' || + currentRoute == '/businessform' + ); + return (
- {currentRoute == '/onboarding' || - currentRoute == '/signupadmin' || - currentRoute == '/signupbusiness' || - currentRoute == '/login' || - currentRoute == '/forgotpassword' || - currentRoute == '/businessform' || } + {shouldShowSidebar ? : null}
- } /> - } - /> - } - /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> } /> } /> } /> } /> } > } > } > } /> } /> } /> } /> + } /> } - /> - } /> } /> - } - /> + } /> + + {/* Catch-all route */} + } />
diff --git a/src/components/Authentication/ForgotPassword.jsx b/src/components/Authentication/ForgotPassword.jsx index e382675..49d0b41 100644 --- a/src/components/Authentication/ForgotPassword.jsx +++ b/src/components/Authentication/ForgotPassword.jsx @@ -14,7 +14,6 @@ import { Text, } from '@chakra-ui/react'; import { useAuth } from '../../contexts/AuthContext'; -import PropTypes from 'prop-types'; import LOGO from './fph_logo.png'; const ForgotPassword = () => { @@ -75,8 +74,4 @@ const ForgotPassword = () => { ); }; -ForgotPassword.propTypes = { - isAdmin: PropTypes.bool.isRequired, -}; - export default ForgotPassword; diff --git a/src/components/Authentication/Signup.jsx b/src/components/Authentication/Signup.jsx index 70eadbb..a086c3b 100644 --- a/src/components/Authentication/Signup.jsx +++ b/src/components/Authentication/Signup.jsx @@ -89,11 +89,7 @@ const Signup = ({ isAdmin }) => { Already have an account?{' '} - + Log In diff --git a/src/components/CatchAll.tsx b/src/components/CatchAll.tsx new file mode 100644 index 0000000..aec3cfc --- /dev/null +++ b/src/components/CatchAll.tsx @@ -0,0 +1,24 @@ +import React, { useEffect } from 'react'; +import { useAuth } from '../contexts/AuthContext'; +import { useNavigate } from 'react-router-dom'; + +const CatchAll = () => { + const { isAdmin } = useAuth(); + const navigate = useNavigate(); + + useEffect(() => { + const checkIsAdmin = async () => { + if (await isAdmin()) { + navigate('/AdminDashboard'); + } else { + navigate('/BusinessDashboard'); + } + }; + + checkIsAdmin(); + }, []); + + return null; +}; + +export default CatchAll; diff --git a/src/components/OnBoarding/BusinessFormMaster.jsx b/src/components/OnBoarding/BusinessFormMaster.jsx index d5f15a0..c7d4a95 100644 --- a/src/components/OnBoarding/BusinessFormMaster.jsx +++ b/src/components/OnBoarding/BusinessFormMaster.jsx @@ -5,13 +5,12 @@ import ThirdForm from './ThirdForm'; import FourthForm from './FourthForm'; import { useBackend } from '../../contexts/BackendContext'; import { Box, Text, SimpleGrid, Stack, Image, Flex } from '@chakra-ui/react'; -import propTypes from 'prop-types'; import ICON1 from './icon1.png'; import ICON2 from './icon2.png'; import ICON3 from './icon3.png'; import ICON4 from './icon4.png'; -const BusinessFormMaster = ({ setFormOpen }) => { +const BusinessFormMaster = () => { const { backend } = useBackend(); const [formData, setFormData] = useState({ businessName: '', @@ -156,13 +155,7 @@ const BusinessFormMaster = ({ setFormOpen }) => { handleSubmit={handleSubmit} setFormData={setFormData} />, - , + , ]; return ( @@ -303,8 +296,4 @@ const BusinessFormMaster = ({ setFormOpen }) => { ); }; -BusinessFormMaster.propTypes = { - setFormOpen: propTypes.func.isRequired, -}; - export default BusinessFormMaster; diff --git a/src/components/OnBoarding/FourthForm.jsx b/src/components/OnBoarding/FourthForm.jsx index d3eab7f..c6ae535 100644 --- a/src/components/OnBoarding/FourthForm.jsx +++ b/src/components/OnBoarding/FourthForm.jsx @@ -1,8 +1,7 @@ import { Box, Link, Text, Image, Flex, Stack } from '@chakra-ui/react'; import FPH_LOGO from './fph_logo.png'; -import propTypes from 'prop-types'; -const FourthForm = ({ setFormOpen }) => { +const FourthForm = () => { return ( @@ -19,13 +18,7 @@ const FourthForm = ({ setFormOpen }) => { Thank you for taking the time to apply for becoming one of our donors! While our team reviews your application, see what we've been up to by heading back to our  { - { - setFormOpen(false); - }} - > + website } @@ -37,8 +30,4 @@ const FourthForm = ({ setFormOpen }) => { ); }; -FourthForm.propTypes = { - setFormOpen: propTypes.func.isRequired, -}; - export default FourthForm; diff --git a/src/components/SetUp/BusinessSetupPageMaster.jsx b/src/components/SetUp/BusinessSetupPageMaster.jsx index 65a7c82..037a602 100644 --- a/src/components/SetUp/BusinessSetupPageMaster.jsx +++ b/src/components/SetUp/BusinessSetupPageMaster.jsx @@ -87,7 +87,7 @@ const BusinessSetupPageMaster = ({ isAdmin }) => { }; BusinessSetupPageMaster.propTypes = { - isAdmin: propTypes.func.isRequired, + isAdmin: propTypes.bool.isRequired, }; export default BusinessSetupPageMaster; diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index c24d589..73fff09 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -1,5 +1,4 @@ import { Link, useNavigate } from 'react-router-dom'; -import PropTypes from 'prop-types'; import { Button, VStack, @@ -173,8 +172,4 @@ function Sidebar() { ); } -Sidebar.propTypes = { - isAdmin: PropTypes.bool.isRequired, -}; - export default Sidebar; diff --git a/src/utils/ProtectedRoute.jsx b/src/utils/ProtectedRoute.jsx index bc609a0..778dc1f 100644 --- a/src/utils/ProtectedRoute.jsx +++ b/src/utils/ProtectedRoute.jsx @@ -17,7 +17,7 @@ const ProtectedRoute = ({ Component }) => { return

Loading...

; } - return isAuthenticated ? : ; + return isAuthenticated ? : ; }; export default ProtectedRoute; From f3733925d230c198d62699caa9d32424de50fcc2 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Mon, 29 Jul 2024 22:31:48 -0700 Subject: [PATCH 12/65] feat (wip): settings page --- src/components/EditContactInformation.jsx | 143 ++++++------------ src/components/ReferenceGuide.jsx | 171 +++++++++++----------- 2 files changed, 130 insertions(+), 184 deletions(-) diff --git a/src/components/EditContactInformation.jsx b/src/components/EditContactInformation.jsx index 0348b84..24ed493 100644 --- a/src/components/EditContactInformation.jsx +++ b/src/components/EditContactInformation.jsx @@ -99,12 +99,12 @@ const EditContactInformation = () => { const notificationData = { businessId: FPH_ID, - message: "Edited their business information.", + message: 'Edited their business information.', type: 'Edited Information', senderId: businessId, businessName: businessContactInfo.businessName, donationId: null, - } + }; await backend.post('/notification', notificationData); return toast({ @@ -132,10 +132,15 @@ const EditContactInformation = () => { const name = businessContact.contact_name.split(' '); const firstName = name[0]; const lastName = name[1]; - + let formattedPhoneNumber = ''; if (businessContact.primary_phone !== '') { - formattedPhoneNumber = businessContact.primary_phone.slice(0, 3) + '-' + businessContact.primary_phone.slice(3, 6) + '-' + businessContact.primary_phone.slice(6); + formattedPhoneNumber = + businessContact.primary_phone.slice(0, 3) + + '-' + + businessContact.primary_phone.slice(3, 6) + + '-' + + businessContact.primary_phone.slice(6); } setBusinessContactInfo({ @@ -174,37 +179,33 @@ const EditContactInformation = () => { return ( <> - - + + Settings - + + - - + + + Business Reference Guide + - - - - - + + + + + BUSINESS NAME { setBusinessContactInfo({ @@ -215,14 +216,8 @@ const EditContactInformation = () => { }} /> - - + + NAME { placeholder="Enter First Name" value={businessContactInfo.firstName} name="firstName" - width={'34.5%'} onChange={handleChange} /> { placeholder="Enter Last Name" value={businessContactInfo.lastName} name="lastName" - width={'34%'} onChange={handleChange} /> - - + + EMAIL { value={businessContactInfo.email} name="email" onChange={handleChange} - width={'70%'} /> - - + + WEBSITE { value={businessContactInfo.website} name="website" onChange={handleChange} - width={'70%'} /> - - + + LOCATION { value={businessContactInfo.street} name="street" onChange={handleChange} - width={'70%'} /> - - + + + CITY + { setBusinessContactInfo({ @@ -323,7 +290,6 @@ const EditContactInformation = () => { { setBusinessContactInfo({ @@ -336,7 +302,6 @@ const EditContactInformation = () => { { setBusinessContactInfo({ ...businessContactInfo, zip: e.target.value }); @@ -344,14 +309,8 @@ const EditContactInformation = () => { }} /> - - + + PHONE { value={businessContactInfo.phoneNumber} name="phoneNumber" onChange={handleChange} - width={'70%'} /> - - + + BUSINESS HOURS { setBusinessContactInfo({ @@ -389,30 +340,28 @@ const EditContactInformation = () => { + - + - - + + + + diff --git a/src/components/ReferenceGuide.jsx b/src/components/ReferenceGuide.jsx index bfd2702..a5e04c9 100644 --- a/src/components/ReferenceGuide.jsx +++ b/src/components/ReferenceGuide.jsx @@ -2,7 +2,6 @@ import { Flex, Box, Card, - TabPanel, CardBody, Text, Accordion, @@ -37,95 +36,93 @@ const ReferenceGuide = () => { panelInfo: PropTypes.string.isRequired, }; return ( - - - - - Refer to the following if you have any questions, otherwise contact us at - - info@petsofthehomeless.org - + + + + Refer to the following if you have any questions, otherwise contact us at + + info@petsofthehomeless.org - - - - - - - - - - - - - - - Tips on how to increase donations - - - - - - - - - - Host events and ask that a donation of pet food be the admittance ticket. - - - - - - Offer a service with a donation as the fee for your services. Example: grooming, - pet sitting, boarding, etc. + /> + + + + + + + + + + + + + Tips on how to increase donations - - - - Fill a truck or police vehicle (popular choice). - - - - - Print our newsletter or flyer and post it to boards in your community with your - business name and address. - - - - - We are always open to other suggestions. - - - - - - - + + + + + + + + + Host events and ask that a donation of pet food be the admittance ticket. + + + + + + Offer a service with a donation as the fee for your services. Example: grooming, + pet sitting, boarding, etc. + + + + + Fill a truck or police vehicle (popular choice). + + + + + Print our newsletter or flyer and post it to boards in your community with your + business name and address. + + + + + We are always open to other suggestions. + + + + + + ); }; From bf0e385776b387be0333e91f262a7bc17e9c6b1c Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Mon, 29 Jul 2024 23:55:56 -0700 Subject: [PATCH 13/65] feat: add a small redirecting message to catch all --- src/components/CatchAll.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CatchAll.tsx b/src/components/CatchAll.tsx index aec3cfc..9646541 100644 --- a/src/components/CatchAll.tsx +++ b/src/components/CatchAll.tsx @@ -18,7 +18,7 @@ const CatchAll = () => { checkIsAdmin(); }, []); - return null; + return

Route not found... redirecting...

; }; export default CatchAll; From a226bc911052a1100c7ec7636de03189c49034cf Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Tue, 30 Jul 2024 00:06:35 -0700 Subject: [PATCH 14/65] chore: update icon list in sidebar --- src/components/Sidebar/Sidebar.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 73fff09..6e09a6e 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -25,6 +25,8 @@ import { BiHomeSmile, BiUser, BiHelpCircle, + BiBuildingHouse, + BiCog, } from 'react-icons/bi'; import { ContactInformationModal } from './ContactInformationModal'; import React from 'react'; @@ -56,10 +58,10 @@ function Sidebar() { ]; const adminList = [ - { name: 'Home', path: '/AdminDashboard', icon: 'home-smile' }, - { name: 'Donation Tracking', path: '/DonationTrackingTable', icon: 'building-house' }, - { name: 'Donation Items', path: '/DonationItemsTable', icon: 'heart-circle' }, - { name: 'Settings', path: '/AdminSettings', icon: 'cog' }, + { name: 'Home', path: '/AdminDashboard', icon: BiHomeSmile }, + { name: 'Donation Tracking', path: '/DonationTrackingTable', icon: BiBuildingHouse }, + { name: 'Donation Items', path: '/DonationItemsTable', icon: BiHeartCircle }, + { name: 'Settings', path: '/AdminSettings', icon: BiCog }, ]; const navigate = useNavigate(); @@ -107,7 +109,6 @@ function Sidebar() { {navList.map(item => { - console.log(item.icon); return ( - - {/* */} - - + ); diff --git a/src/components/AdminSettings/AdminSettings.module.css b/src/components/AdminSettings/AdminSettings.module.css index 04b54e3..bf314e9 100644 --- a/src/components/AdminSettings/AdminSettings.module.css +++ b/src/components/AdminSettings/AdminSettings.module.css @@ -15,21 +15,6 @@ padding: 32px; } -.roundedTable { - margin-top: var(--chakra-space-5); - padding: var(--chakra-space-3); - border-width: 1px; - border-radius: 12px; - overflow-x: auto; - background: var(--white, #fff); -} - -.addAdminContainer { - display: flex; - justify-content: space-between; - margin-bottom: 12px; -} - .tabs { margin-top: 24px; margin-bottom: 3%; diff --git a/src/components/AdminSettings/AdminSettingsMaster.jsx b/src/components/AdminSettings/AdminSettingsMaster.jsx index 225acc7..9e8b1e9 100644 --- a/src/components/AdminSettings/AdminSettingsMaster.jsx +++ b/src/components/AdminSettings/AdminSettingsMaster.jsx @@ -24,7 +24,7 @@ const AdminSettingsMaster = () => { const fetchNotifications = async () => { const response = await backend.get('/notification/0'); setNotification(response.data); - } + }; fetchNotifications(); }, [isAdmin, navigate]); @@ -41,10 +41,10 @@ const AdminSettingsMaster = () => { - + - + diff --git a/src/components/AdminSettings/AdminsTable.jsx b/src/components/AdminSettings/AdminsTable.jsx index 74bc9ec..7750449 100644 --- a/src/components/AdminSettings/AdminsTable.jsx +++ b/src/components/AdminSettings/AdminsTable.jsx @@ -8,6 +8,7 @@ import { Flex, IconButton, Table, + Text, Thead, Tbody, Tr, @@ -25,7 +26,6 @@ import classes from './AdminSettings.module.css'; const AdminsTable = () => { const { backend } = useBackend(); const [data, setData] = useState([]); - // const [editEmail, setEditEmail] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [currentItemNum, setCurrentItemNum] = useState(0); const [currentPageNum, setCurrentPageNum] = useState(1); @@ -110,19 +110,32 @@ const AdminsTable = () => { return ( <> -
+ - -
+ -
+ { @@ -143,20 +156,21 @@ const AdminsTable = () => { loadInfo={getAdminData} toast={toast} /> + - - - - + + + + {displayAdminTable()}
NameEmailLast UpdatedNameEmailLast Updated
-
+
@@ -167,14 +181,14 @@ const AdminsTable = () => { aria-label="Back button" isDisabled={currentPageNum <= 1} icon={} - variant='ghost' + variant="ghost" onClick={() => setCurrentPageNum(currentPageNum - 1)} /> = pageLimit} icon={} - variant='ghost' + variant="ghost" onClick={() => setCurrentPageNum(currentPageNum + 1)} />
From 8ae0b099d5ce717c2c981d7f53ce6c15c1ae9cde Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Tue, 30 Jul 2024 22:48:53 -0700 Subject: [PATCH 17/65] style: finish cleanup --- src/App.jsx | 2 +- src/components/AdminSettings/AdminAccount.jsx | 37 +++++++++---------- .../AdminSettings/AdminSettingsMaster.jsx | 4 +- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index cc59d6b..2a1fe15 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -22,7 +22,7 @@ import ViewBusiness from './components/ViewBusiness/ViewBusiness.jsx'; import { AddBusinessForm, BusinessForm } from './components/BusinessForm/BusinessForm.jsx'; import Congrats from './components/DonationForm/Congrats.jsx'; import ViewRequest from './components/ViewRequest/ViewRequest.jsx'; -import AdminSettingsMaster from './components/AdminSettings/AdminSettingsMaster.jsx'; +import { AdminSettingsMaster } from './components/AdminSettings/AdminSettingsMaster.jsx'; import ViewDonation from './components/ViewDonation/ViewDonation.jsx'; import CatchAll from './components/CatchAll'; diff --git a/src/components/AdminSettings/AdminAccount.jsx b/src/components/AdminSettings/AdminAccount.jsx index 9486476..46ea9cf 100644 --- a/src/components/AdminSettings/AdminAccount.jsx +++ b/src/components/AdminSettings/AdminAccount.jsx @@ -13,6 +13,14 @@ import { } from '@chakra-ui/react'; import { ArrowForwardIcon } from '@chakra-ui/icons'; +// duplicated from EditContactInformation +const formLabelStyles = { + minWidth: '150px', + fontSize: '16px', + fontWeight: 'bold', + alignItems: 'center', +}; + const AdminAccount = () => { const toast = useToast(); const { backend } = useBackend(); @@ -44,7 +52,6 @@ const AdminAccount = () => { const updateContactInfo = async () => { try { - console.log(adminContactInfo.firstName); await backend.put(`/adminuser/${adminContactInfo.email}`, { name: adminContactInfo.firstName + ' ' + adminContactInfo.lastName, }); @@ -94,13 +101,11 @@ const AdminAccount = () => { }; return ( - <> - - + + + - - NAME - + NAME { onChange={handleChange} /> - - - EMAIL - + + EMAIL { /> - + PASSWORD - + ); }; diff --git a/src/components/AdminSettings/AdminSettingsMaster.jsx b/src/components/AdminSettings/AdminSettingsMaster.jsx index 9e8b1e9..9e5dc3b 100644 --- a/src/components/AdminSettings/AdminSettingsMaster.jsx +++ b/src/components/AdminSettings/AdminSettingsMaster.jsx @@ -8,7 +8,7 @@ import { useNavigate } from 'react-router-dom'; import NotificationsDrawer from '../NotificationsDrawer/NotificationsDrawer.jsx'; import classes from './AdminSettings.module.css'; -const AdminSettingsMaster = () => { +export const AdminSettingsMaster = () => { const { isAdmin } = useAuth(); const navigate = useNavigate(); const [notification, setNotification] = useState([]); @@ -52,5 +52,3 @@ const AdminSettingsMaster = () => {
); }; - -export default AdminSettingsMaster; From f65e84222ddf16a769c3926428fa87607f942065 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Tue, 30 Jul 2024 22:54:16 -0700 Subject: [PATCH 18/65] chore: remove console log in authcontext --- src/contexts/AuthContext.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx index 396bcdb..c208a92 100644 --- a/src/contexts/AuthContext.jsx +++ b/src/contexts/AuthContext.jsx @@ -44,7 +44,6 @@ export function AuthProvider({ children }) { }; const isAdmin = async (user = currentUser) => { - console.log(user); if (user !== null) { try { let response = await backend.get(`/adminUser/${user.email}`); From b592f0e909818bd5ae26a4418e729d620b6babeb Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Tue, 30 Jul 2024 23:51:35 -0700 Subject: [PATCH 19/65] feat: cleanup --- src/App.jsx | 2 +- ...essDashboard.jsx => BusinessDashboard.tsx} | 286 ++++++------------ .../BusinessDashboard/BusinessTotals.tsx | 98 ++++++ src/types/notification.ts | 11 + 4 files changed, 205 insertions(+), 192 deletions(-) rename src/components/BusinessDashboard/{BusinessDashboard.jsx => BusinessDashboard.tsx} (60%) create mode 100644 src/components/BusinessDashboard/BusinessTotals.tsx create mode 100644 src/types/notification.ts diff --git a/src/App.jsx b/src/App.jsx index 2a1fe15..f158df1 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,7 @@ import { Route, Routes, useLocation } from 'react-router-dom'; import { BackendProvider } from './contexts/BackendContext.jsx'; import Sidebar from './components/Sidebar/Sidebar'; -import BusinessDashboard from './components/BusinessDashboard/BusinessDashboard.jsx'; +import { BusinessDashboard } from './components/BusinessDashboard/BusinessDashboard'; import DonationForm from './components/DonationForm/DonationForm.jsx'; import BusinessNotificationCenter from './components/BusinessNotificationCenter/BusinessNotificationCenter.jsx'; import AdminDashboard from './components/AdminDashboard/AdminDashboard.jsx'; diff --git a/src/components/BusinessDashboard/BusinessDashboard.jsx b/src/components/BusinessDashboard/BusinessDashboard.tsx similarity index 60% rename from src/components/BusinessDashboard/BusinessDashboard.jsx rename to src/components/BusinessDashboard/BusinessDashboard.tsx index bd97cb0..bf9dff0 100644 --- a/src/components/BusinessDashboard/BusinessDashboard.jsx +++ b/src/components/BusinessDashboard/BusinessDashboard.tsx @@ -16,8 +16,6 @@ import { Text, Icon, Flex, - Divider, - Center, Drawer, DrawerBody, DrawerHeader, @@ -25,19 +23,17 @@ import { DrawerContent, DrawerCloseButton, useDisclosure, + Heading, } from '@chakra-ui/react'; -import { BiDonateHeart, BiMoney, BiPackage } from 'react-icons/bi'; +import { BiPackage } from 'react-icons/bi'; import { useNavigate } from 'react-router-dom'; -import 'boxicons'; -import { - ArrowForwardIcon, - WarningIcon, - EmailIcon, - CheckCircleIcon, - TimeIcon, -} from '@chakra-ui/icons'; +import { ArrowForwardIcon, WarningIcon, EmailIcon, CheckCircleIcon } from '@chakra-ui/icons'; import ViewDonationHistory from '../BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory'; +// @ts-expect-error CSS file exists import classes from './BusinessDashboard.module.css'; +import React from 'react'; +import type { Notification } from '../../types/notification'; +import { BusinessTotals } from './BusinessTotals'; // Currently, this whole component depends on each notification having its correct 'type' when created. We defined 4 types: // 1. Donation Form Confirmation @@ -46,14 +42,14 @@ import classes from './BusinessDashboard.module.css'; // 4. Donation Request Receieved // However, none of the current code anywhere does this, and the type for all notifications is null by default. Either that has to be fixed // or this will need to be changed so that it doesn't rely on a notification having a type. -const BusinessDashboard = () => { +export const BusinessDashboard = () => { const [userName, setUserName] = useState(''); const { backend } = useBackend(); const { currentUser } = useAuth(); const navigate = useNavigate(); const [donationData, setDonationData] = useState([]); const [notifClicked, setNotifClicked] = useState(null); - const [currentQuarter, setCurrentQuarter] = useState(); + const [currentQuarter, setCurrentQuarter] = useState(); const { isOpen: requestDrawerIsOpen, onOpen: requestDrawerOnOpen, @@ -94,18 +90,18 @@ const BusinessDashboard = () => { // request to efficiently pull the prices of dry and canned dog and cat food // ************************************************************************* const [priceData, setPriceData] = useState([]); - const [reminderData, setReminderData] = useState([]); + const [reminderData, setReminderData] = useState([]); const [supplyRequest, setSupplyRequest] = useState([0, 0, 0, 0, 0, 0, 0]); const [notes, setNotes] = useState(''); const supplyRequestItems = [ - "Pet Food Decal", - "Decal", - "Homeless Card", - "Business Card", - "Donation Site Decal", - "Donation Site Bin Decals", - "Donation Envelopes", - "Homeless Card 2" + 'Pet Food Decal', + 'Decal', + 'Homeless Card', + 'Business Card', + 'Donation Site Decal', + 'Donation Site Bin Decals', + 'Donation Envelopes', + 'Homeless Card 2', ]; // const formatDate = timestamp => { @@ -152,15 +148,32 @@ const BusinessDashboard = () => { getData(); }, []); + useEffect(() => { + const currentTime = new Date(); + + if (currentTime.getMonth() <= 2) { + setCurrentQuarter('Q1'); + } else if (currentTime.getMonth() <= 5) { + setCurrentQuarter('Q2'); + } else if (currentTime.getMonth() <= 8) { + setCurrentQuarter('Q3'); + } else { + setCurrentQuarter('Q4'); + } + }, [setCurrentQuarter]); + const parseSupplyRequestData = async message => { const lines = message.split(','); - const numbersList = []; + const numbersList: number[] = []; let notes = ''; let notesFound = false; lines.forEach(line => { if (line.startsWith('"Notes":')) { - notes = line.substring(line.indexOf(':') + 2).trim().split('"')[0]; + notes = line + .substring(line.indexOf(':') + 2) + .trim() + .split('"')[0]; notesFound = true; } else if (!notesFound) { const value = line.split(':')[1]; @@ -175,47 +188,6 @@ const BusinessDashboard = () => { setNotes(notes); }; - const calculateTotalPounds = () => { - return ( - donationData['canned_dog_food_quantity'] + - donationData['dry_dog_food_quantity'] + - donationData['canned_cat_food_quantity'] + - donationData['dry_cat_food_quantity'] - ); - }; - - - const calculateTotalWorth = () => { - // *********************************************************************** - // CHANGE LATER: Right now the prices are just the first four rows of the - // fair_market_value table, thus if the table has less than four tables - // this function will break - // *********************************************************************** - //return donationData['canned_dog_food_quantity'] && priceData.length > 0 - // ? - //: 0; - const totalWorth = - (donationData['canned_dog_food_quantity'] || 0) * (priceData[0] ? priceData[0]['price'] : 0) + - (donationData['dry_dog_food_quantity'] || 0) * (priceData[1] ? priceData[1]['price'] : 0) + - (donationData['canned_cat_food_quantity'] || 0) * (priceData[2] ? priceData[2]['price'] : 0) + - (donationData['dry_cat_food_quantity'] || 0) * (priceData[3] ? priceData[3]['price'] : 0); - return totalWorth.toFixed(2); - - - }; - - const calculateTimeSince = () => { - if (!(reminderData.length > 0)) { - return 0; - } - const currentTime = new Date(); - const previousTime = new Date(reminderData[0].timestamp); - const timeDifference = Math.abs(currentTime - previousTime); - // Convert milliseconds to days - const daysDifference = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); - return daysDifference; - }; - const isUrgentNotif = () => { const currentTime = new Date(); const currentYear = currentTime.getFullYear(); @@ -235,18 +207,17 @@ const BusinessDashboard = () => { setCurrentQuarter('Q4'); } - const timeDifference = Math.abs(currentTime - dueDate); + const timeDifference = Math.abs(currentTime.getTime() - dueDate.getTime()); const daysDifference = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); return daysDifference < 3; }; - const isRedNotif = (reminder) => { - return reminder['type'] === "Not Submitted" && isUrgentNotif(reminder['timestamp']); - } + const isRedNotif = (reminder: Notification) => { + return reminder['type'] === 'Not Submitted' && isUrgentNotif(); + }; - const getNotifText = (reminder) => { + const getNotifText = (reminder: Notification) => { const notifDate = new Date(reminder['timestamp']); - console.log(notifDate); const formattedDate = notifDate.toLocaleDateString('en-US', { month: 'long', day: 'numeric' }); const notifText = { 'Donation Form Confirmation': `Donation form has been submitted for ${currentQuarter}. Thank you!`, @@ -256,12 +227,13 @@ const BusinessDashboard = () => { 'Supply Request Shipped': `Supply request submitted from ${formattedDate} has been shipped.`, 'Supply Request Received': 'Supplies will be shipped in 3-5 business days.', 'Supply Request': 'Supplies will be shipped in 3-5 business days.', - } + }; + return notifText[reminder['type']]; }; return !notifClicked ? ( -
+ { - - { - - - Welcome Back, {`${userName}`} - - - } - { - - } + + + Welcome Back, {userName} + + + - - - {} - { -
-
- - - ${calculateTotalWorth()} - - - - - Worth of Donation Supplies - - -
-
- } -
- { -
- -
- } - - {} - { -
-
- - - {calculateTotalPounds()} - - - - - Pounds of Food Donated - - -
-
- } -
- { -
- -
- } - - {} - { -
-
- - - {calculateTimeSince()} - - - - Days Since Last Submission - -
-
- } -
-
+ - +
- @@ -431,43 +326,54 @@ const BusinessDashboard = () => { isRedNotif(reminder) ? "#F56565" : "#359797"} - backgroundColor={() => isRedNotif(reminder) ? "#FED7D7" : ""} - className={() => isRedNotif(reminder) ? classes.warningNotif : ""} + borderColor={isRedNotif(reminder) ? '#F56565' : '#359797'} + backgroundColor={isRedNotif(reminder) ? '#FED7D7' : ''} + className={isRedNotif(reminder) ? classes.warningNotif : ''} > - - + + {' '} isRedNotif(reminder) ? "#E53E3E" : undefined} - > + color={isRedNotif(reminder) ? '#E53E3E' : undefined} + /> - + {reminder['type']} - {new Date(reminder['timestamp']).toLocaleDateString('en-us', { - timeZone: 'America/Los_Angeles', - year: 'numeric', - month: 'long', - day: 'numeric' })} + + {new Date(reminder['timestamp']).toLocaleDateString('en-us', { + timeZone: 'America/Los_Angeles', + year: 'numeric', + month: 'long', + day: 'numeric', + })} - {getNotifText(reminder)} + {getNotifText(reminder)} - +
- {' '} + Notifications
-
+ ) : ( - + ); }; - -export default BusinessDashboard; diff --git a/src/components/BusinessDashboard/BusinessTotals.tsx b/src/components/BusinessDashboard/BusinessTotals.tsx new file mode 100644 index 0000000..62a6ce5 --- /dev/null +++ b/src/components/BusinessDashboard/BusinessTotals.tsx @@ -0,0 +1,98 @@ +import { TimeIcon } from '@chakra-ui/icons'; +import { Center, Divider, Flex, Icon, Text, useBreakpointValue } from '@chakra-ui/react'; +import React from 'react'; +import { BiDonateHeart, BiMoney } from 'react-icons/bi'; +import { Notification } from '../../types/notification'; + +interface BusinessTotalsProps { + donationData: any; + reminderData: Notification[]; + priceData: any; +} + +const calculateTotalPounds = (donationData: any) => { + const totalPounds = + donationData['canned_dog_food_quantity'] + + donationData['dry_dog_food_quantity'] + + donationData['canned_cat_food_quantity'] + + donationData['dry_cat_food_quantity']; + + return totalPounds.toString() ?? 0; +}; + +const calculateTotalWorth = (donationData: any, priceData: any) => { + const totalWorth = + (donationData['canned_dog_food_quantity'] || 0) * (priceData[0] ? priceData[0]['price'] : 0) + + (donationData['dry_dog_food_quantity'] || 0) * (priceData[1] ? priceData[1]['price'] : 0) + + (donationData['canned_cat_food_quantity'] || 0) * (priceData[2] ? priceData[2]['price'] : 0) + + (donationData['dry_cat_food_quantity'] || 0) * (priceData[3] ? priceData[3]['price'] : 0); + return totalWorth.toFixed(2); +}; + +const calculateTimeSince = (reminderData: Notification[]) => { + if (reminderData.length <= 0) { + return 0; + } + + const currentTime = new Date(); + const previousTime = new Date(reminderData.at(0)?.timestamp ?? 0); + const timeDifference = Math.abs(currentTime.getTime() - previousTime.getTime()); + + const daysDifference = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); // milliseconds to days + return daysDifference; +}; + +export const BusinessTotals = ({ donationData, reminderData, priceData }: BusinessTotalsProps) => { + const displayIcon = useBreakpointValue({ base: 'block', lg: 'none', xl: 'block' }); + + const stats = [ + { + icon: BiMoney, + value: `$${calculateTotalWorth(donationData, priceData)}`, + label: 'Worth of Donation Supplies', + }, + { + icon: BiDonateHeart, + value: calculateTotalPounds(donationData), + label: 'Pounds of Food Donated', + }, + { + icon: TimeIcon, + value: calculateTimeSince(reminderData), + label: 'Days Since Last Submission', + }, + ]; + + return ( + + {stats.map((stat, index) => ( + + + + + + + {stat.value} + + + {stat.label} + + + + {index < stats.length - 1 && ( +
+ +
+ )} +
+ ))} +
+ ); +}; diff --git a/src/types/notification.ts b/src/types/notification.ts new file mode 100644 index 0000000..cb4b24b --- /dev/null +++ b/src/types/notification.ts @@ -0,0 +1,11 @@ +export type Notification = { + businessId: number; + notificationId: number; + message: string; + timestamp: Date; + beenDismissed: boolean; + type: string; + senderId: number; + businessName: string; + donationId: number; +}; From 0fdb6ac53f5387d988f8021a10f9a9de5d9aa568 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Wed, 31 Jul 2024 01:10:54 -0700 Subject: [PATCH 20/65] feat: jfc --- src/App.jsx | 6 +- .../AdminDashboard/AdminDashboard.jsx | 17 ++-- .../BusinessDashboard/BusinessDashboard.tsx | 5 +- .../BusinessDashboard/BusinessTotals.tsx | 6 +- .../BusinessSetup/BusinessSetupPage.tsx | 75 ++++++++++++++ .../{SetUp => BusinessSetup}/FifthForm.jsx | 0 .../{SetUp => BusinessSetup}/FourthForm.jsx | 0 src/components/BusinessSetup/Information.tsx | 71 +++++++++++++ .../SetupSignup.tsx} | 29 +++--- src/components/BusinessSetup/Welcome.tsx | 22 +++++ .../{SetUp => BusinessSetup}/fph_logo.png | Bin src/components/CatchAll.tsx | 2 +- .../SetUp/BusinessSetupPageMaster.jsx | 93 ------------------ src/components/SetUp/SecondForm.jsx | 29 ------ src/components/SetUp/ThirdForm.jsx | 78 --------------- src/contexts/AuthContext.jsx | 4 +- tsconfig.json | 5 + 17 files changed, 206 insertions(+), 236 deletions(-) create mode 100644 src/components/BusinessSetup/BusinessSetupPage.tsx rename src/components/{SetUp => BusinessSetup}/FifthForm.jsx (100%) rename src/components/{SetUp => BusinessSetup}/FourthForm.jsx (100%) create mode 100644 src/components/BusinessSetup/Information.tsx rename src/components/{SetUp/FirstForm.jsx => BusinessSetup/SetupSignup.tsx} (92%) create mode 100644 src/components/BusinessSetup/Welcome.tsx rename src/components/{SetUp => BusinessSetup}/fph_logo.png (100%) delete mode 100644 src/components/SetUp/BusinessSetupPageMaster.jsx delete mode 100644 src/components/SetUp/SecondForm.jsx delete mode 100644 src/components/SetUp/ThirdForm.jsx create mode 100644 tsconfig.json diff --git a/src/App.jsx b/src/App.jsx index f158df1..234b8fb 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -12,7 +12,7 @@ import ContactUs from './components/ContactUsForm/ContactUs.jsx'; import DonationTrackingTable from './components/DonationTrackingTable/DonationTrackingTable.jsx'; import styles from './App.module.css'; import DonationItemsTable from './components/DonationItemsTable/DonationItemsTable.jsx'; -import BusinessSetupPageMaster from './components/SetUp/BusinessSetupPageMaster.jsx'; +import { BusinessSetupPage } from './components/BusinessSetup/BusinessSetupPage'; import Login from './components/Authentication/Login.jsx'; import BusinessFormMaster from './components/OnBoarding/BusinessFormMaster.jsx'; import ForgotPassword from './components/Authentication/ForgotPassword.jsx'; @@ -47,8 +47,8 @@ const App = () => {
} /> - } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/AdminDashboard/AdminDashboard.jsx b/src/components/AdminDashboard/AdminDashboard.jsx index 4d7bd7a..df7cab5 100644 --- a/src/components/AdminDashboard/AdminDashboard.jsx +++ b/src/components/AdminDashboard/AdminDashboard.jsx @@ -9,23 +9,22 @@ import BusinessTable from '../BusinessTable/BusinessTable.jsx'; import { BiBuildingHouse, BiDonateHeart, BiFile, BiTime } from 'react-icons/bi'; const AdminDashboard = () => { - // Created the use states const { backend } = useBackend(); const { isAdmin } = useAuth(); const navigate = useNavigate(); - const [businessDictionary, setBusinessDictionary] = useState([]); + const [businesses, setBusinesses] = useState([]); const [pendingBusinesses, setPendingBusinesses] = useState([]); const [notification, setNotifications] = useState([]); useEffect(() => { const getData = async () => { try { - checkIsAdmin(); - // fetches the business table to be used in pending &total # - const businessResponse = await backend.get('/business/filter/All'); - setBusinessDictionary(businessResponse.data); + const businessResponse = await backend.get('/business'); + setBusinesses(businessResponse.data); + const numPending = await backend.get('/business/totalBusinesses?tab=Pending'); setPendingBusinesses(numPending.data[0]['count']); + const notificationResponse = await backend.get(`/notification/0`); setNotifications(notificationResponse.data); } catch (error) { @@ -53,8 +52,8 @@ const AdminDashboard = () => { // } // } var submittedBusinesses = 0; - for (const [value] of Object.entries(businessDictionary)) { - const status = businessDictionary[value].submitted; + for (const [value] of Object.entries(businesses)) { + const status = businesses[value].submitted; if (status) { submittedBusinesses += 1; } @@ -65,7 +64,7 @@ const AdminDashboard = () => { // Counts number of Donation Sites const calculateTotalDonationSites = () => { - return Object.keys(businessDictionary).length; + return Object.keys(businesses).length; }; // Calculates number of pending applications from businesses diff --git a/src/components/BusinessDashboard/BusinessDashboard.tsx b/src/components/BusinessDashboard/BusinessDashboard.tsx index bf9dff0..d7c4be1 100644 --- a/src/components/BusinessDashboard/BusinessDashboard.tsx +++ b/src/components/BusinessDashboard/BusinessDashboard.tsx @@ -31,7 +31,6 @@ import { ArrowForwardIcon, WarningIcon, EmailIcon, CheckCircleIcon } from '@chak import ViewDonationHistory from '../BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory'; // @ts-expect-error CSS file exists import classes from './BusinessDashboard.module.css'; -import React from 'react'; import type { Notification } from '../../types/notification'; import { BusinessTotals } from './BusinessTotals'; @@ -233,7 +232,7 @@ export const BusinessDashboard = () => { }; return !notifClicked ? ( - + { ))} - + ) : ( ); diff --git a/src/components/BusinessDashboard/BusinessTotals.tsx b/src/components/BusinessDashboard/BusinessTotals.tsx index 62a6ce5..f666a8e 100644 --- a/src/components/BusinessDashboard/BusinessTotals.tsx +++ b/src/components/BusinessDashboard/BusinessTotals.tsx @@ -1,8 +1,8 @@ import { TimeIcon } from '@chakra-ui/icons'; import { Center, Divider, Flex, Icon, Text, useBreakpointValue } from '@chakra-ui/react'; -import React from 'react'; import { BiDonateHeart, BiMoney } from 'react-icons/bi'; import { Notification } from '../../types/notification'; +import { Fragment } from 'react/jsx-runtime'; interface BusinessTotalsProps { donationData: any; @@ -73,7 +73,7 @@ export const BusinessTotals = ({ donationData, reminderData, priceData }: Busine bg="#FFFFFF" > {stats.map((stat, index) => ( - + @@ -91,7 +91,7 @@ export const BusinessTotals = ({ donationData, reminderData, priceData }: Busine )} - + ))} ); diff --git a/src/components/BusinessSetup/BusinessSetupPage.tsx b/src/components/BusinessSetup/BusinessSetupPage.tsx new file mode 100644 index 0000000..6615687 --- /dev/null +++ b/src/components/BusinessSetup/BusinessSetupPage.tsx @@ -0,0 +1,75 @@ +import { useState } from 'react'; +import { SetupSignup } from './SetupSignup'; +import { Welcome } from './Welcome'; +import { Information } from './Information'; +import FourthForm from './FourthForm'; +import { useNavigate } from 'react-router-dom'; +import FifthForm from './FifthForm'; +import { Box, Flex } from '@chakra-ui/react'; +import { Button } from '@chakra-ui/react'; + +export const BusinessSetupPage = ({ isAdmin }) => { + const navigate = useNavigate(); + const steps = [ + { title: 'First', description: 'Contact Info' }, + { title: 'Second', description: 'Date & Time' }, + { title: 'Third', description: 'Select Rooms' }, + { title: 'Fourth', description: 'Fourth' }, + ]; + + const [activeStep, setActiveStep] = useState(0); + const [step, setStep] = useState(0); + + const nextStep = () => { + setActiveStep(prevState => prevState + 1); + setStep(prevState => prevState + 1); + }; + + const stepsComponents = [ + , + , + , + , + , + ]; + + return ( + + {stepsComponents[step]} + {step !== 0 ? ( + + + {steps.map((_, index) => ( + + ))} + + + + + ) : null} + + ); +}; diff --git a/src/components/SetUp/FifthForm.jsx b/src/components/BusinessSetup/FifthForm.jsx similarity index 100% rename from src/components/SetUp/FifthForm.jsx rename to src/components/BusinessSetup/FifthForm.jsx diff --git a/src/components/SetUp/FourthForm.jsx b/src/components/BusinessSetup/FourthForm.jsx similarity index 100% rename from src/components/SetUp/FourthForm.jsx rename to src/components/BusinessSetup/FourthForm.jsx diff --git a/src/components/BusinessSetup/Information.tsx b/src/components/BusinessSetup/Information.tsx new file mode 100644 index 0000000..6beb35a --- /dev/null +++ b/src/components/BusinessSetup/Information.tsx @@ -0,0 +1,71 @@ +import { Box, VStack, Text, HStack, Flex } from '@chakra-ui/react'; + +const steps = [ + { + id: 1, + description: + 'Visit our website to familiarize yourself with our mission, donation sites and pet food providers in your area, and other useful info.', + words: 3, + }, + { + id: 2, + description: + 'Set up a donation bin in an accessible area around your business. We accept donations of pet food (cat, dog, canned & dry, NOT expired) and gently used and new pet supplies, such as leashes, collars, bowls, harnesses, beds, etc. Open bags okay, please tape up. We do not accept donations of prescription medications. Over the counter medications are okay.', + words: 5, + }, + { + id: 3, + description: + 'Reach out to Pet Food Providers in your area and set up a pick up or drop off schedule for your donations.', + words: 6, + }, + { + id: 4, + description: + 'Spread the word on social media and let your customers know you have become a Feeding Pets of the Homeless® Donation Site.', + words: 3, + }, +]; + +const splitDescription = (description: string, n: number) => { + const wordsArray = description.split(' '); + const firstWords = wordsArray.slice(0, n).join(' '); + const remainingWords = wordsArray.slice(n).join(' '); + return { + firstWords, + remainingWords, + }; +}; + +export const Information = () => { + return ( + + + + Let's Get You Set Up! + + + {steps.map(({ id, description, words }) => { + const { firstWords, remainingWords } = splitDescription(description, words); + + return ( + + + + {id} + + + + + {firstWords}  + + {remainingWords} + + + + ); + })} + + + ); +}; diff --git a/src/components/SetUp/FirstForm.jsx b/src/components/BusinessSetup/SetupSignup.tsx similarity index 92% rename from src/components/SetUp/FirstForm.jsx rename to src/components/BusinessSetup/SetupSignup.tsx index fb99616..aa7da4e 100644 --- a/src/components/SetUp/FirstForm.jsx +++ b/src/components/BusinessSetup/SetupSignup.tsx @@ -15,28 +15,34 @@ import { useToast, } from '@chakra-ui/react'; import { useNavigate } from 'react-router-dom'; +// @ts-expect-error file exists import LOGO from './fph_logo.png'; import { useAuth } from '../../contexts/AuthContext'; import { useBackend } from '../../contexts/BackendContext'; -import PropTypes from 'prop-types'; // Import PropTypes -const FirstForm = ({ admin, nextStep }) => { - const { isAdmin, currentUser } = useAuth(); +interface SetUpFirstFormProps { + admin: boolean; + nextStep: () => void; +} + +export const SetupSignup = ({ admin, nextStep }: SetUpFirstFormProps) => { + const { isAdmin, currentUser, signup, logout } = useAuth(); const { backend } = useBackend(); + const navigate = useNavigate(); + const toast = useToast(); + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [id, setId] = useState(''); - const { signup, logout } = useAuth(); const [loading, setLoading] = useState(false); - const navigate = useNavigate(); - const toast = useToast(); useEffect(() => { const setUp = async () => { const urlParams = new URLSearchParams(window.location.search); const idFromUrl = urlParams.get('id'); - setId(idFromUrl); + + setId(idFromUrl ?? ''); await logout(); }; @@ -78,7 +84,7 @@ const FirstForm = ({ admin, nextStep }) => { } } else { if (id) { - signup(email, password); + await signup(email, password); } else { toast({ title: 'Business Not Found', @@ -173,10 +179,3 @@ const FirstForm = ({ admin, nextStep }) => { ); }; - -FirstForm.propTypes = { - admin: PropTypes.bool.isRequired, - nextStep: PropTypes.bool.isRequired, -}; - -export default FirstForm; diff --git a/src/components/BusinessSetup/Welcome.tsx b/src/components/BusinessSetup/Welcome.tsx new file mode 100644 index 0000000..61715da --- /dev/null +++ b/src/components/BusinessSetup/Welcome.tsx @@ -0,0 +1,22 @@ +import { VStack, Text, Image, Center } from '@chakra-ui/react'; +//@ts-expect-error file exists +import LOGO from './fph_logo.png'; + +export const Welcome = () => { + return ( +
+ + + + + Welcome! + + + Thank you for your interest and signing up to become a Feeding Pets of the Homeless + Donation Site! Let’s walk through your Donation Site Kit to get you started! + + + +
+ ); +}; diff --git a/src/components/SetUp/fph_logo.png b/src/components/BusinessSetup/fph_logo.png similarity index 100% rename from src/components/SetUp/fph_logo.png rename to src/components/BusinessSetup/fph_logo.png diff --git a/src/components/CatchAll.tsx b/src/components/CatchAll.tsx index 9646541..0c1cf02 100644 --- a/src/components/CatchAll.tsx +++ b/src/components/CatchAll.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { useAuth } from '../contexts/AuthContext'; import { useNavigate } from 'react-router-dom'; diff --git a/src/components/SetUp/BusinessSetupPageMaster.jsx b/src/components/SetUp/BusinessSetupPageMaster.jsx deleted file mode 100644 index 037a602..0000000 --- a/src/components/SetUp/BusinessSetupPageMaster.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState } from 'react'; -import FirstForm from './FirstForm'; -import SecondForm from './SecondForm'; -import ThirdForm from './ThirdForm'; -import FourthForm from './FourthForm'; -import { useNavigate } from 'react-router-dom'; -import FifthForm from './FifthForm'; -import { Box, Flex } from '@chakra-ui/react'; -import { Button } from '@chakra-ui/react'; -import propTypes from 'prop-types'; - -const BusinessSetupPageMaster = ({ isAdmin }) => { - const navigate = useNavigate(); - const steps = [ - { title: 'First', description: 'Contact Info' }, - { title: 'Second', description: 'Date & Time' }, - { title: 'Third', description: 'Select Rooms' }, - { title: 'Fourth', description: 'Fourth' }, - ]; - - const [activeStep, setActiveStep] = useState(0); - const [step, setStep] = useState(0); - - const nextStep = () => { - setActiveStep(prevState => prevState + 1); - setStep(prevState => prevState + 1); - }; - - const stepsComponents = [ - , - , - , - , - , - ]; - - return ( -
- {step === 0 ? ( - stepsComponents[step] - ) : ( - <> - {stepsComponents[step]} - - - {steps.map((step, index) => ( - - - {index < steps.length - 1 && ( - - )} - - ))} - - - - - )} -
- ); -}; - -BusinessSetupPageMaster.propTypes = { - isAdmin: propTypes.bool.isRequired, -}; - -export default BusinessSetupPageMaster; diff --git a/src/components/SetUp/SecondForm.jsx b/src/components/SetUp/SecondForm.jsx deleted file mode 100644 index a41a12b..0000000 --- a/src/components/SetUp/SecondForm.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import { VStack, Text, Image, Flex } from '@chakra-ui/react'; -import LOGO from './fph_logo.png'; -import PropTypes from 'prop-types'; - -const SecondForm = () => { - return ( - <> - - - - - Welcome! - - - Thank you for your interest and signing up to become a Feeding Pets of the Homeless - Donation Site! Let’s walk through your Donation Site Kit to get you started! - - - - - ); -}; - -SecondForm.propTypes = { - isAdmin: PropTypes.bool.isRequired, - nextStep: PropTypes.bool.isRequired, -}; - -export default SecondForm; diff --git a/src/components/SetUp/ThirdForm.jsx b/src/components/SetUp/ThirdForm.jsx deleted file mode 100644 index 68cb0f3..0000000 --- a/src/components/SetUp/ThirdForm.jsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Box, VStack, Text, HStack, Flex } from '@chakra-ui/react'; -import PropTypes from 'prop-types'; - -const ThirdForm = () => { - return ( - - - - Let's Get You Set Up! - - - - - 1 - - - - Visit our website - {' '} - to familiarize yourself with our mission, donation sites and pet food providers in - your area, and other useful info. - - - - - - - 2 - - - - Set up a donation bin{' '} - {' '} - in an accessible area around your business. We accept donations of pet food (cat, dog, - canned & dry, NOT expired) and gently used and new pet supplies, such as leashes, - collars, bowls, harnesses, beds, etc. Open bags okay, please tape up. We do not accept - donations of prescription medications. Over the counter medications are okay. - - - - - - - 3 - - - - Reach out to Pet Food Providers - {' '} - in your area and set up a pick up or drop off schedule for your donations. - - - - - - - 4 - - - - Spread the word{' '} - {' '} - on social media and let your customers know you have become a Feeding Pets of the - Homeless® Donation Site. - - - - - - ); -}; - -ThirdForm.propTypes = { - isAdmin: PropTypes.bool.isRequired, - nextStep: PropTypes.bool.isRequired, -}; - -export default ThirdForm; diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx index c208a92..5c5830a 100644 --- a/src/contexts/AuthContext.jsx +++ b/src/contexts/AuthContext.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useContext } from 'react'; +import { createContext, useState, useEffect, useContext } from 'react'; import { auth } from '../utils/firebaseAuthUtil'; import { useBackend } from './BackendContext'; import { @@ -8,7 +8,7 @@ import { sendPasswordResetEmail, } from 'firebase/auth'; -const AuthContext = React.createContext(); +const AuthContext = createContext(); export function useAuth() { return useContext(AuthContext); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a224293 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "jsx": "react-jsx" + } +} From b0ff89a1630cf4b608752e97206d5ddf7b9d91d1 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Wed, 31 Jul 2024 21:34:34 -0700 Subject: [PATCH 21/65] feat: revamp setup --- public/{fph-logo.png => fph_logo.png} | Bin .../fph_logo.png => public/fph_logo_no_bg.png | Bin src/App.css | 0 src/App.module.css | 1 - .../BusinessSetup/BusinessSetupPage.tsx | 39 +++---- src/components/BusinessSetup/FifthForm.jsx | 95 ------------------ src/components/BusinessSetup/FourthForm.jsx | 78 -------------- .../{Information.tsx => InformationFirst.tsx} | 13 ++- .../BusinessSetup/InformationSecond.tsx | 78 ++++++++++++++ src/components/BusinessSetup/SetupSignup.tsx | 2 +- src/components/BusinessSetup/Tips.tsx | 70 +++++++++++++ src/components/BusinessSetup/Welcome.tsx | 2 +- 12 files changed, 182 insertions(+), 196 deletions(-) rename public/{fph-logo.png => fph_logo.png} (100%) rename src/components/BusinessSetup/fph_logo.png => public/fph_logo_no_bg.png (100%) delete mode 100644 src/App.css delete mode 100644 src/components/BusinessSetup/FifthForm.jsx delete mode 100644 src/components/BusinessSetup/FourthForm.jsx rename src/components/BusinessSetup/{Information.tsx => InformationFirst.tsx} (90%) create mode 100644 src/components/BusinessSetup/InformationSecond.tsx create mode 100644 src/components/BusinessSetup/Tips.tsx diff --git a/public/fph-logo.png b/public/fph_logo.png similarity index 100% rename from public/fph-logo.png rename to public/fph_logo.png diff --git a/src/components/BusinessSetup/fph_logo.png b/public/fph_logo_no_bg.png similarity index 100% rename from src/components/BusinessSetup/fph_logo.png rename to public/fph_logo_no_bg.png diff --git a/src/App.css b/src/App.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/App.module.css b/src/App.module.css index 32bbeee..3fa3733 100644 --- a/src/App.module.css +++ b/src/App.module.css @@ -6,6 +6,5 @@ .mainContent { flex: 1; /* Takes up the remaining space */ - overflow: auto; /* Adds scroll to the main content if needed */ background-color: var(--fph-gray-50, #f9f8f7); } diff --git a/src/components/BusinessSetup/BusinessSetupPage.tsx b/src/components/BusinessSetup/BusinessSetupPage.tsx index 6615687..f5fc8ff 100644 --- a/src/components/BusinessSetup/BusinessSetupPage.tsx +++ b/src/components/BusinessSetup/BusinessSetupPage.tsx @@ -1,12 +1,11 @@ import { useState } from 'react'; import { SetupSignup } from './SetupSignup'; import { Welcome } from './Welcome'; -import { Information } from './Information'; -import FourthForm from './FourthForm'; +import { InformationFirst } from './InformationFirst'; +import { InformationSecond } from './InformationSecond'; import { useNavigate } from 'react-router-dom'; -import FifthForm from './FifthForm'; -import { Box, Flex } from '@chakra-ui/react'; -import { Button } from '@chakra-ui/react'; +import { Tips } from './Tips'; +import { Box, Flex, Button } from '@chakra-ui/react'; export const BusinessSetupPage = ({ isAdmin }) => { const navigate = useNavigate(); @@ -18,26 +17,31 @@ export const BusinessSetupPage = ({ isAdmin }) => { ]; const [activeStep, setActiveStep] = useState(0); - const [step, setStep] = useState(0); const nextStep = () => { setActiveStep(prevState => prevState + 1); - setStep(prevState => prevState + 1); }; const stepsComponents = [ , , - , - , - , + , + , + , ]; return ( - {stepsComponents[step]} - {step !== 0 ? ( - + {stepsComponents[activeStep]} + {activeStep !== 0 ? ( + {steps.map((_, index) => ( { bg={index === activeStep - 1 ? '#D9D9D9' : '#F5F5F5'} color={index === activeStep - 1 ? 'white' : 'black'} borderRadius="md" + borderWidth={1.5} + borderColor={'teal'} key={index} /> ))} - + ) : null} ); diff --git a/src/components/BusinessSetup/FifthForm.jsx b/src/components/BusinessSetup/FifthForm.jsx deleted file mode 100644 index 3a93ce2..0000000 --- a/src/components/BusinessSetup/FifthForm.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import { Box, VStack, Text, HStack, Flex } from '@chakra-ui/react'; -import 'boxicons'; -import PropTypes from 'prop-types'; - -const FifthForm = () => { - return ( - - - - Tips on how to increase your donations! - - - - - - Host events{' '} - {' '} - and ask that a donation of pet food be the admittance ticket. - - - - - - - Offer a service with a donation - {' '} - as the fee for your services. Example: grooming, pet sitting, boarding, etc. - - - - - - - Fill a truck or police vehicle - {' '} - (popular choice). - - - - - - - Print our newsletter or flyer - {' '} - and post it to boards in your community with your business name and address. - - - - - - We are always{' '} - - {' '} - open to other suggestions. - - - - - - ); -}; - -FifthForm.propTypes = { - isAdmin: PropTypes.bool.isRequired, - nextStep: PropTypes.bool.isRequired, -}; - -export default FifthForm; diff --git a/src/components/BusinessSetup/FourthForm.jsx b/src/components/BusinessSetup/FourthForm.jsx deleted file mode 100644 index e7129ac..0000000 --- a/src/components/BusinessSetup/FourthForm.jsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Box, VStack, Text, HStack, Flex } from '@chakra-ui/react'; -import PropTypes from 'prop-types'; - -const FourthForm = () => { - return ( - - - - Let's Get You Set Up! - - - - - 5 - - - - Report Monthly: - {' '} - Each month log your donations. You will be entered in our quarterly drawing for a $50 - Starbucks gift card. - - - - - - - 6 - - - - Add this verbiage to your email signature: - {' '} - “We are a Feeding Pets of the Homeless® Donation Site! Please bring by dog or cat - food, treats or supplies to help!” Feeding Pets of the Homeless® is the first and - only national animal organization focused completely on feeding and providing - emergency veterinary care to pets that belong to homeless people. - - - - - - - 7 - - - - Do not accept cash! - {' '} - Check should be addressed to Feeding Pets of the Homeless® and sent to headquarters. - - - - - - - 8 - - - - If a donor requests a donation receipt, - {' '} - please use form included in the startup kit and return to us. We will then send an - official receipt to them from headquarters. - - - - - - ); -}; - -FourthForm.propTypes = { - isAdmin: PropTypes.bool.isRequired, - nextStep: PropTypes.bool.isRequired, -}; - -export default FourthForm; diff --git a/src/components/BusinessSetup/Information.tsx b/src/components/BusinessSetup/InformationFirst.tsx similarity index 90% rename from src/components/BusinessSetup/Information.tsx rename to src/components/BusinessSetup/InformationFirst.tsx index 6beb35a..537fb3b 100644 --- a/src/components/BusinessSetup/Information.tsx +++ b/src/components/BusinessSetup/InformationFirst.tsx @@ -37,10 +37,17 @@ const splitDescription = (description: string, n: number) => { }; }; -export const Information = () => { +export const InformationFirst = () => { return ( - - + + Let's Get You Set Up! diff --git a/src/components/BusinessSetup/InformationSecond.tsx b/src/components/BusinessSetup/InformationSecond.tsx new file mode 100644 index 0000000..2eea9e4 --- /dev/null +++ b/src/components/BusinessSetup/InformationSecond.tsx @@ -0,0 +1,78 @@ +import { Box, VStack, Text, Flex } from '@chakra-ui/react'; + +const steps = [ + { + id: 5, + description: + 'Report Monthly: Each month log your donations. You will be entered in our quarterly drawing for a $50 Starbucks gift card.', + words: 2, + }, + { + id: 6, + description: + 'Add this verbiage to your email signature: “We are a Feeding Pets of the Homeless® Donation Site! Please bring by dog or cat food, treats or supplies to help!” Feeding Pets of the Homeless® is the first and only national animal organization focused completely on feeding and providing emergency veterinary care to pets that belong to homeless people.', + words: 7, + }, + { + id: 7, + description: + 'Do not accept cash! Check should be addressed to Feeding Pets of the Homeless® and sent to headquarters.', + words: 3, + }, + { + id: 8, + description: + 'If a donor requests a donation receipt, please use form included in the startup kit and return to us. We will then send an official receipt to them from headquarters.', + words: 6, + }, +]; + +const splitDescription = (description: string, n: number) => { + const wordsArray = description.split(' '); + const firstWords = wordsArray.slice(0, n).join(' '); + const remainingWords = wordsArray.slice(n).join(' '); + return { + firstWords, + remainingWords, + }; +}; + +export const InformationSecond = () => { + return ( + + + + Let's Get You Set Up! + + + {steps.map(({ id, description, words }) => { + const { firstWords, remainingWords } = splitDescription(description, words); + + return ( + + + + {id} + + + + + {firstWords}  + + {remainingWords} + + + + ); + })} + + + ); +}; diff --git a/src/components/BusinessSetup/SetupSignup.tsx b/src/components/BusinessSetup/SetupSignup.tsx index aa7da4e..e1e2564 100644 --- a/src/components/BusinessSetup/SetupSignup.tsx +++ b/src/components/BusinessSetup/SetupSignup.tsx @@ -16,7 +16,7 @@ import { } from '@chakra-ui/react'; import { useNavigate } from 'react-router-dom'; // @ts-expect-error file exists -import LOGO from './fph_logo.png'; +import LOGO from '../../../public/fph_logo_no_bg.png'; import { useAuth } from '../../contexts/AuthContext'; import { useBackend } from '../../contexts/BackendContext'; diff --git a/src/components/BusinessSetup/Tips.tsx b/src/components/BusinessSetup/Tips.tsx new file mode 100644 index 0000000..dff90d5 --- /dev/null +++ b/src/components/BusinessSetup/Tips.tsx @@ -0,0 +1,70 @@ +import { Box, VStack, Text, HStack, Flex, Icon } from '@chakra-ui/react'; +import { BiBone, BiCalendar, BiCar, BiChat, BiCopy } from 'react-icons/bi'; + +const tips = [ + { + icon: BiCalendar, + text: ( + <> + Host events and ask that a donation of pet food be the admittance ticket. + + ), + }, + { + icon: BiBone, + text: ( + <> + Offer a service with a donation as the fee for your services. Example: grooming, pet + sitting, boarding, etc. + + ), + }, + { + icon: BiCar, + text: ( + <> + Fill a truck or police vehicle (popular choice). + + ), + }, + { + icon: BiCopy, + text: ( + <> + Print our newsletter or flyer and post it to boards in your community with your + business name and address. + + ), + }, + { + icon: BiChat, + text: ( + <> + We are always open to other suggestions. + + ), + }, +]; + +export const Tips = () => { + return ( + + + + Tips on how to increase your donations! + + + + {tips.map((tip, index) => ( + + + + {tip.text} + + + ))} + + + + ); +}; diff --git a/src/components/BusinessSetup/Welcome.tsx b/src/components/BusinessSetup/Welcome.tsx index 61715da..0e9bf0a 100644 --- a/src/components/BusinessSetup/Welcome.tsx +++ b/src/components/BusinessSetup/Welcome.tsx @@ -1,6 +1,6 @@ import { VStack, Text, Image, Center } from '@chakra-ui/react'; //@ts-expect-error file exists -import LOGO from './fph_logo.png'; +import LOGO from '../../../public/fph_logo_no_bg.png'; export const Welcome = () => { return ( From d01a4643c9f258df3d3f756a9561051884582434 Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Wed, 31 Jul 2024 21:37:27 -0700 Subject: [PATCH 22/65] fix: misc cleanup --- src/components/BusinessDashboard/BusinessTotals.tsx | 6 +++--- src/components/Sidebar/Sidebar.tsx | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/BusinessDashboard/BusinessTotals.tsx b/src/components/BusinessDashboard/BusinessTotals.tsx index f666a8e..5c3115e 100644 --- a/src/components/BusinessDashboard/BusinessTotals.tsx +++ b/src/components/BusinessDashboard/BusinessTotals.tsx @@ -43,18 +43,18 @@ const calculateTimeSince = (reminderData: Notification[]) => { }; export const BusinessTotals = ({ donationData, reminderData, priceData }: BusinessTotalsProps) => { - const displayIcon = useBreakpointValue({ base: 'block', lg: 'none', xl: 'block' }); + const displayIcon = useBreakpointValue({ base: 'none', lg: 'none', xl: 'block' }); const stats = [ { icon: BiMoney, value: `$${calculateTotalWorth(donationData, priceData)}`, - label: 'Worth of Donation Supplies', + label: 'In Donation Supplies', }, { icon: BiDonateHeart, value: calculateTotalPounds(donationData), - label: 'Pounds of Food Donated', + label: 'Pounds Donated', }, { icon: TimeIcon, diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 6e09a6e..0d82d10 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -14,7 +14,7 @@ import { } from '@chakra-ui/react'; // @ts-expect-error this image path does exist -import fphLogo from '/fph-logo.png'; +import fphLogo from '/fph_logo.png'; import { useAuth } from '../../contexts/AuthContext'; import { useBackend } from '../../contexts/BackendContext'; import { useEffect, useState } from 'react'; @@ -29,7 +29,6 @@ import { BiCog, } from 'react-icons/bi'; import { ContactInformationModal } from './ContactInformationModal'; -import React from 'react'; function Sidebar() { const { backend } = useBackend(); From 3ef6e308e5d81c26ee2abb6ef01ef50c737fc53b Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Thu, 1 Aug 2024 14:02:10 -0700 Subject: [PATCH 23/65] style: set minwidth to width on sidebar --- src/components/Sidebar/Sidebar.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 0d82d10..2f3d887 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -87,7 +87,15 @@ function Sidebar() { return ( <> - + From cf7a4fe7c67e8adaccc4c6d661bf5d10bbbc318f Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Thu, 1 Aug 2024 14:27:10 -0700 Subject: [PATCH 24/65] chore: add react-app-env --- react-app-env.d.ts | 6 ++++++ src/components/BusinessDashboard/BusinessDashboard.tsx | 1 - .../ViewDonationHistory/ViewDonationHistory.jsx | 2 -- src/components/BusinessSetup/SetupSignup.tsx | 1 - src/components/BusinessSetup/Welcome.tsx | 1 - src/components/ContactUsForm/ContactUs.css | 0 src/components/Sidebar/Sidebar.tsx | 1 - 7 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 react-app-env.d.ts delete mode 100644 src/components/ContactUsForm/ContactUs.css diff --git a/react-app-env.d.ts b/react-app-env.d.ts new file mode 100644 index 0000000..8e77789 --- /dev/null +++ b/react-app-env.d.ts @@ -0,0 +1,6 @@ +declare module '*.png'; +declare module '*.svg'; +declare module '*.jpeg'; +declare module '*.jpg'; +declare module '*.module.css'; +declare module '*.css'; diff --git a/src/components/BusinessDashboard/BusinessDashboard.tsx b/src/components/BusinessDashboard/BusinessDashboard.tsx index d7c4be1..49ee55f 100644 --- a/src/components/BusinessDashboard/BusinessDashboard.tsx +++ b/src/components/BusinessDashboard/BusinessDashboard.tsx @@ -29,7 +29,6 @@ import { BiPackage } from 'react-icons/bi'; import { useNavigate } from 'react-router-dom'; import { ArrowForwardIcon, WarningIcon, EmailIcon, CheckCircleIcon } from '@chakra-ui/icons'; import ViewDonationHistory from '../BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory'; -// @ts-expect-error CSS file exists import classes from './BusinessDashboard.module.css'; import type { Notification } from '../../types/notification'; import { BusinessTotals } from './BusinessTotals'; diff --git a/src/components/BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory.jsx b/src/components/BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory.jsx index 98fe1a0..6699b6b 100644 --- a/src/components/BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory.jsx +++ b/src/components/BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory.jsx @@ -1,5 +1,3 @@ -/* eslint-disable react/prop-types */ - import { useEffect, useState } from 'react'; import { IconButton, diff --git a/src/components/BusinessSetup/SetupSignup.tsx b/src/components/BusinessSetup/SetupSignup.tsx index e1e2564..6ccfdab 100644 --- a/src/components/BusinessSetup/SetupSignup.tsx +++ b/src/components/BusinessSetup/SetupSignup.tsx @@ -15,7 +15,6 @@ import { useToast, } from '@chakra-ui/react'; import { useNavigate } from 'react-router-dom'; -// @ts-expect-error file exists import LOGO from '../../../public/fph_logo_no_bg.png'; import { useAuth } from '../../contexts/AuthContext'; import { useBackend } from '../../contexts/BackendContext'; diff --git a/src/components/BusinessSetup/Welcome.tsx b/src/components/BusinessSetup/Welcome.tsx index 0e9bf0a..9626e48 100644 --- a/src/components/BusinessSetup/Welcome.tsx +++ b/src/components/BusinessSetup/Welcome.tsx @@ -1,5 +1,4 @@ import { VStack, Text, Image, Center } from '@chakra-ui/react'; -//@ts-expect-error file exists import LOGO from '../../../public/fph_logo_no_bg.png'; export const Welcome = () => { diff --git a/src/components/ContactUsForm/ContactUs.css b/src/components/ContactUsForm/ContactUs.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx index 2f3d887..eba7a8c 100644 --- a/src/components/Sidebar/Sidebar.tsx +++ b/src/components/Sidebar/Sidebar.tsx @@ -13,7 +13,6 @@ import { IconButton, } from '@chakra-ui/react'; -// @ts-expect-error this image path does exist import fphLogo from '/fph_logo.png'; import { useAuth } from '../../contexts/AuthContext'; import { useBackend } from '../../contexts/BackendContext'; From 9cff54d6707a25f4523bb2f94091cd97dd524acb Mon Sep 17 00:00:00 2001 From: Kevin Wu Date: Thu, 1 Aug 2024 23:58:11 -0700 Subject: [PATCH 25/65] feat: refactor --- src/App.jsx | 7 +- src/components/ContactUsForm/ContactUs.jsx | 337 ------------------ .../SupplyRequests/ConfirmationDialog.tsx | 49 +++ .../SupplyRequests/SupplyRequests.tsx | 126 +++++++ .../SupplyRequests/SupplyRequestsPage.tsx | 183 ++++++++++ .../Supply_0.png | Bin .../Supply_1.png | Bin .../Supply_2.png | Bin .../Supply_3.png | Bin .../Supply_4.png | Bin .../Supply_5.png | Bin .../Supply_6.png | Bin .../Supply_7.png | Bin .../fph_logo.png | Bin 14 files changed, 363 insertions(+), 339 deletions(-) delete mode 100644 src/components/ContactUsForm/ContactUs.jsx create mode 100644 src/components/SupplyRequests/ConfirmationDialog.tsx create mode 100644 src/components/SupplyRequests/SupplyRequests.tsx create mode 100644 src/components/SupplyRequests/SupplyRequestsPage.tsx rename src/components/{ContactUsForm => SupplyRequests}/Supply_0.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_1.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_2.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_3.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_4.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_5.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_6.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/Supply_7.png (100%) rename src/components/{ContactUsForm => SupplyRequests}/fph_logo.png (100%) diff --git a/src/App.jsx b/src/App.jsx index 234b8fb..21d69a8 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,7 +8,7 @@ import AdminDashboard from './components/AdminDashboard/AdminDashboard.jsx'; import EditContactInformation from './components/EditContactInformation.jsx'; import BusinessDonationHistory from './components/BusinessDonationHistory/BusinessDonationHistory.jsx'; import ViewDonationHistory from './components/BusinessDonationHistory/ViewDonationHistory/ViewDonationHistory.jsx'; -import ContactUs from './components/ContactUsForm/ContactUs.jsx'; +import { SupplyRequestsPage } from './components/SupplyRequests/SupplyRequestsPage'; import DonationTrackingTable from './components/DonationTrackingTable/DonationTrackingTable.jsx'; import styles from './App.module.css'; import DonationItemsTable from './components/DonationItemsTable/DonationItemsTable.jsx'; @@ -51,7 +51,10 @@ const App = () => { } /> } /> } /> - } /> + } + /> } /> { - const FPH_ID = 0; - const { backend } = useBackend(); - const { currentUser } = useAuth(); - const navigate = useNavigate(); - const [text, setText] = useState(''); - const [checkedItems, setCheckedItems] = useState([0, 0, 0, 0, 0, 0, 0, 0]); - const [showConfirmation, setShowConfirmation] = useState(false); - const [isFormValid, setIsFormValid] = useState(false); - const checkedThingies = [ - 'Get Pet Food Decal', - 'Decal', - 'Homeless Card', - 'Business Card', - 'Donation Site Decal', - 'Donation Site Bin Decals', - 'Donation Envelopes', - 'Homeless Card 2', - ]; - const supplyDescriptions = [ - 'Stickers to share with your Pet Food Provider.', - 'Stickers that can be handed out and can be placed on water bottles, cars and more.', - 'Cards to share with your Pet Food Provider who may have more direct contact with the homeless in need of emergency veterinary services.', - 'Feeding Pets of the Homeless’ contact information cards.', - 'Decals that indicate that you are a donation site.', - 'Decorate your donation bins with these decals so donators know where to put their items. ', - 'Envelopes to provide potential donators to make a direct donation.', - 'Cards to share with your Pet Food Provider who may have more direct contact with the homeless who are in need of emergency veterinary services.', - ]; - const suppliesImages = [ - Supply_0, - Supply_1, - Supply_2, - Supply_3, - Supply_4, - Supply_5, - Supply_6, - Supply_7, - ]; - - const [businessId, setBusinessId] = useState(null); - const [businessName, setBusinessName] = useState(''); - - useEffect(() => { - // Define an async function inside useEffect since useEffect cannot be asynchronous directly - const fetchBusinessId = async () => { - try { - // Fetch business ID from backend - const businessIdResponse = await backend.get(`/businessUser/${currentUser.uid}`); - const fetchedBusinessId = businessIdResponse.data[0].id; - const businessNameResponse = await backend.get(`/business/${fetchedBusinessId}`); - const fetchedBusinessName = businessNameResponse.data[0].name; - // Set the fetched business ID in state - setBusinessId(fetchedBusinessId); - setBusinessName(fetchedBusinessName); - } catch (error) { - // Handle errors - console.error('Error fetching business ID:', error); - } - }; - - fetchBusinessId(); - }, []); - - const ConfirmationDialog = ({ isOpen, onClose }) => ( - - - - - - fph Logo - - - - - - Supply Request Sent! - - Your supplies will be shipped in 5-7 business days. - - - - - - - - - - ); - - ConfirmationDialog.propTypes = { - isOpen: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - }; - - const SubmitForm = async () => { - const msg = { - 'Get Pet Food Decal': checkedItems[0], - Decal: checkedItems[1], - 'Homeless Card': checkedItems[2], - 'Business Card': checkedItems[3], - 'Donation Site Decal': checkedItems[4], - 'Donation Site Bin Decals': checkedItems[5], - 'Donation Envelopes': checkedItems[6], - 'Homeless Card 2': checkedItems[7], - Notes: text, - }; - const message = JSON.stringify(msg); - const updatedFormData = { - businessId: FPH_ID, - message: message, - type: 'Supply Request', - senderId: businessId, - businessName: businessName, - donationId: null, - }; - const confirmationNotification = { - businessId: businessId, - message: message, - type: 'Supply Request', - senderId: FPH_ID, - businessName: null, - donationId: null, - } - await backend.post('/notification/', updatedFormData); - await backend.post('/notification/', confirmationNotification); - await backend.put(`/business/${businessId}`, { supply_request_status: "Pending" }); - setShowConfirmation(true); - }; - - const handleNumInputChange = (e, index) => { - checkedItems[index] = Number(e); - - // Update the state with the new array - setCheckedItems(checkedItems); - isFormFilled(); - setIsFormValid(checkedItems.some(value => value !== 0) || text.length > 0); - }; - - const isFormFilled = () => { - // Check if all form fields are filled - return checkedItems.some(value => value !== 0) || text.length > 0; - }; - - const textInputHandler = e => { - const newText = e.target.value; - setText(newText); - isFormFilled(); - setIsFormValid(checkedItems.some(value => value !== 0) || text.length > 0); - }; - - const buttonStyles = { - ...(isFormFilled() - ? { colorScheme: 'teal' } // Styles for 'teal' state - : { - backgroundColor: 'blackAlpha.300', - color: 'white', - }), - }; - - const handleCancelButtonClick = () => { - navigate('/BusinessDashboard'); - }; - - const showSupplyRequests = (startingIndex, endingIndex) => { - return ( - - {checkedThingies.slice(startingIndex, endingIndex).map((item, index) => ( - - - {`Supply - - - - handleNumInputChange(startingIndex + index, valueString)} - > - - - - - - - {item} - - - {supplyDescriptions[startingIndex + index]} - - - - ))} - - ); - }; - - return ( - <> - - - - - - Supply Request - - - - - - - Select Supplies Needed - - - - - - - {showSupplyRequests(0, 4)} - - - {showSupplyRequests(4, 8)} - - - - -