From 5ea6a5c9d95f9da575980fab9c7630639ebca657 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Sun, 3 Mar 2024 16:15:33 -0500 Subject: [PATCH 01/10] advisor form field --- common-types/index.d.ts | 1 + .../src/components/Admin/AddUser/AddUser.tsx | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/common-types/index.d.ts b/common-types/index.d.ts index 10803e40c..f3a517743 100644 --- a/common-types/index.d.ts +++ b/common-types/index.d.ts @@ -31,6 +31,7 @@ interface IdolMember { readonly subteams: readonly string[]; readonly formerSubteams?: readonly string[] | null; readonly role: Role; + readonly isAdvisor: boolean; readonly roleDescription: RoleDescription; } diff --git a/frontend/src/components/Admin/AddUser/AddUser.tsx b/frontend/src/components/Admin/AddUser/AddUser.tsx index 57388ef55..ce39a0f89 100644 --- a/frontend/src/components/Admin/AddUser/AddUser.tsx +++ b/frontend/src/components/Admin/AddUser/AddUser.tsx @@ -94,6 +94,7 @@ export default function AddUser(): JSX.Element { pronouns: '', email: '', role: '' as Role, + isAdvisor: false, graduation: '', major: '', doubleMajor: '', @@ -194,6 +195,7 @@ export default function AddUser(): JSX.Element { ? m.formerSubteams.split(', ') : currMember.formerSubteams, role: m.role || currMember.role, + isAdvisor: m.isAdvisor || currMember.isAdvisor, roleDescription: getRoleDescriptionFromRoleID(m.role) } as IdolMember; MembersAPI.updateMember(updatedMember); @@ -216,6 +218,7 @@ export default function AddUser(): JSX.Element { subteams: m.subteam ? [m.subteam] : [], formerSubteams: m.formerSubteams ? m.formerSubteams.split(', ') : [], role: m.role || ('' as Role), + isAdvisor: m.isAdvisor || false, roleDescription: getRoleDescriptionFromRoleID(m.role) } as IdolMember; MembersAPI.setMember(updatedMember); @@ -445,6 +448,26 @@ export default function AddUser(): JSX.Element { }} value={state.currentSelectedMember.role || ''} /> + , + data: HTMLInputElement + ) => { + setCurrentlySelectedMember((currentSelectedMember) => ({ + ...currentSelectedMember, + isAdvisor: data.value === 'true' + })); + }} + value={state.currentSelectedMember.isAdvisor} + /> Date: Sun, 3 Mar 2024 17:40:27 -0500 Subject: [PATCH 02/10] advisor adding --- backend/scripts/add-advisor.ts | 13 +++++++++++++ frontend/src/components/Admin/AddUser/AddUser.tsx | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 backend/scripts/add-advisor.ts diff --git a/backend/scripts/add-advisor.ts b/backend/scripts/add-advisor.ts new file mode 100644 index 000000000..04a41e434 --- /dev/null +++ b/backend/scripts/add-advisor.ts @@ -0,0 +1,13 @@ +import { memberCollection, approvedMemberCollection } from '../src/firebase'; + +const addAdvisor = async (collection) => { + const docs = await collection.listDocuments(); + docs.forEach(async (doc) => { + await doc.update({ + isAdvisor: false + }); + }); +}; + +addAdvisor(memberCollection); +addAdvisor(approvedMemberCollection); diff --git a/frontend/src/components/Admin/AddUser/AddUser.tsx b/frontend/src/components/Admin/AddUser/AddUser.tsx index ce39a0f89..b9c8fe82c 100644 --- a/frontend/src/components/Admin/AddUser/AddUser.tsx +++ b/frontend/src/components/Admin/AddUser/AddUser.tsx @@ -452,8 +452,8 @@ export default function AddUser(): JSX.Element { control={Select} label="Advisor" options={[ - { key: 'true', text: 'Yes', value: true }, - { key: 'false', text: 'No', value: false } + { key: true, text: 'Yes', value: true }, + { key: false, text: 'No', value: false } ]} placeholder="Advisor" disabled={state.currentSelectedMember.role === 'tpm'} // no tpm advisor @@ -463,10 +463,10 @@ export default function AddUser(): JSX.Element { ) => { setCurrentlySelectedMember((currentSelectedMember) => ({ ...currentSelectedMember, - isAdvisor: data.value === 'true' + isAdvisor: Boolean(data.value) })); }} - value={state.currentSelectedMember.isAdvisor} + value={state.currentSelectedMember.isAdvisor || false} /> From 3a1a40a29b21a89d085f9dbe8d6d0648fa5ba707 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Sun, 3 Mar 2024 18:37:05 -0500 Subject: [PATCH 03/10] update csv sample --- frontend/public/sample_csv.zip | Bin 1036 -> 2274 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/public/sample_csv.zip b/frontend/public/sample_csv.zip index c156b2ee2359ff3a3ccd29e27787574593485c57..56cb8438cdcc79985fac4a18a09557a2cd315e06 100644 GIT binary patch literal 2274 zcmWIWW@Zs#0DMcAiA`In}Lz# zDN*CAQo|4Org&;_z;We_k@H8{^!nL_14ii z;d%A6ug|&06aN{L0u`K2h)z=zQ++ly<=Ijr1CzAxa&PTr9{kI@+qvUkeAn(9oyFo- z8duB>HW+L%Gxjw#H83{XR;J^%EscR8z?+?8kHMd&4q(WDJcRJ-EI!L{SeX4`Vqg#f zdKKY6{UBFI7hhMs+!U}+O%w!ELxH~3XTu1**dXt`I|e*=&uem|OlEq^x{J4}DdxbI zqZ?TR7CC!8_VJg$B)52SUQXBk?{X)ni&t!xjxiRFapm4N?KRJ>nQw}IvNDUFv0=Ez z(#&<4VQF;pM1_l{;a-7kCCYo97aC1^w=+Fxo$&J~kr@V@--8w=DSl?ya@25h)uL0+ zB2N6VeI73?q4xcQv=r|=Nf(}+iRWHUJg{k^pWOY2ONCB+brASAqx%;xv+%Nq>O8r+ zAFcPDJh^$zlH-Rz#qd|ZdB${>>0;5zo>S%DqvfR&D|u9xx&5m9kz*0*`E>W{mAw8- z{cVbb_*j-kFFSjPCu{e|iz|Z{#ymDGjk*19-jdZ9Petqx?B@7xc^jXs;SR*8(6ykpMU5^NNMi9%L%7*3ui8U@Y&AnXzACb z&$IT`{CIJxCVu^U?!e8pIrVmX@2#$VdSmvFyH8JkuWwiJ)2n{mnsw$pzqa0|5tjuw6S)IBf$ zgkm&D5~qdTY-4v$f#UfbvN9?bT)*!Jw=lQQD0{)76E{0pK>wz@1)GVaa5sB<{8L`# zo!hs}tvbH%%EVtMRvvh>|Kx^t<>iXs4LTO9EM|YBy4>s7*T<8mJve!bbtX^Sxs<0a zj19dJ0bKVM9b=Gx(Qgo7yg}RAX?KqN1J^s6>Reyrj4ORN95rD$(JH~cpR;_ z8@DE%V>4Ifoe_T2X5GKW+P*iM$13mXDP?&uU!8mXaKygUXQAPGSEGBPgs!MIZ(X(O z6+0-2)wtS5Y6HU^6!zF-1r%Qzz*vD7peRX9y0eTM%4mg4+B+sE# z4p>rJfH%ZoSdD{RK!a)=7&y@A31q^9kCgfc*$Plek6a*tN_rSr(rChn;&$vgA4|E9 zZUb^I1r;nXu%uC)8Oak!!9+qagKQZn<0B_NP{xOWhQ=>I%aH6Kkje-|4#+BGe}Pgn g3^X)$vEuR<0b8-8?Er69Hjv3|K$rt8v=hKQ0GKxFb^rhX delta 613 zcmaDP*uzmD;LXe;!oa}5!BEO?8P4yy{k%RS1H)Y)7GjWLC{D~R$VrXQP0dY8Eh^Sa zE-niV;bdSwt0vBI80IV#{xjmgUF6IeVkf7qou0mb&jjcCZ30Jxj@(gqVrS>Rl51ryScNb z-nlx}FzxEPe3xh^4&~5yZk4 xiL8)FM2o-xZ-lv+4nml_M69tIXg-qrfaU?+H+c_-1RoOv6T@<#wf@W?%K_F}&$R#m From 52588ab1ca6807f227fc916baf472b661c77b671 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Sun, 3 Mar 2024 19:04:16 -0500 Subject: [PATCH 04/10] add isAdvisor to userProfile --- frontend/src/components/Forms/UserProfile/UserProfile.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/Forms/UserProfile/UserProfile.tsx b/frontend/src/components/Forms/UserProfile/UserProfile.tsx index b40979c31..1dedc0bb7 100644 --- a/frontend/src/components/Forms/UserProfile/UserProfile.tsx +++ b/frontend/src/components/Forms/UserProfile/UserProfile.tsx @@ -56,6 +56,7 @@ const UserProfile: React.FC = () => { lastName, pronouns, role: userRole, + isAdvisor: userInfoBeforeEdit?.isAdvisor ?? false, roleDescription: getRoleDescriptionFromRoleID(userRole), graduation, major, From 5046729189512d7b4ce7bd71a85ed5b983997b75 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Mon, 4 Mar 2024 00:42:56 -0500 Subject: [PATCH 05/10] improve error handling for tpm advisors --- common-types/index.d.ts | 3 ++- frontend/src/components/Admin/AddUser/AddUser.tsx | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/common-types/index.d.ts b/common-types/index.d.ts index f3a517743..e1418fe40 100644 --- a/common-types/index.d.ts +++ b/common-types/index.d.ts @@ -10,7 +10,8 @@ type RoleDescription = | 'Product Manager' | 'Developer' | 'Designer' - | 'Business Analyst'; + | 'Business Analyst' + | 'Advisor'; /** The data type used by IDOL to represent a DTI member. */ interface IdolMember { diff --git a/frontend/src/components/Admin/AddUser/AddUser.tsx b/frontend/src/components/Admin/AddUser/AddUser.tsx index b9c8fe82c..b7a00076a 100644 --- a/frontend/src/components/Admin/AddUser/AddUser.tsx +++ b/frontend/src/components/Admin/AddUser/AddUser.tsx @@ -150,6 +150,16 @@ export default function AddUser(): JSX.Element { return; } + // Check that no tpms are also advisors + if (member.role === 'tpm' && member.isAdvisor) { + Emitters.generalError.emit({ + headerMsg: 'Invalid Role!', + contentMsg: + 'TPM advisor is not a valid role. Please select a different role if this member is returning as an advisor.' + }); + return; + } + MembersAPI.setMember({ ...member, netid: getNetIDFromEmail(member.email), @@ -247,7 +257,7 @@ export default function AddUser(): JSX.Element { const json = await csvtojson().fromString(csv); const errors = json .map((m) => { - const [email, role, subteam] = [m.email, m.role, m.subteam]; + const [email, role, subteam, isAdvisor] = [m.email, m.role, m.subteam, m.isAdvisor]; const formerSubteams: string[] = m.formerSubteams ? m.formerSubteams.split(', ') : []; const err = []; if (!email) { @@ -268,6 +278,9 @@ export default function AddUser(): JSX.Element { if (formerSubteams.includes(subteam)) { err.push('subteam cannot be in former subteams'); } + if (isAdvisor && role === 'tpm') { + err.push('TPM cannot be an advisor'); + } return err.length > 0 ? `Row ${json.indexOf(m) + 1}: ${err.join(', ')}` : ''; }) .filter((err) => err.length > 0); From 36ebfe61bd025843d675361a481207944b2c1d86 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Mon, 4 Mar 2024 00:43:31 -0500 Subject: [PATCH 06/10] wording --- frontend/src/components/Admin/AddUser/AddUser.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Admin/AddUser/AddUser.tsx b/frontend/src/components/Admin/AddUser/AddUser.tsx index b7a00076a..813f63490 100644 --- a/frontend/src/components/Admin/AddUser/AddUser.tsx +++ b/frontend/src/components/Admin/AddUser/AddUser.tsx @@ -279,7 +279,7 @@ export default function AddUser(): JSX.Element { err.push('subteam cannot be in former subteams'); } if (isAdvisor && role === 'tpm') { - err.push('TPM cannot be an advisor'); + err.push('tpm advisor is not a valid role'); } return err.length > 0 ? `Row ${json.indexOf(m) + 1}: ${err.join(', ')}` : ''; }) From 342629b22fe2f9fb1906e258c154f5eb2c63ae99 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Mon, 4 Mar 2024 00:49:27 -0500 Subject: [PATCH 07/10] remove advisor role description --- common-types/index.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common-types/index.d.ts b/common-types/index.d.ts index e1418fe40..f3a517743 100644 --- a/common-types/index.d.ts +++ b/common-types/index.d.ts @@ -10,8 +10,7 @@ type RoleDescription = | 'Product Manager' | 'Developer' | 'Designer' - | 'Business Analyst' - | 'Advisor'; + | 'Business Analyst'; /** The data type used by IDOL to represent a DTI member. */ interface IdolMember { From a69fc6e7ada88b029efa1c3e5310bce1f8d951d8 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Mon, 4 Mar 2024 01:32:31 -0500 Subject: [PATCH 08/10] add note about tpm advisor --- frontend/src/components/Admin/AddUser/AddUser.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Admin/AddUser/AddUser.tsx b/frontend/src/components/Admin/AddUser/AddUser.tsx index 813f63490..e00ef2790 100644 --- a/frontend/src/components/Admin/AddUser/AddUser.tsx +++ b/frontend/src/components/Admin/AddUser/AddUser.tsx @@ -155,7 +155,7 @@ export default function AddUser(): JSX.Element { Emitters.generalError.emit({ headerMsg: 'Invalid Role!', contentMsg: - 'TPM advisor is not a valid role. Please select a different role if this member is returning as an advisor.' + 'TPM advisor is not a valid role. Please select a different role if this member is returning as an advisor. Note that previous TPMs may become dev advisors.' }); return; } From cf3cf6004928a8b873997347e6f04c5cacc99fb6 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Mon, 4 Mar 2024 02:16:24 -0500 Subject: [PATCH 09/10] dev advisor dp --- .../Forms/DevPortfolioForm/DevPortfolioForm.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx b/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx index 9c470c332..96c0dbe73 100644 --- a/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx +++ b/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx @@ -12,7 +12,7 @@ const DevPortfolioForm: React.FC = () => { // When the user is logged in, `useSelf` always return non-null data. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const userInfo = useSelf()!; - const isTpm = userInfo.role === 'tpm'; + const [isTpm, isAdvisor] = [userInfo.role === 'tpm', userInfo.isAdvisor]; const [devPortfolio, setDevPortfolio] = useState(undefined); const [devPortfolios, setDevPortfolios] = useState([]); @@ -86,7 +86,7 @@ const DevPortfolioForm: React.FC = () => { ? devPortfolio.lateDeadline : devPortfolio?.deadline; - if (!isTpm && otherEmpty && (openedEmpty || reviewedEmpty)) { + if (!(isTpm || isAdvisor) && otherEmpty && (openedEmpty || reviewedEmpty)) { Emitters.generalError.emit({ headerMsg: 'No opened or reviewed PR url submitted', contentMsg: 'Please paste a link to a opened and reviewed PR!' @@ -110,7 +110,7 @@ const DevPortfolioForm: React.FC = () => { headerMsg: 'Documentation Empty', contentMsg: 'Please write something for the documentation section of the assignment.' }); - } else if (!isTpm && !otherEmpty && textEmpty) { + } else if (!(isTpm || isAdvisor) && !otherEmpty && textEmpty) { Emitters.generalError.emit({ headerMsg: 'Explanation Empty', contentMsg: 'Please write an explanation for your other PR submission.' @@ -225,6 +225,7 @@ const DevPortfolioForm: React.FC = () => { label="Opened Pull Request Github Link:" openOther={openOther} isTpm={isTpm} + isAdvisor={isAdvisor} /> { label="Reviewed Pull Request Github Link:" openOther={openOther} isTpm={isTpm} + isAdvisor={isAdvisor} /> - {isTpm ? ( + {isTpm || isAdvisor ? ( <> ) : ( >; @@ -310,6 +313,7 @@ const PRInputs = ({ placeholder: string; openOther: boolean; isTpm: boolean; + isAdvisor: boolean; }) => { const keyDownHandler = (event: React.KeyboardEvent) => { if (event.code === 'Enter') { @@ -319,7 +323,7 @@ const PRInputs = ({ return (
{prs.map((pr, index) => (
From fbf6ac1d3e8cd7fa06501e442f28f4c860558b17 Mon Sep 17 00:00:00 2001 From: Oscar Wang Date: Mon, 4 Mar 2024 04:13:42 -0500 Subject: [PATCH 10/10] split istpm and isadvisor --- .../src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx b/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx index 96c0dbe73..a89755670 100644 --- a/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx +++ b/frontend/src/components/Forms/DevPortfolioForm/DevPortfolioForm.tsx @@ -12,7 +12,8 @@ const DevPortfolioForm: React.FC = () => { // When the user is logged in, `useSelf` always return non-null data. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const userInfo = useSelf()!; - const [isTpm, isAdvisor] = [userInfo.role === 'tpm', userInfo.isAdvisor]; + const isTpm = userInfo.role === 'tpm'; + const { isAdvisor } = userInfo; const [devPortfolio, setDevPortfolio] = useState(undefined); const [devPortfolios, setDevPortfolios] = useState([]);