Skip to content

Commit

Permalink
Merge pull request #3149 from Northeastern-Electric-Racing/track-onbo…
Browse files Browse the repository at this point in the history
…arded-team-types

Tracking onboarding team types
  • Loading branch information
Peyton-McKee authored Jan 22, 2025
2 parents 03827b1 + 67bc18e commit 9074519
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/backend/src/controllers/teams.controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export default class TeamsController {

static async completeOnboarding(req: Request, res: Response, next: NextFunction) {
try {
await TeamsService.completeOnboarding(req.currentUser, req.organization);
await TeamsService.completeOnboarding(req.currentUser);

res.status(200).json({ message: 'Successfully completed onboarding' });
} catch (error: unknown) {
Expand Down
5 changes: 5 additions & 0 deletions src/backend/src/prisma-query-args/auth-user.query-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export const getAuthUserQueryArgs = (organizationId: string) =>
where: {
organizationId
}
},
onboardedTeamTypes: {
where: {
organizationId
}
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- CreateTable
CREATE TABLE "_onboardedTeamTypes" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL
);

-- CreateIndex
CREATE UNIQUE INDEX "_onboardedTeamTypes_AB_unique" ON "_onboardedTeamTypes"("A", "B");

-- CreateIndex
CREATE INDEX "_onboardedTeamTypes_B_index" ON "_onboardedTeamTypes"("B");

-- AddForeignKey
ALTER TABLE "_onboardedTeamTypes" ADD CONSTRAINT "_onboardedTeamTypes_A_fkey" FOREIGN KEY ("A") REFERENCES "Team_Type"("teamTypeId") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "_onboardedTeamTypes" ADD CONSTRAINT "_onboardedTeamTypes_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("userId") ON DELETE CASCADE ON UPDATE CASCADE;
2 changes: 2 additions & 0 deletions src/backend/src/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ model User {
checkedChecklists Checklist[] @relation(name: "checkedChecklists")
contacts Contact[]
onboardingTeamTypes Team_Type[] @relation(name: "onboardingTeamTypes")
onboardedTeamTypes Team_Type[] @relation(name: "onboardedTeamTypes")
}

model Role {
Expand Down Expand Up @@ -774,6 +775,7 @@ model Team_Type {
calendarId String?
checklists Checklist[]
usersOnboarding User[] @relation(name: "onboardingTeamTypes")
usersOnboarded User[] @relation(name: "onboardedTeamTypes")
dateDeleted DateTime?
deletedById String?
Expand Down
31 changes: 13 additions & 18 deletions src/backend/src/services/teams.services.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isAdmin, isHead, RoleEnum, Team, TeamType } from 'shared';
import { isAdmin, isHead, Team, TeamType } from 'shared';
import { Organization, User, WBS_Element_Status } from '@prisma/client';
import prisma from '../prisma/prisma';
import teamTransformer from '../transformers/teams.transformer';
Expand Down Expand Up @@ -609,30 +609,25 @@ export default class TeamsService {
return teamTypeTransformer(updatedTeamType);
}

static async completeOnboarding(submitter: User, organization: Organization) {
// remove the user from any onboardingTeamTypes they are a part of
const user = await prisma.user.update({
static async completeOnboarding(submitter: User) {
const onboardingTeamTypes = await prisma.team_Type.findMany({
where: { usersOnboarding: { some: { userId: submitter.userId } } }
});

const teamTypeIds = onboardingTeamTypes.map((teamType) => ({ teamTypeId: teamType.teamTypeId }));

// remove the user from any onboardingTeamTypes they are a part of and add them to the onboardedTeamTypes
await prisma.user.update({
where: { userId: submitter.userId },
include: { roles: true },
data: {
onboardedTeamTypes: {
set: teamTypeIds
},
onboardingTeamTypes: {
set: []
}
}
});

// update the users role to member after they complete their onboarding
const currentRole = user.roles.find((role) => role.organizationId === organization.organizationId);
if (currentRole && currentRole.roleType !== RoleEnum.MEMBER) {
await prisma.role.update({
where: {
uniqueRole: { userId: user.userId, organizationId: organization.organizationId }
},
data: {
roleType: RoleEnum.MEMBER
}
});
}
}

static async setTeamTypeImage(
Expand Down
6 changes: 4 additions & 2 deletions src/backend/src/services/users.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@ export default class UsersService {
teamsAsLead: [],
teamsAsMember: [],
roles: [],
onboardingTeamTypes: []
onboardingTeamTypes: [],
onboardedTeamTypes: []
}),
token
};
Expand Down Expand Up @@ -356,7 +357,8 @@ export default class UsersService {
teamsAsLead: [],
teamsAsMember: [],
roles: [],
onboardingTeamTypes: []
onboardingTeamTypes: [],
onboardedTeamTypes: []
});
}

Expand Down
1 change: 1 addition & 0 deletions src/backend/src/transformers/auth-user.transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const authenticatedUserTransformer = (
organizations: user.organizations.map((organization) => organization.organizationId),
currentOrganization: currentOrganization ? organizationTransformer(currentOrganization) : undefined,
onboardingTeamTypeIds: user.onboardingTeamTypes.map((teamType) => teamType.teamTypeId),
onboardedTeamTypeIds: user.onboardedTeamTypes.map((teamType) => teamType.teamTypeId),
teamsAsHead: user.teamsAsHead.map(teamTransformer),
teamsAsLead: user.teamsAsLead.map(teamTransformer),
permissions: user.roles
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/app/AppMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const AppMain: React.FC = () => {
<ToastProvider>
<BrowserRouter>
<AppOAuthProvider>
<AppPublic />
<AppPublic />
</AppOAuthProvider>
</BrowserRouter>
</ToastProvider>
Expand Down
7 changes: 6 additions & 1 deletion src/frontend/src/hooks/onboarding.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ export const useGetImageUrls = (imageList: { objectId: string; imageFileId: stri
export const useChecklistProgress = (parentChecklists: Checklist[], checkedChecklists: Checklist[]) => {
const [progress, setProgress] = useState(0);
useEffect(() => {
if (!checkedChecklists || parentChecklists.length === 0) return;
if (parentChecklists.length === 0) {
setProgress(100);
return;
}

if (!checkedChecklists) return;

const totalChecklistsLength = parentChecklists.length;

Expand Down
21 changes: 21 additions & 0 deletions src/frontend/src/pages/HomePage/GuestHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,25 @@ import MemberEncouragement from './components/MemberEncouragement';
import GuestOrganizationInfo from './components/GuestOrganizationInfo';
import FeaturedProjects from './components/FeaturedProjects';
import OrganizationLogo from './components/OrganizationLogo';
import NERModal from '../../components/NERModal';
import { useEffect, useState } from 'react';

interface GuestHomePageProps {
user: AuthenticatedUser;
}

const GuestHomePage = ({ user }: GuestHomePageProps) => {
const { isLoading, isError, error, data: userSettingsData } = useSingleUserSettings(user.userId);
const [showModal, setShowModal] = useState(false);

// shows modal only once per session
useEffect(() => {
const hasSeenModal = sessionStorage.getItem('hasSeenModal');
if (!hasSeenModal) {
setShowModal(true);
sessionStorage.setItem('hasSeenModal', 'true');
}
}, []);

if (isLoading || !userSettingsData) return <LoadingIndicator />;
if (isError) return <ErrorPage error={error} message={error.message} />;
Expand Down Expand Up @@ -57,6 +69,15 @@ const GuestHomePage = ({ user }: GuestHomePageProps) => {
<FeaturedProjects />
</Box>
</Box>
<NERModal
open={showModal}
title={'Want to become a member?'}
onHide={() => setShowModal(false)}
showCloseButton
hideFormButtons
>
Ask your head to upgrade you to a member to gain full access
</NERModal>
</PageLayout>
);
};
Expand Down
10 changes: 6 additions & 4 deletions src/frontend/src/pages/HomePage/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,25 @@ import OnboardingHomePage from './OnboardingHomePage';
import SelectSubteamPage from './SelectSubteamPage';
import AcceptedPage from '../AcceptedPage/AcceptedPage';
import HomePage from './HomePage';
import { isGuest } from 'shared';
import { useCurrentUser } from '../../hooks/users.hooks';
import IntroGuestHomePage from './IntroGuestHomePage';
import { isGuest } from 'shared';

const Home: React.FC = () => {
const user = useCurrentUser();

const onOnboarding = user.onboardingTeamTypeIds.length > 0;
const userRole = user.role;
const completedOnboarding = user.onboardedTeamTypeIds.length > 0;

return (
<Switch>
{!isGuest(userRole) &&
{completedOnboarding &&
[routes.HOME_GUEST, routes.HOME_PNM, routes.HOME_ONBOARDING, routes.HOME_ACCEPT].map((path) => (
<Redirect exact path={path} to={routes.HOME} />
))}
{!onOnboarding && isGuest(userRole) && <Redirect exact path={routes.HOME} to={routes.HOME_GUEST} />}
{!onOnboarding && !completedOnboarding && isGuest(user.role) && (
<Redirect exact path={routes.HOME} to={routes.HOME_GUEST} />
)}
{onOnboarding && <Redirect exact path={routes.HOME} to={routes.HOME_PNM} />}
<Route exact path={routes.HOME_SELECT_SUBTEAM} component={SelectSubteamPage} />
<Route exact path={routes.HOME_ACCEPT} component={AcceptedPage} />
Expand Down
15 changes: 14 additions & 1 deletion src/frontend/src/pages/HomePage/components/ChecklistSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Grid } from '@mui/material';
import { Box, Grid, Typography } from '@mui/material';
import { groupChecklists } from '../../../utils/onboarding.utils';
import Checklist from './Checklist';
import { Checklist as ChecklistType } from 'shared';
Expand All @@ -20,6 +20,19 @@ const ChecklistSection: React.FC<ChecklistSectionProps> = ({ usersChecklists, ch
</Grid>
))}
</Grid>
{!usersChecklists.length && (
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100px',
marginTop: 6
}}
>
<Typography variant="h2">No checklists found</Typography>
</Box>
)}
</Box>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ export const exampleAuthenticatedAdminUser: AuthenticatedUser = {
changeRequestsToReviewId: [],
organizations: [],
onboardingTeamTypeIds: [],
onboardedTeamTypeIds: [],
permissions: []
};
4 changes: 4 additions & 0 deletions src/frontend/src/tests/test-support/test-data/users.stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const exampleAdminUser: AuthenticatedUser = {
changeRequestsToReviewId: [],
organizations: ['yello'],
onboardingTeamTypeIds: [],
onboardedTeamTypeIds: [],
permissions: []
};

Expand All @@ -40,6 +41,7 @@ export const exampleAdminUser2: AuthenticatedUser = {
changeRequestsToReviewId: [],
organizations: [],
onboardingTeamTypeIds: [],
onboardedTeamTypeIds: [],
permissions: []
};

Expand Down Expand Up @@ -84,6 +86,7 @@ export const exampleMemberUser: AuthenticatedUser = {
changeRequestsToReviewId: [],
organizations: [],
onboardingTeamTypeIds: [],
onboardedTeamTypeIds: [],
permissions: []
};

Expand All @@ -98,6 +101,7 @@ export const exampleGuestUser: AuthenticatedUser = {
changeRequestsToReviewId: [],
organizations: [],
onboardingTeamTypeIds: [],
onboardedTeamTypeIds: [],
permissions: []
};

Expand Down
1 change: 1 addition & 0 deletions src/shared/src/types/user-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export interface AuthenticatedUser {
organizations: string[];
currentOrganization?: OrganizationPreview;
onboardingTeamTypeIds: string[];
onboardedTeamTypeIds: string[];
teamsAsHead?: Team[];
teamsAsLead?: Team[];
permissions: Permission[];
Expand Down

0 comments on commit 9074519

Please sign in to comment.