diff --git a/components/Judging/TeamMembersSelector.js b/components/Judging/TeamMembersSelector.js new file mode 100644 index 00000000..9fc8a964 --- /dev/null +++ b/components/Judging/TeamMembersSelector.js @@ -0,0 +1,313 @@ +// teamMembers should be array of object: +// +// [ +// { +// discord, +// email, +// id, +// name +// }, +// ... +// ] +import { useEffect, useState } from 'react'; +import styled from 'styled-components'; +import TextField from '../TextField'; +import { getAllApplicants } from '../../utility/firebase'; + +const InputArea = styled.div` + background: white; + padding: 0.5rem; + border: 1px solid #eeeeee; + display: flex; + gap: 0.5rem; + position: relative; + min-height: calc(1rem + calc(1rem * 1.2)); +`; + +const Member = styled.div` + background: #2d2937; + color: white; + padding: 0.5rem 0.7rem; + border-radius: 5px; + display: inline-flex; + align-items: center; + gap: 0.5rem; +`; + +const AddText = styled.span` + margin: 0.5rem 0.3rem; + cursor: pointer; + font-weight: 600; + position: absolute; + top: 0.5rem; + right: 10px; +`; + +const AddPanelOverlay = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + background: rgba(0, 0, 0, 0.5); + ${(p) => (p.isAdding ? 'display: flex;' : 'display: none;')} + align-items: flex-end; + justify-content: center; +`; + +const AddPanel = styled.div` + transition: all 0.5s linear; + background: #f8f8f8; + border-style: none solid solid solid; + border-width: 1px; + border-color: #eeeeee; + box-sizing: border-box; + border-radius: 8px 8px 0 0; +`; + +const AddTitle = styled.h4` + line-height: 150%; + margin: 0; +`; + +const HackerOptions = styled.div` + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; + + ::-webkit-scrollbar { + width: 5px; + } + ::-webkit-scrollbar-track { + background: #f8f8f8; + } + ::-webkit-scrollbar-thumb { + background: #eeeeee; + border-radius: 9001px; + } +`; + +const EmptyOptions = styled.div` + font-size: 0.9rem; + color: #777777; +`; + +const SelectableHacker = styled.div` + line-height: 150%; + box-sizing: border-box; + padding: 0.5rem 1.5rem; + cursor: pointer; + width: 100%; + + :hover { + background: #eeeeee; + } +`; + +const SmallText = styled.span` + font-size: 0.8rem; + color: #777777; +`; + +const TeamMembersSelector = (props) => { + const { value, updateValue, fnNoOverflow } = props; + // onChange => ('teamMembers', e) + + // const copyValues = () => { + // const duplicate = []; + // value.forEach((item) => { + // duplicate.push(item); + // }); + // return duplicate; + // }; + + const [members, setMembers] = useState(value); + const [renderMembers, setRenderMembers] = useState([]); + const [trigger, setTrigger] = useState(false); + const [isAdding, setIsAdding] = useState(false); + const [hackerSearch, setHackerSearch] = useState(''); + const [applicants, setApplicants] = useState([]); + + const handleAddHackerToTeam = (hacker) => { + setHackerSearch(''); // doesn't reset... maybe something to do with onChangeCustomValue + const currentMembers = members; + members.push(hacker); + setMembers(currentMembers); + setTrigger(!trigger); + setIsAdding(false); + }; + + const handleDeleteMember = (index) => { + const currentMembers = members; + currentMembers.splice(index, 1); + setMembers(currentMembers); + setTrigger(!trigger); + }; + + const DeleteIcon = ({ fn }) => { + return ( +
+ + + + +
+ ); + }; + + useEffect(() => { + const renderArray = []; + + if (members) { + members.forEach((item, index) => { + renderArray.push( + + {item.name} + handleDeleteMember(index)} /> + + ); + }); + } + + setRenderMembers(renderArray); + + // Save + updateValue('teamMembers', members, true); + }, [trigger]); + + useEffect(() => { + if (isAdding) { + document + .getElementById('project-modal') + ?.scrollTo(0, document.getElementById('project-modal')?.scrollHeight); + fnNoOverflow(true); + } else { + fnNoOverflow(false); + } + }, [isAdding]); + + useEffect(() => { + getAllApplicants(setApplicants); + }, []); + + return ( + <> +
+ + {renderMembers} + setIsAdding(true)}>Add Member + +
+ + + {/* Head */} +
+
+ {/* Title + Cancel */} + Add Team Member + setIsAdding(false)} /> +
+ {/* Search */} + setHackerSearch(e.target.value)} + /> +
+ + {/* Searched Options */} + + {/* OPTIMIZATION TODO: debounce search + pull from firebase where applicant is accepted (if possible) */} + {hackerSearch ? ( + applicants?.map((applicant, index) => { + const name = `${applicant.basicInfo.firstName} ${applicant.basicInfo.lastName}`; + const { email } = applicant.basicInfo; + if ( + (name.toLowerCase().includes(hackerSearch.toLowerCase()) || + hackerSearch === '*') && + applicant.status.applicationStatus === 'accepted' && + applicant.status.attending && + (members + ? !( + members?.filter((e) => e.id === applicant._id).length > + 0 + ) + : true) + ) { + return ( + + handleAddHackerToTeam({ + discord: '-#-', + email, + id: applicant._id, + name, + }) + } + > + {name} {email} + + ); + } + return <>; + }) + ) : ( + Begin typing to search + )} + +
+
+ + ); +}; + +export default TeamMembersSelector; diff --git a/components/modal.js b/components/modal.js index 3bbd6117..ad013f7a 100644 --- a/components/modal.js +++ b/components/modal.js @@ -138,7 +138,7 @@ const ModalBackground = styled.div` width: 740px; max-width: 100%; max-height: 70%; - overflow-y: auto; + ${(p) => (p.noOverflow ? 'overflow-y: hidden;' : 'overflow-y: auto;')} `; const ButtonContainer = styled.div` @@ -239,6 +239,7 @@ export default function Modal({ lastModified, children, modalTitle, + noOverflow, }) { if (!isOpen) { return null; @@ -277,7 +278,11 @@ export default function Modal({ return ( <> {isOpen && } - + {(modalTitle && getTitle(modalTitle)) || getTitle(modalAction)} {children} diff --git a/components/projectsCard.js b/components/projectsCard.js index 2b64ee83..e0e575ca 100644 --- a/components/projectsCard.js +++ b/components/projectsCard.js @@ -79,7 +79,11 @@ export default ({ Link - {project.teamMembers?.toString()} + + {project.teamMembers.map((item, index) => ( + {item.name} + ))} +