diff --git a/index.html b/index.html index 0c589ec..9d579a2 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,12 @@ + + + Vite + React diff --git a/package.json b/package.json index 2927530..7317ac2 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "react-dropzone": "^14.2.3", "react-hook-form": "7.37.0", "react-icons": "^5.0.1", - "react-pdf": "^7.7.0", "react-router-dom": "^6.21.1", "vite-plugin-top-level-await": "^1.4.1", "yup": "^1.3.3" diff --git a/src/App.jsx b/src/App.jsx index 74d5e75..8dbec58 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -26,6 +26,7 @@ import SelectEvent from './pages/SelectEvent'; import DummyVolunteerQR from './pages/DummyVolunteerQR'; import DummyAdminQR from './pages/DummyAdminQR'; import Navbar from './components/Navbar/Navbar'; +import AdminPage from './pages/AdminPage'; const Layout = () => { return ( @@ -64,26 +65,19 @@ const App = () => { } /> } /> - {/* SPRINT 4 */} - - {/* Jessie and Brendan */} + } /> - {/* Rayan and Emmy */} } /> - {/* Phillip and Katy */} } /> - {/* Nate and Farhnaz */} } /> } /> - {/* Matthew and Bobby */} } /> - {/* If your Sprint 3 task requires you to create a new component, you can use this route to test the look of your component */} } /> - {/* SPRINT 5 */} - {/* Kevin and Jasmine */} } /> } /> + + } /> diff --git a/src/components/AdminModals/AddUserModal.jsx b/src/components/AdminModals/AddUserModal.jsx new file mode 100644 index 0000000..753241c --- /dev/null +++ b/src/components/AdminModals/AddUserModal.jsx @@ -0,0 +1,180 @@ +import { + Flex, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + Spinner, + FormControl, + FormLabel, + Input, + Button, + Select, + useToast, + ModalCloseButton, +} from '@chakra-ui/react'; +import Dropzone from '../Dropzone.tsx'; +import { useState} from 'react'; +import { postProfile } from '../../utils/profileUtils.js'; +import PropTypes from 'prop-types'; + + +export default function AddUserModal({ isOpen, onClose, setAdminData }) { + const [isLoading, setIsLoading] = useState(false); + const [userData, setUserData] = useState({ + first_name: '', + last_name: '', + role: 'admin', + email: '', + }); + const toast = useToast(); + + const handleSubmit = async () => { + try { + await postProfile(userData); + + toast({ + title: 'New administrator added.', + description: 'New administrator added.', + position: 'bottom-right', + status: 'success', + duration: 9000, + isClosable: true, + }); + + setAdminData(prev => [...prev, userData]); + setUserData({ + first_name: '', + last_name: '', + role: 'admin', + email: '', + }); + + onClose(); + } catch (err) { + console.log('Error:', err); + toast({ + title: 'Error creating new administrator.', + description: 'There was an error creating new administator. Please try again.', + status: 'error', + position: 'bottom-right', + duration: 9000, + isClosable: true, + }); + } + }; + + const emailRegex = /\S+@\S+\.\S+/; + const isSubmittable = + userData.first_name === '' || userData.last_name === '' || !emailRegex.test(userData.email); + + console.log('userData:', userData); + + return ( + + + + + + + Add New User + + + + + + + + {isLoading ? ( + + ) : ( + + )} + + + + + + First Name + + setUserData({ ...userData, first_name: e.target.value })} + /> + + + + Last Name + + setUserData({ ...userData, last_name: e.target.value })} + /> + + + + Email + + setUserData({ ...userData, email: e.target.value })} + /> + + Add Role + + + + Set Account Type + + + + + + + + + + + + ); +} + +AddUserModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + setAdminData: PropTypes.func.isRequired, +}; diff --git a/src/components/AdminModals/EditUserModal.jsx b/src/components/AdminModals/EditUserModal.jsx new file mode 100644 index 0000000..b9ddd4f --- /dev/null +++ b/src/components/AdminModals/EditUserModal.jsx @@ -0,0 +1,226 @@ +import { + Flex, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + Spinner, + FormControl, + FormLabel, + Input, + Button, + Select, + useToast, + ModalCloseButton, +} from '@chakra-ui/react'; +import Dropzone from '../Dropzone.tsx'; +import { useState } from 'react'; +import { updateProfile, deleteProfile } from '../../utils/profileUtils.js'; +import PropTypes from 'prop-types'; + +export default function EditUserModal({ isOpen, onClose, selectedAdmin, setAdminData, adminData }) { + const [isLoading, setIsLoading] = useState(false); + const [currentUserData, setCurrentUserData] = useState({ + first_name: selectedAdmin.first_name, + last_name: selectedAdmin.last_name, + role: 'admin', + email: selectedAdmin.email, + imageUrl: selectedAdmin.image_url, + }); + const toast = useToast(); + + const editAdminDataArray = () => { + const arrayIndex = adminData.findIndex(admin => admin.id === selectedAdmin.id); + + if (arrayIndex !== -1) { + const newAdminDataArray = [ + ...adminData.slice(0, arrayIndex), + currentUserData, + ...adminData.slice(arrayIndex + 1), + ]; + + return newAdminDataArray; + } + }; + + const handleSubmit = async () => { + try { + await updateProfile(selectedAdmin.id, currentUserData); + toast({ + title: 'Changes saved.', + description: 'Changes saved.', + position: 'bottom-right', + status: 'success', + duration: 9000, + isClosable: true, + }); + setAdminData(editAdminDataArray()); + onClose(); + } catch (err) { + console.log(err); + toast({ + title: 'Error updating administrator.', + description: 'There was an error updating administator. Please try again.', + status: 'error', + position: 'bottom-right', + duration: 9000, + isClosable: true, + }); + } + }; + + const handleDelete = async () => { + try { + await deleteProfile(selectedAdmin.id); + toast({ + title: 'Administrator removed.', + description: 'Administrator removed.', + position: 'bottom-right', + status: 'success', + duration: 9000, + isClosable: true, + }); + setAdminData(prev => prev.filter(admin => admin.id !== selectedAdmin.id)); + onClose(); + } catch (err) { + console.log(err); + toast({ + title: 'Error removing administrator.', + description: 'There was an error removing administator. Please try again.', + status: 'error', + position: 'bottom-right', + duration: 9000, + isClosable: true, + }); + } + }; + + console.log('currentUserData:', currentUserData); + + return ( + + + + + + + Edit Admin + + + + + + + + {isLoading ? ( + + ) : ( + + )} + + + + + + First Name + + + setCurrentUserData({ ...currentUserData, first_name: e.target.value }) + } + /> + + + + Last Name + + + setCurrentUserData({ ...currentUserData, last_name: e.target.value }) + } + /> + + + + Email + + setCurrentUserData({ ...currentUserData, email: e.target.value })} + /> + + Add Role + + + + Set Account Type + + + + + + + + + + + + + ); +} + +EditUserModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + selectedAdmin: PropTypes.object.isRequired, + setAdminData: PropTypes.func.isRequired, + adminData: PropTypes.array.isRequired, +}; diff --git a/src/components/Dropzone.tsx b/src/components/Dropzone.tsx index 65a7a2c..8898eb7 100644 --- a/src/components/Dropzone.tsx +++ b/src/components/Dropzone.tsx @@ -9,7 +9,7 @@ import { useEffect } from 'react'; // import {FileUploadIcon} from "./Icons/EventsModalIcons.jsx" -const Dropzone = ({ setEventData, eventData, setIsLoading }) => { +const Dropzone = ({ setData, data, setIsLoading }) => { const { getRootProps, getInputProps, isDragAccept, isDragReject, acceptedFiles } = useDropzone({ noKeyboard: true, accept: { 'image/jpeg': ['.jpeg', '.jpg'], 'image/png': ['.png'] }, @@ -38,7 +38,7 @@ const Dropzone = ({ setEventData, eventData, setIsLoading }) => { }); const imageUrl = uploadUrl.split('?')[0]; - setEventData({ ...eventData, imageUrl: imageUrl }); + setData({ ...data, imageUrl: imageUrl }); setIsLoading(false); return imageUrl; @@ -63,15 +63,23 @@ const Dropzone = ({ setEventData, eventData, setIsLoading }) => { className={dropzoneBox} {...getRootProps()} backgroundColor="#D9D9D9" - minW={"37vw"} + minW={'37vw'} height={'30vh'} justify={'center'} align={'center'} borderRadius={'30px'} - backgroundImage={eventData?.imageUrl} + backgroundImage={data?.imageUrl} > - } height={'62px'} width={'62px'} borderRadius={100} size={'lg'} aria-label={''} opacity={0.9}/> + } + height={'62px'} + width={'62px'} + borderRadius={100} + size={'lg'} + aria-label={''} + opacity={0.9} + /> {/*