From f4a68cb2366d5bc6c4ca2a652335b37a44aa66e9 Mon Sep 17 00:00:00 2001 From: Richard Feng Date: Thu, 5 Feb 2026 15:44:44 -0500 Subject: [PATCH 01/13] #3872 Created the guest page sidebar --- src/frontend/src/app/AppAuthenticated.tsx | 8 +- src/frontend/src/layouts/Sidebar/Sidebar.tsx | 125 +++++++++++++++--- .../src/pages/HomePage/IntroGuestHomePage.tsx | 36 ++--- 3 files changed, 127 insertions(+), 42 deletions(-) diff --git a/src/frontend/src/app/AppAuthenticated.tsx b/src/frontend/src/app/AppAuthenticated.tsx index 9b58381a39..aed4b7fc9e 100644 --- a/src/frontend/src/app/AppAuthenticated.tsx +++ b/src/frontend/src/app/AppAuthenticated.tsx @@ -70,7 +70,7 @@ const AppAuthenticated: React.FC = ({ userId, userRole }) return userSettingsData.slackId || isGuest(userRole) ? ( - {!onGuestHomePage && ( + { <> { @@ -108,12 +108,12 @@ const AppAuthenticated: React.FC = ({ userId, userRole }) setMoveContent={setMoveContent} /> - )} + } - + diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index 6fd7b11f4d..b5e497e548 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -4,11 +4,20 @@ */ import { routes } from '../../utils/routes'; +import { Route } from 'react-router-dom'; +import TeamSpecificPage from '../../pages/TeamsPage/TeamSpecificPage'; import { LinkItem } from '../../utils/types'; import styles from '../../stylesheets/layouts/sidebar/sidebar.module.css'; import { Typography, Box, IconButton, Divider } from '@mui/material'; import HomeIcon from '@mui/icons-material/Home'; import AlignHorizontalLeftIcon from '@mui/icons-material/AlignHorizontalLeft'; +import RateReviewIcon from '@mui/icons-material/RateReview'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +import ConstructionIcon from '@mui/icons-material/Construction'; +import BoltIcon from '@mui/icons-material/Bolt'; +import CodeIcon from '@mui/icons-material/Code'; +import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; +import BusinessCenterIcon from '@mui/icons-material/BusinessCenter'; import FolderIcon from '@mui/icons-material/Folder'; import SyncAltIcon from '@mui/icons-material/SyncAlt'; import GroupIcon from '@mui/icons-material/Group'; @@ -19,15 +28,16 @@ import NavPageLink from './NavPageLink'; import NERDrawer from '../../components/NERDrawer'; import NavUserMenu from '../PageTitle/NavUserMenu'; import DrawerHeader from '../../components/DrawerHeader'; -import { Cached, ChevronLeft, ChevronRight } from '@mui/icons-material'; +import { Cached, ChevronLeft, ChevronRight, NotListedLocation } from '@mui/icons-material'; import { useHomePageContext } from '../../app/HomePageContext'; import { isGuest } from 'shared'; +import { getAllTeams } from '../../apis/teams.api'; import BarChartIcon from '@mui/icons-material/BarChart'; import { useCurrentUser } from '../../hooks/users.hooks'; import QueryStatsIcon from '@mui/icons-material/QueryStats'; import CurrencyExchangeIcon from '@mui/icons-material/CurrencyExchange'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; interface SidebarProps { drawerOpen: boolean; @@ -40,29 +50,70 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid const [openSubmenu, setOpenSubmenu] = useState(null); const { onPNMHomePage, onOnboardingHomePage } = useHomePageContext(); const user = useCurrentUser(); + const { onGuestHomePage } = useHomePageContext(); + const [allTeams, setAllTeams] = useState([]); + getAllTeams().then((response) => { + setAllTeams( + response.data.map((team) => ({ + name: team.teamName, + icon: undefined, + route: routes.TEAMS + '/' + team.teamId + })) + ); + }); + + // Now allTeams will update when data arrives const memberLinkItems: LinkItem[] = [ { name: 'Home', icon: , - route: routes.HOME + route: onGuestHomePage ? routes.HOME_GUEST : routes.HOME }, - { + !onGuestHomePage && { name: 'Gantt', icon: , route: routes.GANTT }, - { - name: 'Projects', - icon: , - route: routes.PROJECTS - }, - { + !onGuestHomePage + ? { + name: 'Projects', + icon: , + route: routes.PROJECTS + } + : { + name: 'Project Management', + icon: , + route: routes.PROJECTS, + subItems: [ + { + name: 'Gantt', + icon: , + route: routes.GANTT + }, + { + name: 'Projects', + icon: , + route: routes.PROJECTS + }, + { + name: 'Change Requests', + icon: , + route: routes.CHANGE_REQUESTS + }, + { + name: 'Design Review', + icon: , + route: routes.DESIGN_REVIEW_BY_ID + } + ] + }, + !onGuestHomePage && { name: 'Change Requests', icon: , route: routes.CHANGE_REQUESTS }, - { + !onGuestHomePage && { name: 'Finance', icon: , route: routes.FINANCE, @@ -84,29 +135,63 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid } ] }, - { - name: 'Teams', - icon: , - route: routes.TEAMS - }, - { + !onGuestHomePage + ? { + name: 'Teams', + icon: , + route: routes.TEAMS + } + : { + name: 'Divisions', + icon: , + route: routes.TEAMS, + subItems: allTeams + // subItems: [ + // { + // name: 'Mechanical', + // icon: , + // route: routes.FINANCE_DASHBOARD + // }, + // { + // name: 'Electrical', + // icon: , + // route: routes.REIMBURSEMENT_REQUESTS + // }, + // { + // name: 'Software', + // icon: , + // route: routes.CALENDAR + // }, + // { + // name: 'Business', + // icon: , + // route: routes.FINANCE_DASHBOARD + // } + // ] + }, + !onGuestHomePage && { name: 'Calendar', icon: , route: routes.CALENDAR }, - { + !onGuestHomePage && { name: 'Retrospective', icon: , route: routes.RETROSPECTIVE }, + onGuestHomePage && { + name: 'Sponsors', + icon: , + route: routes.RETROSPECTIVE + }, { name: 'Info', icon: , route: routes.INFO } - ]; + ].filter(Boolean) as LinkItem[]; - if (!isGuest(user.role)) { + if (!isGuest(user.role) && !onGuestHomePage) { memberLinkItems.splice(6, 0, { name: 'Statistics', icon: , diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index 08e2bd763b..57de994ec2 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -22,18 +22,18 @@ const IntroGuestHomePage = () => { } = useCurrentOrganization(); const { setCurrentHomePage } = useHomePageContext(); - const { - data: applyInterestImageUrl, - isLoading: applyImageLoading, - isError: applyImageIsError, - error: applyImageError - } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { - data: exploreGuestImageUrl, - isLoading: exploreImageLoading, - isError: exploreImageIsError, - error: exploreImageError - } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); + // const { + // data: applyInterestImageUrl, + // isLoading: applyImageLoading, + // isError: applyImageIsError, + // error: applyImageError + // } = useGetImageUrl(organization?.applyInterestImageId ?? null); + // const { + // data: exploreGuestImageUrl, + // isLoading: exploreImageLoading, + // isError: exploreImageIsError, + // error: exploreImageError + // } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); useEffect(() => { setCurrentHomePage('guest'); @@ -42,11 +42,11 @@ const IntroGuestHomePage = () => { if (organizationIsError) { return ; } - if (applyImageIsError) return ; - if (exploreImageIsError) return ; + // if (applyImageIsError) return ; + // if (exploreImageIsError) return ; - if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; - if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; + // if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; + // if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; return ( @@ -66,13 +66,13 @@ const IntroGuestHomePage = () => { history.push(routes.HOME_PNM)} /> history.push(routes.HOME_MEMBER)} /> From 234ea7b9216b6e97a345deb44cc3e76242efd0f8 Mon Sep 17 00:00:00 2001 From: Richard Feng Date: Thu, 5 Feb 2026 16:03:44 -0500 Subject: [PATCH 02/13] #3872 removed hard coded teams --- src/frontend/src/layouts/Sidebar/Sidebar.tsx | 25 +------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index b5e497e548..74bcdf1b83 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -63,7 +63,6 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid ); }); - // Now allTeams will update when data arrives const memberLinkItems: LinkItem[] = [ { name: 'Home', @@ -104,7 +103,7 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid { name: 'Design Review', icon: , - route: routes.DESIGN_REVIEW_BY_ID + route: routes.CALENDAR } ] }, @@ -146,28 +145,6 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid icon: , route: routes.TEAMS, subItems: allTeams - // subItems: [ - // { - // name: 'Mechanical', - // icon: , - // route: routes.FINANCE_DASHBOARD - // }, - // { - // name: 'Electrical', - // icon: , - // route: routes.REIMBURSEMENT_REQUESTS - // }, - // { - // name: 'Software', - // icon: , - // route: routes.CALENDAR - // }, - // { - // name: 'Business', - // icon: , - // route: routes.FINANCE_DASHBOARD - // } - // ] }, !onGuestHomePage && { name: 'Calendar', From c63523209893287f06450afc672b537fc6928ec7 Mon Sep 17 00:00:00 2001 From: Richard Feng Date: Sun, 8 Feb 2026 16:35:30 -0500 Subject: [PATCH 03/13] 3872 fixed subteams tab --- src/frontend/src/layouts/Sidebar/Sidebar.tsx | 52 ++++++++++++------- .../src/pages/HomePage/IntroGuestHomePage.tsx | 9 +--- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index 74bcdf1b83..a0436ac327 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -4,8 +4,6 @@ */ import { routes } from '../../utils/routes'; -import { Route } from 'react-router-dom'; -import TeamSpecificPage from '../../pages/TeamsPage/TeamSpecificPage'; import { LinkItem } from '../../utils/types'; import styles from '../../stylesheets/layouts/sidebar/sidebar.module.css'; import { Typography, Box, IconButton, Divider } from '@mui/material'; @@ -13,11 +11,7 @@ import HomeIcon from '@mui/icons-material/Home'; import AlignHorizontalLeftIcon from '@mui/icons-material/AlignHorizontalLeft'; import RateReviewIcon from '@mui/icons-material/RateReview'; import DashboardIcon from '@mui/icons-material/Dashboard'; -import ConstructionIcon from '@mui/icons-material/Construction'; -import BoltIcon from '@mui/icons-material/Bolt'; -import CodeIcon from '@mui/icons-material/Code'; import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; -import BusinessCenterIcon from '@mui/icons-material/BusinessCenter'; import FolderIcon from '@mui/icons-material/Folder'; import SyncAltIcon from '@mui/icons-material/SyncAlt'; import GroupIcon from '@mui/icons-material/Group'; @@ -28,10 +22,14 @@ import NavPageLink from './NavPageLink'; import NERDrawer from '../../components/NERDrawer'; import NavUserMenu from '../PageTitle/NavUserMenu'; import DrawerHeader from '../../components/DrawerHeader'; -import { Cached, ChevronLeft, ChevronRight, NotListedLocation } from '@mui/icons-material'; +import { Cached, ChevronLeft, ChevronRight } from '@mui/icons-material'; import { useHomePageContext } from '../../app/HomePageContext'; -import { isGuest } from 'shared'; -import { getAllTeams } from '../../apis/teams.api'; +import { isGuest, TeamType } from 'shared'; +import { getAllTeamTypes } from '../../apis/team-types.api'; +import ConstructionIcon from '@mui/icons-material/Construction'; +import ElectricBoltIcon from '@mui/icons-material/ElectricBolt'; +import CodeIcon from '@mui/icons-material/Code'; +import WorkIcon from '@mui/icons-material/Work'; import BarChartIcon from '@mui/icons-material/BarChart'; import { useCurrentUser } from '../../hooks/users.hooks'; import QueryStatsIcon from '@mui/icons-material/QueryStats'; @@ -53,15 +51,33 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid const { onGuestHomePage } = useHomePageContext(); const [allTeams, setAllTeams] = useState([]); - getAllTeams().then((response) => { - setAllTeams( - response.data.map((team) => ({ - name: team.teamName, - icon: undefined, - route: routes.TEAMS + '/' + team.teamId - })) - ); - }); + const getIcon = (iconName: string) => { + const icons: { [key: string]: React.ComponentType } = { + ConstructionIcon, + CodeIcon, + ElectricBoltIcon, + WorkIcon + }; + const Icon = icons[iconName]; + return Icon ? : undefined; + }; + + useEffect(() => { + getAllTeamTypes() + .then((response) => { + console.log('All teams from API:', response.data); + setAllTeams( + response.data.map((team: TeamType) => ({ + name: team.name, + icon: getIcon(team.iconName), + route: routes.TEAMS + '/' + team.teamTypeId + })) + ); + }) + .catch((error) => { + console.log("Teams couldn't load " + error); + }); + }, []); const memberLinkItems: LinkItem[] = [ { diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index 57de994ec2..818b46fef3 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -7,19 +7,12 @@ import { useCurrentUser } from '../../hooks/users.hooks'; import { useEffect } from 'react'; import { useHomePageContext } from '../../app/HomePageContext'; import { useCurrentOrganization } from '../../hooks/organizations.hooks'; -import LoadingIndicator from '../../components/LoadingIndicator'; import ErrorPage from '../ErrorPage'; -import { useGetImageUrl } from '../../hooks/onboarding.hook'; const IntroGuestHomePage = () => { const user = useCurrentUser(); const history = useHistory(); - const { - data: organization, - isLoading: organizationIsLoading, - isError: organizationIsError, - error: organizationError - } = useCurrentOrganization(); + const { isError: organizationIsError, error: organizationError } = useCurrentOrganization(); const { setCurrentHomePage } = useHomePageContext(); // const { From 9b047c94d3e8250ea32cf17dd39e5083bfb73847 Mon Sep 17 00:00:00 2001 From: Richard Feng Date: Thu, 19 Feb 2026 19:32:19 -0500 Subject: [PATCH 04/13] #3872 used different hook for subdivisions --- src/backend/src/prisma/seed.ts | 12 ++--- src/frontend/src/layouts/Sidebar/Sidebar.tsx | 46 ++++++-------------- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index 8b641d8659..57a7dca15e 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -350,21 +350,15 @@ const performSeed: () => Promise = async () => { const mechanical = await TeamsService.createTeamType( batman, 'Mechanical', - 'YouTubeIcon', + 'Construction', 'This is the mechanical team', ner ); - const software = await TeamsService.createTeamType( - thomasEmrax, - 'Software', - 'InstagramIcon', - 'This is the software team', - ner - ); + const software = await TeamsService.createTeamType(thomasEmrax, 'Software', 'Code', 'This is the software team', ner); const electrical = await TeamsService.createTeamType( cyborg, 'Electrical', - 'SettingsIcon', + 'ElectricBolt', 'This is the electrical team', ner ); diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index a0436ac327..b4ec31e1ca 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -25,17 +25,15 @@ import DrawerHeader from '../../components/DrawerHeader'; import { Cached, ChevronLeft, ChevronRight } from '@mui/icons-material'; import { useHomePageContext } from '../../app/HomePageContext'; import { isGuest, TeamType } from 'shared'; -import { getAllTeamTypes } from '../../apis/team-types.api'; -import ConstructionIcon from '@mui/icons-material/Construction'; -import ElectricBoltIcon from '@mui/icons-material/ElectricBolt'; -import CodeIcon from '@mui/icons-material/Code'; -import WorkIcon from '@mui/icons-material/Work'; +import * as MuiIcons from '@mui/icons-material'; +import { useAllTeamTypes } from '../../hooks/team-types.hooks'; +import ErrorPage from '../../pages/ErrorPage'; import BarChartIcon from '@mui/icons-material/BarChart'; import { useCurrentUser } from '../../hooks/users.hooks'; import QueryStatsIcon from '@mui/icons-material/QueryStats'; import CurrencyExchangeIcon from '@mui/icons-material/CurrencyExchange'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; -import { useState, useEffect } from 'react'; +import { useState } from 'react'; interface SidebarProps { drawerOpen: boolean; @@ -49,36 +47,18 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid const { onPNMHomePage, onOnboardingHomePage } = useHomePageContext(); const user = useCurrentUser(); const { onGuestHomePage } = useHomePageContext(); - const [allTeams, setAllTeams] = useState([]); + const { isError: teamsError, error: teamsErrorMsg, data: teams } = useAllTeamTypes(); - const getIcon = (iconName: string) => { - const icons: { [key: string]: React.ComponentType } = { - ConstructionIcon, - CodeIcon, - ElectricBoltIcon, - WorkIcon + const allTeams: LinkItem[] = (teams ?? []).map((team: TeamType) => { + const IconComponent = MuiIcons[(team.iconName in MuiIcons ? team.iconName : 'Circle') as keyof typeof MuiIcons]; + return { + name: team.name, + icon: , + route: routes.TEAMS + '/' + team.teamTypeId }; - const Icon = icons[iconName]; - return Icon ? : undefined; - }; - - useEffect(() => { - getAllTeamTypes() - .then((response) => { - console.log('All teams from API:', response.data); - setAllTeams( - response.data.map((team: TeamType) => ({ - name: team.name, - icon: getIcon(team.iconName), - route: routes.TEAMS + '/' + team.teamTypeId - })) - ); - }) - .catch((error) => { - console.log("Teams couldn't load " + error); - }); - }, []); + }); + if (teamsError) return ; const memberLinkItems: LinkItem[] = [ { name: 'Home', From 697fb446b9e530b486cafc0ca23d4199284cbbed Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:15:58 -0500 Subject: [PATCH 05/13] #3873 working frontend --- src/backend/src/prisma/schema.prisma | 16 +- src/backend/src/prisma/seed.ts | 5 +- .../src/pages/HomePage/IntroGuestHomePage.tsx | 153 ++++++++++++------ .../HomePage/components/FeaturedProjects.tsx | 6 +- src/shared/src/types/user-types.ts | 2 + 5 files changed, 126 insertions(+), 56 deletions(-) diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 3d3ba18d35..c289931ebe 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1027,12 +1027,12 @@ enum DayOfWeek { } model Schedule_Slot { - scheduleSlotId String @id @default(uuid()) - startTime DateTime - endTime DateTime - allDay Boolean @default(false) - eventId String - event Event @relation(fields: [eventId], references: [eventId]) + scheduleSlotId String @id @default(uuid()) + startTime DateTime + endTime DateTime + allDay Boolean @default(false) + eventId String + event Event @relation(fields: [eventId], references: [eventId]) @@index([endTime]) @@index([startTime]) @@ -1088,7 +1088,7 @@ model Event { shops Shop[] machinery Machinery[] workPackages Work_Package[] - documents Document[] + documents Document[] status Event_Status initialDateScheduled DateTime? questionDocumentLink String? @@ -1322,6 +1322,8 @@ model Organization { partReviewSampleImageId String? partReviewGuideLink String? sponsorshipNotificationsSlackChannelId String? + finishlineDescription String @default("") + finishlineLogo String @default("") // Relation references wbsElements WBS_Element[] diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index a785823598..42b1d43833 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -128,7 +128,10 @@ const performSeed: () => Promise = async () => { applyInterestImageId: '1_iak6ord4JP9TcR1sOYopyEs6EjTKQpw', exploreAsGuestImageId: '1wRes7V_bMm9W7_3JCIDXYkMUiy6B3wRI', applicationLink: - 'https://docs.google.com/forms/d/e/1FAIpQLSeCvG7GqmZm_gmSZiahbVTW9ZFpEWG0YfGQbkSB_whhHzxXpA/closedform' + 'https://docs.google.com/forms/d/e/1FAIpQLSeCvG7GqmZm_gmSZiahbVTW9ZFpEWG0YfGQbkSB_whhHzxXpA/closedform', + finishlineDescription: + 'Finishline is a Project Management Dashboard developed by the Software Team at Northeastern Electric Racing.', + finishlineLogo: '1auQO3GYydZOo1-vCn0D2iyCfaxaVFssx' } }); diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index 08e2bd763b..b228eea171 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -1,19 +1,19 @@ -import { Typography, Box } from '@mui/material'; -import PageLayout from '../../components/PageLayout'; -import ImageWithButton from './components/ImageWithButton'; -import { useHistory } from 'react-router-dom'; -import { routes } from '../../utils/routes'; -import { useCurrentUser } from '../../hooks/users.hooks'; +import { Typography, Box, Icon, Card, CardContent } from '@mui/material'; +import Link from '@mui/material/Link'; +import { Link as RouterLink } from 'react-router-dom'; import { useEffect } from 'react'; import { useHomePageContext } from '../../app/HomePageContext'; import { useCurrentOrganization } from '../../hooks/organizations.hooks'; import LoadingIndicator from '../../components/LoadingIndicator'; import ErrorPage from '../ErrorPage'; import { useGetImageUrl } from '../../hooks/onboarding.hook'; +import FeaturedProjects from './components/FeaturedProjects'; +import { useAllUsefulLinks } from '../../hooks/projects.hooks'; +import { Stack } from '@mui/system'; +import { routes } from '../../utils/routes'; +import { NERButton } from '../../components/NERButton'; const IntroGuestHomePage = () => { - const user = useCurrentUser(); - const history = useHistory(); const { data: organization, isLoading: organizationIsLoading, @@ -22,63 +22,124 @@ const IntroGuestHomePage = () => { } = useCurrentOrganization(); const { setCurrentHomePage } = useHomePageContext(); - const { - data: applyInterestImageUrl, - isLoading: applyImageLoading, - isError: applyImageIsError, - error: applyImageError - } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { - data: exploreGuestImageUrl, - isLoading: exploreImageLoading, - isError: exploreImageIsError, - error: exploreImageError - } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); - useEffect(() => { setCurrentHomePage('guest'); }, [setCurrentHomePage]); + const { + data: usefulLinks, + isLoading: usefulLinksIsLoading, + isError: usefulLinksIsError, + error: usefulLinksError + } = useAllUsefulLinks(); + + const { + data: finishlineImageUrl, + isLoading: finishlineImageIsLoading, + isError: finishlineImageIsError, + error: finishlineImageError + } = useGetImageUrl(organization?.finishlineLogo ?? null); + if (organizationIsError) { return ; } - if (applyImageIsError) return ; - if (exploreImageIsError) return ; - if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; - if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; + if (usefulLinksIsError) { + return ; + } + + if (finishlineImageIsError) { + return ; + } + + if ( + !finishlineImageUrl || + finishlineImageIsLoading || + !organization || + organizationIsLoading || + !usefulLinks || + usefulLinksIsLoading + ) + return ; return ( - - - {user ? `Welcome, ${user.firstName}!` : 'Welcome, Guest!'} + + + FinishLine By NER + + + + + {organization.finishlineDescription} + + + + {usefulLinks.map((link) => ( + + {link.linkType.iconName} + + ))} + + + - - history.push(routes.HOME_PNM)} - /> - history.push(routes.HOME_MEMBER)} - /> - + + + Are you a student interested in joining NER? + + + Learn more + + + + + + - + ); }; diff --git a/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx b/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx index 958e3f05f8..d706fe0315 100644 --- a/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx +++ b/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx @@ -10,7 +10,7 @@ import { wbsPipe } from 'shared'; import LoadingIndicator from '../../../components/LoadingIndicator'; import ScrollablePageBlock from './ScrollablePageBlock'; import EmptyPageBlockDisplay from './EmptyPageBlockDisplay'; -import { Box } from '@mui/material'; +import { Box, useMediaQuery, useTheme } from '@mui/material'; import { Error } from '@mui/icons-material'; const NoFeaturedProjectsDisplay: React.FC = () => { @@ -35,13 +35,15 @@ const NoFeaturedProjectsDisplay: React.FC = () => { }; const FeaturedProjects: React.FC = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const { data: featuredProjects, isLoading, isError, error } = useFeaturedProjects(); if (isLoading || !featuredProjects) return ; if (isError) return ; const fullDisplay = ( - + {featuredProjects.length === 0 ? ( ) : ( diff --git a/src/shared/src/types/user-types.ts b/src/shared/src/types/user-types.ts index f0edc940cc..f2e50608e3 100644 --- a/src/shared/src/types/user-types.ts +++ b/src/shared/src/types/user-types.ts @@ -72,6 +72,8 @@ export interface Organization { slackWorkspaceId?: string; partReviewGuideLink?: string; sponsorshipNotificationsSlackChannelId?: string; + finishlineDescription: string; + finishlineLogo: string; } /** From 58041bfb66da6719dbc90af7ebd90c2e80e914ef Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Sun, 15 Feb 2026 10:23:51 -0500 Subject: [PATCH 06/13] #3873 finished guest home page + guest user seed data --- .../migration.sql | 3 + src/backend/src/prisma/schema.prisma | 2 +- .../src/prisma/seed-data/users.seed.ts | 15 ++++ src/backend/src/prisma/seed.ts | 1 + .../src/pages/HomePage/GuestLandingPage.tsx | 85 ------------------- .../src/pages/HomePage/IntroGuestHomePage.tsx | 10 ++- 6 files changed, 26 insertions(+), 90 deletions(-) create mode 100644 src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql delete mode 100644 src/frontend/src/pages/HomePage/GuestLandingPage.tsx diff --git a/src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql b/src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql new file mode 100644 index 0000000000..94049c1cbc --- /dev/null +++ b/src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "finishlineDescription" TEXT NOT NULL DEFAULT '', +ADD COLUMN "finishlineLogo" TEXT NOT NULL DEFAULT ''; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index c289931ebe..19af0f6a12 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1323,7 +1323,7 @@ model Organization { partReviewGuideLink String? sponsorshipNotificationsSlackChannelId String? finishlineDescription String @default("") - finishlineLogo String @default("") + finishlineLogo String? // Relation references wbsElements WBS_Element[] diff --git a/src/backend/src/prisma/seed-data/users.seed.ts b/src/backend/src/prisma/seed-data/users.seed.ts index dbb31d55ed..5c7e31c84d 100644 --- a/src/backend/src/prisma/seed-data/users.seed.ts +++ b/src/backend/src/prisma/seed-data/users.seed.ts @@ -62,6 +62,20 @@ const joeBlow: Prisma.UserCreateInput = { } }; +const guestUser: Prisma.UserCreateInput = { + firstName: 'Guest', + lastName: 'User', + googleAuthId: 'guest-google-id', + email: 'guest@husky.neu.edu', + emailId: 'guest', + userSettings: { + create: { + defaultTheme: Theme.DARK, + slackId: SLACK_ID ? SLACK_ID : 'guest' + } + } +}; + const wonderwoman: Prisma.UserCreateInput = { firstName: 'Diana', lastName: 'Prince', @@ -994,6 +1008,7 @@ export const dbSeedAllUsers = { thomasEmrax, joeShmoe, joeBlow, + guestUser, wonderwoman, flash, aquaman, diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index 42b1d43833..86650c7216 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -267,6 +267,7 @@ const performSeed: () => Promise = async () => { const regina = await createUser(dbSeedAllUsers.regina, RoleEnum.MEMBER, organizationId); const patrick = await createUser(dbSeedAllUsers.patrick, RoleEnum.MEMBER, organizationId); const spongebob = await createUser(dbSeedAllUsers.spongebob, RoleEnum.MEMBER, organizationId); + await createUser(dbSeedAllUsers.guestUser, RoleEnum.GUEST, organizationId); await UsersService.updateUserRole(cyborg.userId, thomasEmrax, 'APP_ADMIN', ner); diff --git a/src/frontend/src/pages/HomePage/GuestLandingPage.tsx b/src/frontend/src/pages/HomePage/GuestLandingPage.tsx deleted file mode 100644 index c9839917a3..0000000000 --- a/src/frontend/src/pages/HomePage/GuestLandingPage.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Typography, Box } from '@mui/material'; -import PageLayout from '../../components/PageLayout'; -import ImageWithButton from './components/ImageWithButton'; -import { useHistory } from 'react-router-dom'; -import { routes } from '../../utils/routes'; -import { useCurrentUser } from '../../hooks/users.hooks'; -import { useEffect } from 'react'; -import { useHomePageContext } from '../../app/HomePageContext'; -import { useCurrentOrganization } from '../../hooks/organizations.hooks'; -import LoadingIndicator from '../../components/LoadingIndicator'; -import ErrorPage from '../ErrorPage'; -import { useGetImageUrl } from '../../hooks/onboarding.hook'; - -const GuestHomePage = () => { - const user = useCurrentUser(); - const history = useHistory(); - const { - data: organization, - isLoading: organizationIsLoading, - isError: organizationIsError, - error: organizationError - } = useCurrentOrganization(); - const { setCurrentHomePage } = useHomePageContext(); - - const { - data: applyInterestImageUrl, - isLoading: applyImageLoading, - isError: applyImageIsError, - error: applyImageError - } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { - data: exploreGuestImageUrl, - isLoading: exploreImageLoading, - isError: exploreImageIsError, - error: exploreImageError - } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); - - useEffect(() => { - setCurrentHomePage('guest'); - }, [setCurrentHomePage]); - - if (organizationIsError) { - return ; - } - if (applyImageIsError) return ; - if (exploreImageIsError) return ; - - if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; - if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; - - return ( - - - {user ? `Welcome, ${user.firstName}!` : 'Welcome, Guest!'} - - - - history.push(routes.HOME_PNM)} - /> - history.push(routes.HOME_MEMBER)} - /> - - - - ); -}; - -export default GuestHomePage; diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index b228eea171..d4c10386b7 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -64,7 +64,7 @@ const IntroGuestHomePage = () => { return ( - + FinishLine By NER { Are you a student interested in joining NER? - - Learn more - + + + Learn more + + From 1f3415822cdd57aac611a3b3620436e1f5a1d915 Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Sun, 15 Feb 2026 10:56:23 -0500 Subject: [PATCH 07/13] #3873 - removed applyInterestImageId and exploreAsGuestImageId --- .../controllers/organizations.controllers.ts | 31 ---- .../migration.sql | 3 - .../migration.sql | 12 ++ src/backend/src/prisma/schema.prisma | 2 - src/backend/src/prisma/seed.ts | 2 - .../src/routes/organizations.routes.ts | 8 - .../src/services/organizations.services.ts | 51 ------- .../transformers/organizationTransformer.ts | 2 - src/backend/tests/unit/organization.test.ts | 64 -------- .../tests/unmocked/organization.test.ts | 40 ----- src/frontend/src/apis/organizations.api.ts | 9 -- src/frontend/src/hooks/organizations.hooks.ts | 17 --- .../AdminToolsRecruitmentConfig.tsx | 138 +----------------- src/frontend/src/utils/urls.ts | 2 - src/shared/src/types/user-types.ts | 12 +- 15 files changed, 16 insertions(+), 377 deletions(-) delete mode 100644 src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql create mode 100644 src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql diff --git a/src/backend/src/controllers/organizations.controllers.ts b/src/backend/src/controllers/organizations.controllers.ts index 966691d9bb..28eb50b394 100644 --- a/src/backend/src/controllers/organizations.controllers.ts +++ b/src/backend/src/controllers/organizations.controllers.ts @@ -22,28 +22,6 @@ export default class OrganizationsController { } } - static async setImages(req: Request, res: Response, next: NextFunction) { - try { - const { applyInterestImage = [], exploreAsGuestImage = [] } = req.files as { - applyInterestImage?: Express.Multer.File[]; - exploreAsGuestImage?: Express.Multer.File[]; - }; - - const applyInterestFile = applyInterestImage[0] || null; - const exploreAsGuestFile = exploreAsGuestImage[0] || null; - - const newImages = await OrganizationsService.setImages( - applyInterestFile, - exploreAsGuestFile, - req.currentUser, - req.organization - ); - - res.status(200).json(newImages); - } catch (error: unknown) { - next(error); - } - } static async getAllUsefulLinks(req: Request, res: Response, next: NextFunction) { try { const links = await OrganizationsService.getAllUsefulLinks(req.organization.organizationId); @@ -97,15 +75,6 @@ export default class OrganizationsController { } } - static async getOrganizationImages(req: Request, res: Response, next: NextFunction) { - try { - const images = await OrganizationsService.getOrganizationImages(req.organization.organizationId); - res.status(200).json(images); - } catch (error: unknown) { - next(error); - } - } - static async setOrganizationFeaturedProjects(req: Request, res: Response, next: NextFunction) { try { const { projectIds } = req.body; diff --git a/src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql b/src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql deleted file mode 100644 index 94049c1cbc..0000000000 --- a/src/backend/src/prisma/migrations/20260211034109_guest_home_page/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "Organization" ADD COLUMN "finishlineDescription" TEXT NOT NULL DEFAULT '', -ADD COLUMN "finishlineLogo" TEXT NOT NULL DEFAULT ''; diff --git a/src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql b/src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql new file mode 100644 index 0000000000..0ee806b24b --- /dev/null +++ b/src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to drop the column `applyInterestImageId` on the `Organization` table. All the data in the column will be lost. + - You are about to drop the column `exploreAsGuestImageId` on the `Organization` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Organization" DROP COLUMN "applyInterestImageId", +DROP COLUMN "exploreAsGuestImageId", +ADD COLUMN "finishlineDescription" TEXT NOT NULL DEFAULT '', +ADD COLUMN "finishlineLogo" TEXT; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 19af0f6a12..4e0f7cd18f 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1312,8 +1312,6 @@ model Organization { advisor User? @relation(name: "advisor", fields: [advisorId], references: [userId]) advisorId String? description String @default("") - applyInterestImageId String? - exploreAsGuestImageId String? newMemberImageId String? logoImageId String? slackWorkspaceId String? diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index 86650c7216..3f6ebe6ba1 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -125,8 +125,6 @@ const performSeed: () => Promise = async () => { userCreatedId: thomasEmrax.userId, description: 'Northeastern Electric Racing is a student-run organization at Northeastern University building all-electric formula-style race cars from scratch to compete in Forumla Hybrid + Electric Formula SAE (FSAE).', - applyInterestImageId: '1_iak6ord4JP9TcR1sOYopyEs6EjTKQpw', - exploreAsGuestImageId: '1wRes7V_bMm9W7_3JCIDXYkMUiy6B3wRI', applicationLink: 'https://docs.google.com/forms/d/e/1FAIpQLSeCvG7GqmZm_gmSZiahbVTW9ZFpEWG0YfGQbkSB_whhHzxXpA/closedform', finishlineDescription: diff --git a/src/backend/src/routes/organizations.routes.ts b/src/backend/src/routes/organizations.routes.ts index ebc6d9f869..a3dd1ed319 100644 --- a/src/backend/src/routes/organizations.routes.ts +++ b/src/backend/src/routes/organizations.routes.ts @@ -11,14 +11,6 @@ const upload = multer({ limits: { fileSize: MAX_FILE_SIZE }, storage: memoryStor organizationRouter.get('/current', OrganizationsController.getCurrentOrganization); organizationRouter.post('/useful-links/set', ...linkValidators, validateInputs, OrganizationsController.setUsefulLinks); organizationRouter.get('/useful-links', OrganizationsController.getAllUsefulLinks); -organizationRouter.post( - '/images/update', - upload.fields([ - { name: 'applyInterestImage', maxCount: 1 }, - { name: 'exploreAsGuestImage', maxCount: 1 } - ]), - OrganizationsController.setImages -); organizationRouter.post( '/application-link/update', diff --git a/src/backend/src/services/organizations.services.ts b/src/backend/src/services/organizations.services.ts index 1a6620b88a..29c2f0b52f 100644 --- a/src/backend/src/services/organizations.services.ts +++ b/src/backend/src/services/organizations.services.ts @@ -104,37 +104,6 @@ export default class OrganizationsService { return newLinks; } - /** - * sets an organizations images - * @param submitter the user who is setting the images - * @param organizationId the organization which the images will be set up - * @param images the images which are being set - */ - static async setImages( - applyInterestImage: Express.Multer.File | null, - exploreAsGuestImage: Express.Multer.File | null, - submitter: User, - organization: Organization - ) { - if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { - throw new AccessDeniedAdminOnlyException('update images'); - } - - const applyInterestImageData = applyInterestImage ? await uploadFile(applyInterestImage) : null; - const exploreAsGuestImageData = exploreAsGuestImage ? await uploadFile(exploreAsGuestImage) : null; - const updateData = { - ...(applyInterestImageData && { applyInterestImageId: applyInterestImageData.id }), - ...(exploreAsGuestImageData && { exploreAsGuestImageId: exploreAsGuestImageData.id }) - }; - - const newImages = await prisma.organization.update({ - where: { organizationId: organization.organizationId }, - data: updateData - }); - - return newImages; - } - /** Gets all the useful links for an organization @param organizationId the organization to get the links for @@ -255,26 +224,6 @@ export default class OrganizationsService { return updatedOrganization; } - /** - * Gets all organization Images for the given organization Id - * @param organizationId organization Id of the milestone - * @returns all the milestones from the given organization - */ - static async getOrganizationImages(organizationId: string) { - const organization = await prisma.organization.findUnique({ - where: { organizationId } - }); - - if (!organization) { - throw new NotFoundException('Organization', organizationId); - } - - return { - applyInterestImage: organization.applyInterestImageId, - exploreAsGuestImage: organization.exploreAsGuestImageId - }; - } - /** * Updates the featured projects of an organization * @param projectIds project ids of featured projects diff --git a/src/backend/src/transformers/organizationTransformer.ts b/src/backend/src/transformers/organizationTransformer.ts index 89ddc1981c..74f2f25ac9 100644 --- a/src/backend/src/transformers/organizationTransformer.ts +++ b/src/backend/src/transformers/organizationTransformer.ts @@ -5,8 +5,6 @@ export const organizationTransformer = (organization: Organization): Organizatio return { ...organization, applicationLink: organization.applicationLink ?? undefined, - applyInterestImageId: organization.applyInterestImageId ?? undefined, - exploreAsGuestImageId: organization.exploreAsGuestImageId ?? undefined, newMemberImageId: organization.newMemberImageId ?? undefined }; }; diff --git a/src/backend/tests/unit/organization.test.ts b/src/backend/tests/unit/organization.test.ts index 3bdec303a0..14219b804e 100644 --- a/src/backend/tests/unit/organization.test.ts +++ b/src/backend/tests/unit/organization.test.ts @@ -42,46 +42,6 @@ describe('Organization Tests', () => { }); }); - describe('Set Images', () => { - const file1 = { originalname: 'image1.png' } as Express.Multer.File; - const file2 = { originalname: 'image2.png' } as Express.Multer.File; - const file3 = { originalname: 'image3.png' } as Express.Multer.File; - it('Fails if user is not an admin', async () => { - await expect( - OrganizationsService.setImages(file1, file2, await createTestUser(wonderwomanGuest, orgId), organization) - ).rejects.toThrow(new AccessDeniedAdminOnlyException('update images')); - }); - - it('Succeeds and updates all the images', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - (uploadFile as Mock).mockImplementation((file) => { - return Promise.resolve({ name: `${file.originalname}`, id: `uploaded-${file.originalname}` }); - }); - - await OrganizationsService.setImages(file1, file2, testBatman, organization); - - const oldOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(oldOrganization).not.toBeNull(); - expect(oldOrganization?.applyInterestImageId).toBe('uploaded-image1.png'); - expect(oldOrganization?.exploreAsGuestImageId).toBe('uploaded-image2.png'); - - await OrganizationsService.setImages(file1, file3, testBatman, organization); - - const updatedOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(updatedOrganization?.exploreAsGuestImageId).toBe('uploaded-image3.png'); - }); - }); - describe('Set Useful Links', () => { it('Fails if user is not an admin', async () => { await expect( @@ -204,30 +164,6 @@ describe('Organization Tests', () => { }); }); - describe('Get Organization Images', () => { - it('Fails if an organization does not exist', async () => { - await expect(async () => await OrganizationsService.getOrganizationImages('1')).rejects.toThrow( - new NotFoundException('Organization', '1') - ); - }); - - it('Succeeds and gets all the images', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - await createTestLinkType(testBatman, orgId); - await OrganizationsService.setImages( - { originalname: 'image1.png' } as Express.Multer.File, - { originalname: 'image2.png' } as Express.Multer.File, - testBatman, - organization - ); - const images = await OrganizationsService.getOrganizationImages(orgId); - - expect(images).not.toBeNull(); - expect(images.applyInterestImage).toBe('uploaded-image1.png'); - expect(images.exploreAsGuestImage).toBe('uploaded-image2.png'); - }); - }); - describe('Set Logo', () => { const file1 = { originalname: 'image1.png' } as Express.Multer.File; const file2 = { originalname: 'image2.png' } as Express.Multer.File; diff --git a/src/backend/tests/unmocked/organization.test.ts b/src/backend/tests/unmocked/organization.test.ts index 22598ca555..0c9ec1e8f4 100644 --- a/src/backend/tests/unmocked/organization.test.ts +++ b/src/backend/tests/unmocked/organization.test.ts @@ -42,46 +42,6 @@ describe('Organization Tests', () => { }); }); - describe('Set Images', () => { - const file1 = { originalname: 'image1.png' } as Express.Multer.File; - const file2 = { originalname: 'image2.png' } as Express.Multer.File; - const file3 = { originalname: 'image3.png' } as Express.Multer.File; - it('Fails if user is not an admin', async () => { - await expect( - OrganizationsService.setImages(file1, file2, await createTestUser(wonderwomanGuest, orgId), organization) - ).rejects.toThrow(new AccessDeniedAdminOnlyException('update images')); - }); - - it('Succeeds and updates all the images', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - (uploadFile as Mock).mockImplementation((file) => { - return Promise.resolve({ id: `uploaded-${file.originalname}` }); - }); - - await OrganizationsService.setImages(file1, file2, testBatman, organization); - - const oldOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(oldOrganization).not.toBeNull(); - expect(oldOrganization?.applyInterestImageId).toBe('uploaded-image1.png'); - expect(oldOrganization?.exploreAsGuestImageId).toBe('uploaded-image2.png'); - - await OrganizationsService.setImages(file1, file3, testBatman, organization); - - const updatedOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(updatedOrganization?.exploreAsGuestImageId).toBe('uploaded-image3.png'); - }); - }); - describe('Set Useful Links', () => { it('Fails if user is not an admin', async () => { await expect( diff --git a/src/frontend/src/apis/organizations.api.ts b/src/frontend/src/apis/organizations.api.ts index 6ba806add1..5990004cb4 100644 --- a/src/frontend/src/apis/organizations.api.ts +++ b/src/frontend/src/apis/organizations.api.ts @@ -93,15 +93,6 @@ export const downloadGoogleImage = async (fileId: string): Promise => { return imageBlob; }; -export const setOrganizationImages = (images: File[]) => { - const formData = new FormData(); - - formData.append('applyInterestImage', images[0]); - formData.append('exploreAsGuestImage', images[1]); - - return axios.post<{ message: string }>(apiUrls.organizationsSetImages(), formData, {}); -}; - /** * Sets the contacts for an organization * @param contacts all the contact information that is being set diff --git a/src/frontend/src/hooks/organizations.hooks.ts b/src/frontend/src/hooks/organizations.hooks.ts index cff6cd0f45..888cac0f1e 100644 --- a/src/frontend/src/hooks/organizations.hooks.ts +++ b/src/frontend/src/hooks/organizations.hooks.ts @@ -13,7 +13,6 @@ import { updateApplicationLink, setOnboardingText, updateOrganizationContacts, - setOrganizationImages, getPartReviewGuideLink, setPartReviewGuideLink, setSlackSponsorshipNotificationSlackChannelId, @@ -66,22 +65,6 @@ export const useProvideOrganization = (): OrganizationProvider => { }; }; -export const useSetOrganizationImages = () => { - const queryClient = useQueryClient(); - - return useMutation( - async (images: File[]) => { - const { data } = await setOrganizationImages(images); - return data; - }, - { - onSuccess: () => { - queryClient.invalidateQueries(['organizations']); - } - } - ); -}; - export const useFeaturedProjects = () => { return useQuery(['organizations', 'featured-projects'], async () => { const { data } = await getFeaturedProjects(); diff --git a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx index 40cfebe5e4..22e705438a 100644 --- a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx +++ b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx @@ -1,26 +1,13 @@ import { Box, Grid, Typography } from '@mui/material'; import MilestoneTable from './MilestoneTable'; import FAQsTable from './FAQTable'; -import { useToast } from '../../../hooks/toasts.hooks'; -import NERUploadButton from '../../../components/NERUploadButton'; -import React, { useState } from 'react'; -import { useCurrentOrganization, useSetOrganizationImages } from '../../../hooks/organizations.hooks'; +import React from 'react'; +import { useCurrentOrganization } from '../../../hooks/organizations.hooks'; import LoadingIndicator from '../../../components/LoadingIndicator'; -import { useGetImageUrl } from '../../../hooks/onboarding.hook'; import ErrorPage from '../../ErrorPage'; import ApplicationLinkTable from './ApplicationLinkTable'; -import { MAX_FILE_SIZE } from 'shared'; const AdminToolsRecruitmentConfig: React.FC = () => { - const { - mutateAsync: organizationImages, - isLoading: organizationImagesIsLoading, - isError: organizationImagesIsError, - error: organizationImagesError - } = useSetOrganizationImages(); - - const toast = useToast(); - const { data: organization, isLoading: organizationIsLoading, @@ -28,48 +15,11 @@ const AdminToolsRecruitmentConfig: React.FC = () => { error: organizationError } = useCurrentOrganization(); - const { data: applyInterestImageUrl } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { data: exploreGuestImageUrl } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); - - const [addedImage1, setAddedImage1] = useState(undefined); - const [addedImage2, setAddedImage2] = useState(undefined); - const [isUploadingApply, setIsUploadingApply] = useState(false); - const [isUploadingExplore, setIsUploadingExplore] = useState(false); - if (organizationIsError) { return ; } - if (organizationImagesIsLoading || !organization || organizationIsLoading) return ; - - const handleFileUpload = async (files: File[], type: 'exploreAsGuest' | 'applyInterest') => { - const validFiles: File[] = []; - files.forEach((file) => { - if (file.size < MAX_FILE_SIZE) { - if (type === 'applyInterest') { - validFiles[0] = file; - } else if (type === 'exploreAsGuest') { - validFiles[1] = file; - } - } else { - toast.error(`Error uploading ${file.name}; file must be less than ${MAX_FILE_SIZE / 1024 / 1024} MB`, 5000); - } - }); - - if (validFiles.length > 0) { - try { - type === 'applyInterest' ? setIsUploadingApply(true) : setIsUploadingExplore(true); - await organizationImages(validFiles); - toast.success('Image uploaded successfully!'); - } catch (error: any) { - if (organizationImagesIsError && organizationImagesError instanceof Error) { - toast.error(organizationImagesError.message); - } - } finally { - type === 'applyInterest' ? setIsUploadingApply(false) : setIsUploadingExplore(false); - } - } - }; + if (!organization || organizationIsLoading) return ; return ( @@ -89,88 +39,6 @@ const AdminToolsRecruitmentConfig: React.FC = () => { - - - Recruitment Images - - - - - Apply Interest Image - - {isUploadingApply ? ( - - - - ) : ( - <> - {!addedImage1 && applyInterestImageUrl && ( - - )} - { - if (e.target.files) { - setAddedImage1(e.target.files[0]); - } - }} - onSubmit={() => { - if (addedImage1) { - handleFileUpload([addedImage1], 'applyInterest'); - setAddedImage1(undefined); - } - }} - addedImage={addedImage1} - setAddedImage={setAddedImage1} - /> - - )} - - - - - Explore As Guest Image - - {isUploadingExplore ? ( - - - - ) : ( - <> - {!addedImage2 && exploreGuestImageUrl && ( - - )} - { - if (e.target.files) { - setAddedImage2(e.target.files[0]); - } - }} - onSubmit={() => { - if (addedImage2) { - handleFileUpload([addedImage2], 'exploreAsGuest'); - setAddedImage2(undefined); - } - }} - addedImage={addedImage2} - setAddedImage={setAddedImage2} - /> - - )} - - - ); diff --git a/src/frontend/src/utils/urls.ts b/src/frontend/src/utils/urls.ts index 316e21837d..5218d23ea4 100644 --- a/src/frontend/src/utils/urls.ts +++ b/src/frontend/src/utils/urls.ts @@ -358,7 +358,6 @@ const organizations = () => `${API_URL}/organizations`; const currentOrganization = () => `${organizations()}/current`; const organizationsUsefulLinks = () => `${organizations()}/useful-links`; const organizationsSetUsefulLinks = () => `${organizationsUsefulLinks()}/set`; -const organizationsSetImages = () => `${organizations()}/images/update`; const organizationsUpdateContacts = () => `${organizations()}/contacts/set`; const organizationsSetOnboardingText = () => `${organizations()}/onboardingText/set`; const organizationsUpdateApplicationLink = () => `${organizations()}/application-link/update`; @@ -719,7 +718,6 @@ export const apiUrls = { currentOrganization, organizationsUsefulLinks, organizationsSetUsefulLinks, - organizationsSetImages, organizationsUpdateContacts, organizationsSetOnboardingText, organizationsUpdateApplicationLink, diff --git a/src/shared/src/types/user-types.ts b/src/shared/src/types/user-types.ts index f2e50608e3..a31499094d 100644 --- a/src/shared/src/types/user-types.ts +++ b/src/shared/src/types/user-types.ts @@ -42,15 +42,7 @@ export type ThemeName = 'DARK' | 'LIGHT'; export type OrganizationPreview = Pick< Organization, - | 'organizationId' - | 'name' - | 'dateCreated' - | 'dateDeleted' - | 'description' - | 'applicationLink' - | 'applyInterestImageId' - | 'exploreAsGuestImageId' - | 'newMemberImageId' + 'organizationId' | 'name' | 'dateCreated' | 'dateDeleted' | 'description' | 'applicationLink' | 'newMemberImageId' >; export interface Organization { @@ -63,8 +55,6 @@ export interface Organization { treasurer?: User; advisor?: User; description: string; - applyInterestImageId?: string; - exploreAsGuestImageId?: string; newMemberImageId?: string; applicationLink?: string; onboardingText?: string; From c45043fcd8669093c8e13ced15134083d91d8c93 Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Sun, 15 Feb 2026 11:04:37 -0500 Subject: [PATCH 08/13] #3873 fix lint --- src/backend/tests/unmocked/organization.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/tests/unmocked/organization.test.ts b/src/backend/tests/unmocked/organization.test.ts index 0c9ec1e8f4..4712a97137 100644 --- a/src/backend/tests/unmocked/organization.test.ts +++ b/src/backend/tests/unmocked/organization.test.ts @@ -4,8 +4,7 @@ import { batmanAppAdmin, flashAdmin, supermanAdmin, wonderwomanGuest } from '../ import { createTestLinkType, createTestOrganization, createTestUser, resetUsers } from '../test-utils.js'; import prisma from '../../src/prisma/prisma.js'; import { testLink1 } from '../test-data/organizations.test-data.js'; -import { uploadFile } from '../../src/utils/google-integration.utils.js'; -import { Mock, vi } from 'vitest'; +import { vi } from 'vitest'; import OrganizationsService from '../../src/services/organizations.services.js'; import { Organization } from '@prisma/client'; From 0031382774afff4c095136a0dd5879d0de5f83b8 Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Sun, 22 Feb 2026 19:22:07 -0500 Subject: [PATCH 09/13] #3873 renamed platform logo and implemented get/set platform endpoints + hooks + frontend impl --- .../controllers/organizations.controllers.ts | 23 +++++++ .../migration.sql | 4 +- src/backend/src/prisma/schema.prisma | 4 +- src/backend/src/prisma/seed.ts | 4 +- .../src/routes/organizations.routes.ts | 8 +++ .../src/services/organizations.services.ts | 46 +++++++++++++ .../transformers/organizationTransformer.ts | 3 +- src/backend/tests/unit/organization.test.ts | 60 +++++++++++++++++ src/frontend/src/apis/organizations.api.ts | 12 ++++ src/frontend/src/hooks/organizations.hooks.ts | 23 ++++++- .../AdminToolsRecruitmentConfig.tsx | 64 ++++++++++++++++++- .../src/pages/HomePage/IntroGuestHomePage.tsx | 4 +- src/frontend/src/utils/urls.ts | 4 ++ src/shared/src/types/user-types.ts | 13 +++- 14 files changed, 257 insertions(+), 15 deletions(-) rename src/backend/src/prisma/migrations/{20260215155228_guest_home_page => 20260221213604_guest_home_page}/migration.sql (78%) diff --git a/src/backend/src/controllers/organizations.controllers.ts b/src/backend/src/controllers/organizations.controllers.ts index 28eb50b394..6be9bce571 100644 --- a/src/backend/src/controllers/organizations.controllers.ts +++ b/src/backend/src/controllers/organizations.controllers.ts @@ -111,6 +111,29 @@ export default class OrganizationsController { } } + static async setPlatformLogoImage(req: Request, res: Response, next: NextFunction) { + try { + if (!req.file) { + throw new HttpException(400, 'Invalid or undefined image data'); + } + + const updatedOrg = await OrganizationsService.setPlatformLogoImage(req.file, req.currentUser, req.organization); + + res.status(200).json(updatedOrg); + } catch (error: unknown) { + next(error); + } + } + + static async getPlatformLogoImage(req: Request, res: Response, next: NextFunction) { + try { + const platformLogoImageId = await OrganizationsService.getPlatformLogoImage(req.organization.organizationId); + res.status(200).json(platformLogoImageId); + } catch (error: unknown) { + next(error); + } + } + static async setNewMemberImage(req: Request, res: Response, next: NextFunction) { try { if (!req.file) { diff --git a/src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql b/src/backend/src/prisma/migrations/20260221213604_guest_home_page/migration.sql similarity index 78% rename from src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql rename to src/backend/src/prisma/migrations/20260221213604_guest_home_page/migration.sql index 0ee806b24b..efb65c70d9 100644 --- a/src/backend/src/prisma/migrations/20260215155228_guest_home_page/migration.sql +++ b/src/backend/src/prisma/migrations/20260221213604_guest_home_page/migration.sql @@ -8,5 +8,5 @@ -- AlterTable ALTER TABLE "Organization" DROP COLUMN "applyInterestImageId", DROP COLUMN "exploreAsGuestImageId", -ADD COLUMN "finishlineDescription" TEXT NOT NULL DEFAULT '', -ADD COLUMN "finishlineLogo" TEXT; +ADD COLUMN "platformDescription" TEXT NOT NULL DEFAULT '', +ADD COLUMN "platformLogoImageId" TEXT; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 4e0f7cd18f..a00a8ccbbc 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1320,8 +1320,8 @@ model Organization { partReviewSampleImageId String? partReviewGuideLink String? sponsorshipNotificationsSlackChannelId String? - finishlineDescription String @default("") - finishlineLogo String? + platformDescription String @default("") + platformLogoImageId String? // Relation references wbsElements WBS_Element[] diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index 3f6ebe6ba1..28b31c43c9 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -127,9 +127,9 @@ const performSeed: () => Promise = async () => { 'Northeastern Electric Racing is a student-run organization at Northeastern University building all-electric formula-style race cars from scratch to compete in Forumla Hybrid + Electric Formula SAE (FSAE).', applicationLink: 'https://docs.google.com/forms/d/e/1FAIpQLSeCvG7GqmZm_gmSZiahbVTW9ZFpEWG0YfGQbkSB_whhHzxXpA/closedform', - finishlineDescription: + platformDescription: 'Finishline is a Project Management Dashboard developed by the Software Team at Northeastern Electric Racing.', - finishlineLogo: '1auQO3GYydZOo1-vCn0D2iyCfaxaVFssx' + platformLogoImageId: '1auQO3GYydZOo1-vCn0D2iyCfaxaVFssx' } }); diff --git a/src/backend/src/routes/organizations.routes.ts b/src/backend/src/routes/organizations.routes.ts index a3dd1ed319..6353da80b8 100644 --- a/src/backend/src/routes/organizations.routes.ts +++ b/src/backend/src/routes/organizations.routes.ts @@ -43,6 +43,14 @@ organizationRouter.post( ); organizationRouter.post('/logo/update', upload.single('logo'), OrganizationsController.setLogoImage); organizationRouter.get('/logo', OrganizationsController.getOrganizationLogoImage); + +organizationRouter.post( + '/platform-logo/update', + upload.single('platformLogo'), + OrganizationsController.setPlatformLogoImage +); +organizationRouter.get('/platform-logo', OrganizationsController.getPlatformLogoImage); + organizationRouter.post( '/new-member-image/update', upload.single('newMemberImage'), diff --git a/src/backend/src/services/organizations.services.ts b/src/backend/src/services/organizations.services.ts index 29c2f0b52f..0752bac6ad 100644 --- a/src/backend/src/services/organizations.services.ts +++ b/src/backend/src/services/organizations.services.ts @@ -545,4 +545,50 @@ export default class OrganizationsService { return updatedOrg.financeDelegates.map(userTransformer); } + + /** + * sets an organizations platform image + * @param submitter the user who is setting the images + * @param organizationId the organization which the images will be set up + * @param images the images which are being set + */ + static async setPlatformLogoImage( + platformLogoImageId: Express.Multer.File | null, + submitter: User, + organization: Organization + ) { + if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { + throw new AccessDeniedAdminOnlyException('update platform logo'); + } + + const platformLogoImageData = platformLogoImageId ? await uploadFile(platformLogoImageId) : null; + + const updateData = { + ...(platformLogoImageData && { platformLogoImageId: platformLogoImageData.id }) + }; + + const newImages = await prisma.organization.update({ + where: { organizationId: organization.organizationId }, + data: updateData + }); + + return newImages; + } + + /** + * Gets platform logo image for the given organization + * @param organizationId organization Id of the milestone + * @returns all the milestones from the given organization + */ + static async getPlatformLogoImage(organizationId: string) { + const organization = await prisma.organization.findUnique({ + where: { organizationId } + }); + + if (!organization) { + throw new NotFoundException('Organization', organizationId); + } + + return organization.platformLogoImageId; + } } diff --git a/src/backend/src/transformers/organizationTransformer.ts b/src/backend/src/transformers/organizationTransformer.ts index 74f2f25ac9..477671cb03 100644 --- a/src/backend/src/transformers/organizationTransformer.ts +++ b/src/backend/src/transformers/organizationTransformer.ts @@ -5,6 +5,7 @@ export const organizationTransformer = (organization: Organization): Organizatio return { ...organization, applicationLink: organization.applicationLink ?? undefined, - newMemberImageId: organization.newMemberImageId ?? undefined + newMemberImageId: organization.newMemberImageId ?? undefined, + platformLogoImageId: organization.platformLogoImageId ?? undefined }; }; diff --git a/src/backend/tests/unit/organization.test.ts b/src/backend/tests/unit/organization.test.ts index 14219b804e..b42f24594a 100644 --- a/src/backend/tests/unit/organization.test.ts +++ b/src/backend/tests/unit/organization.test.ts @@ -295,4 +295,64 @@ describe('Organization Tests', () => { expect(updatedOrganization.partReviewGuideLink).toBe('newlink'); }); }); + + describe('Set Organization Platform Logo', () => { + const file1 = { originalname: 'image1.png' } as Express.Multer.File; + const file2 = { originalname: 'image2.png' } as Express.Multer.File; + const file3 = { originalname: 'image3.png' } as Express.Multer.File; + it('Fails if user is not an admin', async () => { + await expect( + OrganizationsService.setPlatformLogoImage(file1, await createTestUser(wonderwomanGuest, orgId), organization) + ).rejects.toThrow(new AccessDeniedAdminOnlyException('update platform logo')); + }); + + it('Succeeds and updates all the images', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + (uploadFile as Mock).mockImplementation((file) => { + return Promise.resolve({ name: `${file.originalname}`, id: `uploaded-${file.originalname}` }); + }); + + await OrganizationsService.setPlatformLogoImage(file2, testBatman, organization); + + const oldOrganization = await prisma.organization.findUnique({ + where: { + organizationId: orgId + } + }); + + expect(oldOrganization).not.toBeNull(); + expect(oldOrganization?.platformLogoImageId).toBe('uploaded-image2.png'); + + await OrganizationsService.setPlatformLogoImage(file3, testBatman, organization); + + const updatedOrganization = await prisma.organization.findUnique({ + where: { + organizationId: orgId + } + }); + + expect(updatedOrganization?.platformLogoImageId).toBe('uploaded-image3.png'); + }); + }); + + describe('Get Organization Platform Logo', () => { + it('Fails if an organization does not exist', async () => { + await expect(async () => await OrganizationsService.getPlatformLogoImage('1')).rejects.toThrow( + new NotFoundException('Organization', '1') + ); + }); + + it('Succeeds and gets the image', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + await OrganizationsService.setPlatformLogoImage( + { originalname: 'image1.png' } as Express.Multer.File, + testBatman, + organization + ); + const image = await OrganizationsService.getPlatformLogoImage(orgId); + + expect(image).not.toBeNull(); + expect(image).toBe('uploaded-image1.png'); + }); + }); }); diff --git a/src/frontend/src/apis/organizations.api.ts b/src/frontend/src/apis/organizations.api.ts index 5990004cb4..3a1f333a24 100644 --- a/src/frontend/src/apis/organizations.api.ts +++ b/src/frontend/src/apis/organizations.api.ts @@ -66,6 +66,18 @@ export const getOrganizationNewMemberImage = async () => { }); }; +export const setOrganizationPlatformLogoImage = async (file: File) => { + const formData = new FormData(); + formData.append('platformLogo', file); + return axios.post(apiUrls.organizationsSetPlatformLogoImage(), formData); +}; + +export const getOrganizationPlatformLogoImage = async () => { + return axios.get(apiUrls.organizationsPlatformLogoImage(), { + transformResponse: (data) => JSON.parse(data) + }); +}; + export const setOrganizationFeaturedProjects = async (featuredProjectIds: string[]) => { return axios.post(apiUrls.organizationsSetFeaturedProjects(), { projectIds: featuredProjectIds diff --git a/src/frontend/src/hooks/organizations.hooks.ts b/src/frontend/src/hooks/organizations.hooks.ts index 888cac0f1e..a40cbc0cd6 100644 --- a/src/frontend/src/hooks/organizations.hooks.ts +++ b/src/frontend/src/hooks/organizations.hooks.ts @@ -19,7 +19,9 @@ import { getFinanceDelegates, setFinanceDelegates, setOrganizationNewMemberImage, - getOrganizationNewMemberImage + getOrganizationNewMemberImage, + setOrganizationPlatformLogoImage, + getOrganizationPlatformLogoImage } from '../apis/organizations.api'; import { downloadGoogleImage } from '../apis/organizations.api'; @@ -218,6 +220,25 @@ export const useSetOrganizationNewMemberImage = () => { }); }; +export const useSetOrganizationPlatformLogoImage = () => { + const queryClient = useQueryClient(); + return useMutation(['organizations', 'platform-logo'], async (file: File) => { + const { data } = await setOrganizationPlatformLogoImage(file); + queryClient.invalidateQueries(['organizations']); + return data; + }); +}; + +export const useOrganizationPlatformLogoImage = () => { + return useQuery(['organizations', 'platform-logo'], async () => { + const { data: fileId } = await getOrganizationPlatformLogoImage(); + if (!fileId) { + return; + } + return await downloadGoogleImage(fileId); + }); +}; + /* * Custom React Hook to fetch confluence guide for current * organization in backend diff --git a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx index 22e705438a..85529d6bd6 100644 --- a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx +++ b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx @@ -1,11 +1,15 @@ import { Box, Grid, Typography } from '@mui/material'; import MilestoneTable from './MilestoneTable'; import FAQsTable from './FAQTable'; -import React from 'react'; -import { useCurrentOrganization } from '../../../hooks/organizations.hooks'; +import React, { useState } from 'react'; +import { useCurrentOrganization, useSetOrganizationPlatformLogoImage } from '../../../hooks/organizations.hooks'; import LoadingIndicator from '../../../components/LoadingIndicator'; import ErrorPage from '../../ErrorPage'; import ApplicationLinkTable from './ApplicationLinkTable'; +import { useGetImageUrl } from '../../../hooks/onboarding.hook'; +import NERUploadButton from '../../../components/NERUploadButton'; +import { useToast } from '../../../hooks/toasts.hooks'; +import { MAX_FILE_SIZE } from 'shared'; const AdminToolsRecruitmentConfig: React.FC = () => { const { @@ -15,6 +19,32 @@ const AdminToolsRecruitmentConfig: React.FC = () => { error: organizationError } = useCurrentOrganization(); + const { mutateAsync: setPlatformLogoImage, isLoading: platformLogoLoading } = useSetOrganizationPlatformLogoImage(); + + const { data: platformLogoImageUrl } = useGetImageUrl(organization?.platformLogoImageId ?? null); + + const toast = useToast(); + + const [addedPlatformLogo, setAddedPlatformLogo] = useState(undefined); + + const handlePlatformLogoUpload = async () => { + if (!addedPlatformLogo) return; + if (addedPlatformLogo.size >= MAX_FILE_SIZE) { + toast.error( + `Error uploading ${addedPlatformLogo.name}; file must be less than ${MAX_FILE_SIZE / 1024 / 1024} MB`, + 5000 + ); + return; + } + try { + await setPlatformLogoImage(addedPlatformLogo); + toast.success('Platform logo uploaded successfully.'); + setAddedPlatformLogo(undefined); + } catch (e) { + toast.error(e instanceof Error ? e.message : 'Failed to upload image'); + } + }; + if (organizationIsError) { return ; } @@ -39,6 +69,36 @@ const AdminToolsRecruitmentConfig: React.FC = () => { + + + Platform Logo + + {platformLogoLoading ? ( + + + + ) : ( + + { + if (e.target.files?.[0]) setAddedPlatformLogo(e.target.files[0]); + }} + onSubmit={handlePlatformLogoUpload} + addedImage={addedPlatformLogo} + setAddedImage={setAddedPlatformLogo} + /> + {!addedPlatformLogo && platformLogoImageUrl && ( + + )} + + )} + ); diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index d4c10386b7..c7b9e44621 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -38,7 +38,7 @@ const IntroGuestHomePage = () => { isLoading: finishlineImageIsLoading, isError: finishlineImageIsError, error: finishlineImageError - } = useGetImageUrl(organization?.finishlineLogo ?? null); + } = useGetImageUrl(organization?.platformLogoImageId ?? null); if (organizationIsError) { return ; @@ -97,7 +97,7 @@ const IntroGuestHomePage = () => { - {organization.finishlineDescription} + {organization.platformDescription} diff --git a/src/frontend/src/utils/urls.ts b/src/frontend/src/utils/urls.ts index 5218d23ea4..cf659aa197 100644 --- a/src/frontend/src/utils/urls.ts +++ b/src/frontend/src/utils/urls.ts @@ -367,6 +367,8 @@ const organizationsLogoImage = () => `${organizations()}/logo`; const organizationsSetLogoImage = () => `${organizations()}/logo/update`; const organizationsNewMemberImage = () => `${organizations()}/new-member-image`; const organizationsSetNewMemberImage = () => `${organizations()}/new-member-image/update`; +const organizationsPlatformLogoImage = () => `${organizations()}/platform-logo`; +const organizationsSetPlatformLogoImage = () => `${organizationsPlatformLogoImage()}/update`; const organizationsSetFeaturedProjects = () => `${organizationsFeaturedProjects()}/set`; const organizationsSetWorkspaceId = () => `${organizations()}/workspaceId/set`; const organizationsGetPartReviewGuideLink = () => `${organizations()}/part-review-guide-link/get`; @@ -727,6 +729,8 @@ export const apiUrls = { organizationsSetLogoImage, organizationsNewMemberImage, organizationsSetNewMemberImage, + organizationsPlatformLogoImage, + organizationsSetPlatformLogoImage, organizationsSetFeaturedProjects, organizationsSetWorkspaceId, organizationsGetPartReviewGuideLink, diff --git a/src/shared/src/types/user-types.ts b/src/shared/src/types/user-types.ts index a31499094d..5b53bb8bdb 100644 --- a/src/shared/src/types/user-types.ts +++ b/src/shared/src/types/user-types.ts @@ -42,7 +42,14 @@ export type ThemeName = 'DARK' | 'LIGHT'; export type OrganizationPreview = Pick< Organization, - 'organizationId' | 'name' | 'dateCreated' | 'dateDeleted' | 'description' | 'applicationLink' | 'newMemberImageId' + | 'organizationId' + | 'name' + | 'dateCreated' + | 'dateDeleted' + | 'description' + | 'applicationLink' + | 'newMemberImageId' + | 'platformLogoImageId' >; export interface Organization { @@ -62,8 +69,8 @@ export interface Organization { slackWorkspaceId?: string; partReviewGuideLink?: string; sponsorshipNotificationsSlackChannelId?: string; - finishlineDescription: string; - finishlineLogo: string; + platformDescription: string; + platformLogoImageId?: string; } /** From 48667ad32c1b649101f3885a0d3639d52a98f5ec Mon Sep 17 00:00:00 2001 From: Richard Feng Date: Sun, 22 Feb 2026 23:03:32 -0500 Subject: [PATCH 10/13] #3872 fixed homepage --- .../src/pages/HomePage/IntroGuestHomePage.tsx | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index 818b46fef3..08e2bd763b 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -7,26 +7,33 @@ import { useCurrentUser } from '../../hooks/users.hooks'; import { useEffect } from 'react'; import { useHomePageContext } from '../../app/HomePageContext'; import { useCurrentOrganization } from '../../hooks/organizations.hooks'; +import LoadingIndicator from '../../components/LoadingIndicator'; import ErrorPage from '../ErrorPage'; +import { useGetImageUrl } from '../../hooks/onboarding.hook'; const IntroGuestHomePage = () => { const user = useCurrentUser(); const history = useHistory(); - const { isError: organizationIsError, error: organizationError } = useCurrentOrganization(); + const { + data: organization, + isLoading: organizationIsLoading, + isError: organizationIsError, + error: organizationError + } = useCurrentOrganization(); const { setCurrentHomePage } = useHomePageContext(); - // const { - // data: applyInterestImageUrl, - // isLoading: applyImageLoading, - // isError: applyImageIsError, - // error: applyImageError - // } = useGetImageUrl(organization?.applyInterestImageId ?? null); - // const { - // data: exploreGuestImageUrl, - // isLoading: exploreImageLoading, - // isError: exploreImageIsError, - // error: exploreImageError - // } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); + const { + data: applyInterestImageUrl, + isLoading: applyImageLoading, + isError: applyImageIsError, + error: applyImageError + } = useGetImageUrl(organization?.applyInterestImageId ?? null); + const { + data: exploreGuestImageUrl, + isLoading: exploreImageLoading, + isError: exploreImageIsError, + error: exploreImageError + } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); useEffect(() => { setCurrentHomePage('guest'); @@ -35,11 +42,11 @@ const IntroGuestHomePage = () => { if (organizationIsError) { return ; } - // if (applyImageIsError) return ; - // if (exploreImageIsError) return ; + if (applyImageIsError) return ; + if (exploreImageIsError) return ; - // if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; - // if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; + if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; + if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; return ( @@ -59,13 +66,13 @@ const IntroGuestHomePage = () => { history.push(routes.HOME_PNM)} /> history.push(routes.HOME_MEMBER)} /> From 7bb8a6ee116635cee24a1294cfd33b6ae3f0fc30 Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Mon, 23 Feb 2026 17:11:36 -0500 Subject: [PATCH 11/13] #3873 edit platform description endpoints + frontend in recruitment admin tools --- .../controllers/organizations.controllers.ts | 14 ++++ .../src/routes/organizations.routes.ts | 6 ++ .../src/services/organizations.services.ts | 17 +++++ .../transformers/organizationTransformer.ts | 1 + src/frontend/src/apis/organizations.api.ts | 6 ++ src/frontend/src/hooks/organizations.hooks.ts | 17 +++++ .../AdminToolsRecruitmentConfig.tsx | 75 ++++++++++++++++++- src/frontend/src/utils/urls.ts | 2 + src/shared/src/types/user-types.ts | 1 + 9 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/backend/src/controllers/organizations.controllers.ts b/src/backend/src/controllers/organizations.controllers.ts index 6be9bce571..6ed04b5580 100644 --- a/src/backend/src/controllers/organizations.controllers.ts +++ b/src/backend/src/controllers/organizations.controllers.ts @@ -173,6 +173,20 @@ export default class OrganizationsController { } } + static async setPlatformDescription(req: Request, res: Response, next: NextFunction) { + try { + const updatedOrg = await OrganizationsService.setPlatformDescription( + req.body.platformDescription, + req.currentUser, + req.organization + ); + + res.status(200).json(updatedOrg); + } catch (error: unknown) { + next(error); + } + } + static async getOrganizationFeaturedProjects(req: Request, res: Response, next: NextFunction) { try { const featuredProjects = await OrganizationsService.getOrganizationFeaturedProjects(req.organization.organizationId); diff --git a/src/backend/src/routes/organizations.routes.ts b/src/backend/src/routes/organizations.routes.ts index 6353da80b8..bdb50e59a9 100644 --- a/src/backend/src/routes/organizations.routes.ts +++ b/src/backend/src/routes/organizations.routes.ts @@ -63,6 +63,12 @@ organizationRouter.post( validateInputs, OrganizationsController.setOrganizationDescription ); +organizationRouter.post( + '/platform-description/set', + body('platformDescription').isString(), + validateInputs, + OrganizationsController.setPlatformDescription +); organizationRouter.get('/featured-projects', OrganizationsController.getOrganizationFeaturedProjects); organizationRouter.post( '/workspaceId/set', diff --git a/src/backend/src/services/organizations.services.ts b/src/backend/src/services/organizations.services.ts index 0752bac6ad..719a5adf76 100644 --- a/src/backend/src/services/organizations.services.ts +++ b/src/backend/src/services/organizations.services.ts @@ -378,6 +378,23 @@ export default class OrganizationsService { return updatedOrg; } + /** + * Sets the platform description of a given organization. + * @param platformDescription the new platform description + * @param submitter the user making the change + * @param organization the organization whose platform description is changing + * @throws if the user is not an admin + */ + static async setPlatformDescription(platformDescription: string, submitter: User, organization: Organization) { + if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { + throw new AccessDeniedAdminOnlyException('set platform description'); + } + return prisma.organization.update({ + where: { organizationId: organization.organizationId }, + data: { platformDescription } + }); + } + /** * Gets the featured projects for the given organization Id * @param organizationId the organization to get the projects for diff --git a/src/backend/src/transformers/organizationTransformer.ts b/src/backend/src/transformers/organizationTransformer.ts index 477671cb03..a551216a54 100644 --- a/src/backend/src/transformers/organizationTransformer.ts +++ b/src/backend/src/transformers/organizationTransformer.ts @@ -6,6 +6,7 @@ export const organizationTransformer = (organization: Organization): Organizatio ...organization, applicationLink: organization.applicationLink ?? undefined, newMemberImageId: organization.newMemberImageId ?? undefined, + platformDescription: organization.platformDescription ?? '', platformLogoImageId: organization.platformLogoImageId ?? undefined }; }; diff --git a/src/frontend/src/apis/organizations.api.ts b/src/frontend/src/apis/organizations.api.ts index 3a1f333a24..6cfdcd07b2 100644 --- a/src/frontend/src/apis/organizations.api.ts +++ b/src/frontend/src/apis/organizations.api.ts @@ -42,6 +42,12 @@ export const setOrganizationDescription = async (description: string) => { }); }; +export const setPlatformDescription = async (platformDescription: string) => { + return axios.post(apiUrls.organizationsSetPlatformDescription(), { + platformDescription + }); +}; + export const getOrganizationLogo = async () => { return axios.get(apiUrls.organizationsLogoImage(), { transformResponse: (data) => JSON.parse(data) diff --git a/src/frontend/src/hooks/organizations.hooks.ts b/src/frontend/src/hooks/organizations.hooks.ts index a40cbc0cd6..f9eba94a7b 100644 --- a/src/frontend/src/hooks/organizations.hooks.ts +++ b/src/frontend/src/hooks/organizations.hooks.ts @@ -6,6 +6,7 @@ import { getFeaturedProjects, getCurrentOrganization, setOrganizationDescription, + setPlatformDescription, setOrganizationFeaturedProjects, setOrganizationWorkspaceId, setOrganizationLogo, @@ -149,6 +150,22 @@ export const useSetOrganizationDescription = () => { ); }; +export const useSetPlatformDescription = () => { + const queryClient = useQueryClient(); + return useMutation( + ['organizations', 'platform-description'], + async (platformDescription: string) => { + const { data } = await setPlatformDescription(platformDescription); + return data; + }, + { + onSuccess: () => { + queryClient.invalidateQueries(['organizations']); + } + } + ); +}; + export const useSetFeaturedProjects = () => { const queryClient = useQueryClient(); return useMutation( diff --git a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx index 85529d6bd6..9a815ba027 100644 --- a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx +++ b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx @@ -1,16 +1,29 @@ -import { Box, Grid, Typography } from '@mui/material'; +import { Box, FormControl, Grid, Typography } from '@mui/material'; import MilestoneTable from './MilestoneTable'; import FAQsTable from './FAQTable'; import React, { useState } from 'react'; -import { useCurrentOrganization, useSetOrganizationPlatformLogoImage } from '../../../hooks/organizations.hooks'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import * as yup from 'yup'; +import { + useCurrentOrganization, + useSetOrganizationPlatformLogoImage, + useSetPlatformDescription +} from '../../../hooks/organizations.hooks'; import LoadingIndicator from '../../../components/LoadingIndicator'; import ErrorPage from '../../ErrorPage'; import ApplicationLinkTable from './ApplicationLinkTable'; import { useGetImageUrl } from '../../../hooks/onboarding.hook'; import NERUploadButton from '../../../components/NERUploadButton'; +import NERSuccessButton from '../../../components/NERSuccessButton'; +import ReactHookTextField from '../../../components/ReactHookTextField'; import { useToast } from '../../../hooks/toasts.hooks'; import { MAX_FILE_SIZE } from 'shared'; +const platformDescriptionSchema = yup.object().shape({ + platformDescription: yup.string().required() +}); + const AdminToolsRecruitmentConfig: React.FC = () => { const { data: organization, @@ -20,6 +33,7 @@ const AdminToolsRecruitmentConfig: React.FC = () => { } = useCurrentOrganization(); const { mutateAsync: setPlatformLogoImage, isLoading: platformLogoLoading } = useSetOrganizationPlatformLogoImage(); + const { mutateAsync: setPlatformDescriptionMutation, isLoading: platformDescriptionSaving } = useSetPlatformDescription(); const { data: platformLogoImageUrl } = useGetImageUrl(organization?.platformLogoImageId ?? null); @@ -27,6 +41,22 @@ const AdminToolsRecruitmentConfig: React.FC = () => { const [addedPlatformLogo, setAddedPlatformLogo] = useState(undefined); + const { control, handleSubmit, reset } = useForm<{ platformDescription: string }>({ + resolver: yupResolver(platformDescriptionSchema), + defaultValues: { platformDescription: organization?.platformDescription ?? '' } + }); + const formKey = organization?.organizationId ?? 'loading'; + + const onPlatformDescriptionSubmit = async (data: { platformDescription: string }) => { + try { + const updated = await setPlatformDescriptionMutation(data.platformDescription); + reset({ platformDescription: updated.platformDescription }); + toast.success('Platform description saved.'); + } catch (e) { + toast.error(e instanceof Error ? e.message : 'Failed to save platform description'); + } + }; + const handlePlatformLogoUpload = async () => { if (!addedPlatformLogo) return; if (addedPlatformLogo.size >= MAX_FILE_SIZE) { @@ -99,6 +129,47 @@ const AdminToolsRecruitmentConfig: React.FC = () => { )} + + + Platform Description + +
{ + e.preventDefault(); + e.stopPropagation(); + handleSubmit(onPlatformDescriptionSubmit)(e); + }} + onKeyPress={(e) => { + e.key === 'Enter' && e.preventDefault(); + }} + > + + + + + + {platformDescriptionSaving ? 'Saving...' : 'Save'} + + +
+
); diff --git a/src/frontend/src/utils/urls.ts b/src/frontend/src/utils/urls.ts index cf659aa197..fba3ddbc06 100644 --- a/src/frontend/src/utils/urls.ts +++ b/src/frontend/src/utils/urls.ts @@ -362,6 +362,7 @@ const organizationsUpdateContacts = () => `${organizations()}/contacts/set`; const organizationsSetOnboardingText = () => `${organizations()}/onboardingText/set`; const organizationsUpdateApplicationLink = () => `${organizations()}/application-link/update`; const organizationsSetDescription = () => `${organizations()}/description/set`; +const organizationsSetPlatformDescription = () => `${organizations()}/platform-description/set`; const organizationsFeaturedProjects = () => `${organizations()}/featured-projects`; const organizationsLogoImage = () => `${organizations()}/logo`; const organizationsSetLogoImage = () => `${organizations()}/logo/update`; @@ -725,6 +726,7 @@ export const apiUrls = { organizationsUpdateApplicationLink, organizationsFeaturedProjects, organizationsSetDescription, + organizationsSetPlatformDescription, organizationsLogoImage, organizationsSetLogoImage, organizationsNewMemberImage, diff --git a/src/shared/src/types/user-types.ts b/src/shared/src/types/user-types.ts index 5b53bb8bdb..24cc141eac 100644 --- a/src/shared/src/types/user-types.ts +++ b/src/shared/src/types/user-types.ts @@ -49,6 +49,7 @@ export type OrganizationPreview = Pick< | 'description' | 'applicationLink' | 'newMemberImageId' + | 'platformDescription' | 'platformLogoImageId' >; From 65cf82dd498163844b6c598015e62f4ac47d6d5d Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:51:15 -0500 Subject: [PATCH 12/13] #3873 commented out unimplemented pages on the sidebar --- src/frontend/src/layouts/Sidebar/Sidebar.tsx | 73 +++++++++++--------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index b4ec31e1ca..a189085b00 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -11,7 +11,8 @@ import HomeIcon from '@mui/icons-material/Home'; import AlignHorizontalLeftIcon from '@mui/icons-material/AlignHorizontalLeft'; import RateReviewIcon from '@mui/icons-material/RateReview'; import DashboardIcon from '@mui/icons-material/Dashboard'; -import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; +// To be uncommented after guest sponsors page is developed +// import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; import FolderIcon from '@mui/icons-material/Folder'; import SyncAltIcon from '@mui/icons-material/SyncAlt'; import GroupIcon from '@mui/icons-material/Group'; @@ -25,9 +26,10 @@ import DrawerHeader from '../../components/DrawerHeader'; import { Cached, ChevronLeft, ChevronRight } from '@mui/icons-material'; import { useHomePageContext } from '../../app/HomePageContext'; import { isGuest, TeamType } from 'shared'; -import * as MuiIcons from '@mui/icons-material'; -import { useAllTeamTypes } from '../../hooks/team-types.hooks'; -import ErrorPage from '../../pages/ErrorPage'; +// To be uncommented after divisions page is developed +// import * as MuiIcons from '@mui/icons-material'; +// import { useAllTeamTypes } from '../../hooks/team-types.hooks'; +// import ErrorPage from '../../pages/ErrorPage'; import BarChartIcon from '@mui/icons-material/BarChart'; import { useCurrentUser } from '../../hooks/users.hooks'; import QueryStatsIcon from '@mui/icons-material/QueryStats'; @@ -47,18 +49,19 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid const { onPNMHomePage, onOnboardingHomePage } = useHomePageContext(); const user = useCurrentUser(); const { onGuestHomePage } = useHomePageContext(); - const { isError: teamsError, error: teamsErrorMsg, data: teams } = useAllTeamTypes(); + // const { isError: teamsError, error: teamsErrorMsg, data: teams } = useAllTeamTypes(); - const allTeams: LinkItem[] = (teams ?? []).map((team: TeamType) => { - const IconComponent = MuiIcons[(team.iconName in MuiIcons ? team.iconName : 'Circle') as keyof typeof MuiIcons]; - return { - name: team.name, - icon: , - route: routes.TEAMS + '/' + team.teamTypeId - }; - }); + // To be uncommented once guest divisions pages are developed + // const allTeams: LinkItem[] = (teams ?? []).map((team: TeamType) => { + // const IconComponent = MuiIcons[(team.iconName in MuiIcons ? team.iconName : 'Circle') as keyof typeof MuiIcons]; + // return { + // name: team.name, + // icon: , + // route: routes.TEAMS + '/' + team.teamTypeId + // }; + // }); - if (teamsError) return ; + // if (teamsError) return ; const memberLinkItems: LinkItem[] = [ { name: 'Home', @@ -130,18 +133,25 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid } ] }, - !onGuestHomePage - ? { - name: 'Teams', - icon: , - route: routes.TEAMS - } - : { - name: 'Divisions', - icon: , - route: routes.TEAMS, - subItems: allTeams - }, + + // Teams tab here to be replaced with below code once guest divisions is developed + !onGuestHomePage && { + name: 'Teams', + icon: , + route: routes.TEAMS + }, + // !onGuestHomePage + // ? { + // name: 'Teams', + // icon: , + // route: routes.TEAMS + // } + // : { + // name: 'Divisions', + // icon: , + // route: routes.TEAMS, + // subItems: allTeams + // }, !onGuestHomePage && { name: 'Calendar', icon: , @@ -152,11 +162,12 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid icon: , route: routes.RETROSPECTIVE }, - onGuestHomePage && { - name: 'Sponsors', - icon: , - route: routes.RETROSPECTIVE - }, + // To be uncommented once guest mode sponsors page is developed + // onGuestHomePage && { + // name: 'Sponsors', + // icon: , + // route: routes.RETROSPECTIVE + // }, { name: 'Info', icon: , From bedf35d36a68c259793ec148db0a31fb5615237e Mon Sep 17 00:00:00 2001 From: Sarah Taylor <150694563+staysgt@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:54:09 -0500 Subject: [PATCH 13/13] #3873 lint --- src/frontend/src/layouts/Sidebar/Sidebar.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index a189085b00..93e731e345 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -25,7 +25,8 @@ import NavUserMenu from '../PageTitle/NavUserMenu'; import DrawerHeader from '../../components/DrawerHeader'; import { Cached, ChevronLeft, ChevronRight } from '@mui/icons-material'; import { useHomePageContext } from '../../app/HomePageContext'; -import { isGuest, TeamType } from 'shared'; +// once divisions developed, import TeamType from shared +import { isGuest } from 'shared'; // To be uncommented after divisions page is developed // import * as MuiIcons from '@mui/icons-material'; // import { useAllTeamTypes } from '../../hooks/team-types.hooks';