diff --git a/src/backend/src/controllers/users.controllers.ts b/src/backend/src/controllers/users.controllers.ts index 458c19b8e4..4e6c9a9b8c 100644 --- a/src/backend/src/controllers/users.controllers.ts +++ b/src/backend/src/controllers/users.controllers.ts @@ -117,7 +117,7 @@ export default class UsersController { static async toggleCompletedOnboarding(req: Request, res: Response, next: NextFunction) { try { - const { userId } = req.params; + const { userId } = req.currentUser; await UsersService.toggleCompletedOnboarding(userId, req.organization); diff --git a/src/backend/src/routes/users.routes.ts b/src/backend/src/routes/users.routes.ts index 3a580231e5..c80e2d5ed3 100644 --- a/src/backend/src/routes/users.routes.ts +++ b/src/backend/src/routes/users.routes.ts @@ -19,7 +19,7 @@ userRouter.post( UsersController.updateUserSettings ); userRouter.post('/:userId/change-role', isRole(body('role')), validateInputs, UsersController.updateUserRole); -userRouter.post('/:userId/toggle-completed-onboarding', UsersController.toggleCompletedOnboarding); +userRouter.post('/toggle-completed-onboarding', UsersController.toggleCompletedOnboarding); userRouter.post('/auth/login', UsersController.logUserIn); userRouter.post('/auth/login/dev', UsersController.logUserInDev); userRouter.post( diff --git a/src/backend/src/services/users.services.ts b/src/backend/src/services/users.services.ts index a03c231000..e69e88d10a 100644 --- a/src/backend/src/services/users.services.ts +++ b/src/backend/src/services/users.services.ts @@ -395,7 +395,7 @@ export default class UsersService { } /** - * Toggles the completed onboarding status of a user + * Toggles the completed onboarding status of a user and elevates role to member if user completed onboarding * @param user the user who's onboarding status is being toggled * @returns the updated user */ @@ -409,8 +409,24 @@ export default class UsersService { ...getUserQueryArgs(organization.organizationId) }); + if (updatedUser.completedOnboarding) { + const currentRole = updatedUser.roles.find((role) => role.organizationId === organization.organizationId); + + if (currentRole && currentRole.roleType !== RoleEnum.MEMBER) { + await prisma.role.update({ + where: { + uniqueRole: { userId, organizationId: organization.organizationId } + }, + data: { + roleType: RoleEnum.MEMBER + } + }); + } + } + return userTransformer(updatedUser); } + /** * Gets a user's secure settings * @param userId the id of user who's secure settings are being returned diff --git a/src/frontend/src/apis/users.api.ts b/src/frontend/src/apis/users.api.ts index 44d7a40d70..1ae1171efb 100644 --- a/src/frontend/src/apis/users.api.ts +++ b/src/frontend/src/apis/users.api.ts @@ -141,3 +141,7 @@ export const updateUserScheduleSettings = (settings: SetUserScheduleSettingsPayl export const updateUserRole = (id: string, role: string) => { return axios.post<{ message: string }>(apiUrls.userRoleByUserId(id), { role }); }; + +export const toggleCompletedOnboarding = () => { + return axios.post<{ message: string }>(apiUrls.toggleCompletedOnboarding()); +}; diff --git a/src/frontend/src/hooks/users.hooks.ts b/src/frontend/src/hooks/users.hooks.ts index b0c76b09b9..d8f2a20137 100644 --- a/src/frontend/src/hooks/users.hooks.ts +++ b/src/frontend/src/hooks/users.hooks.ts @@ -17,7 +17,8 @@ import { getCurrentUserSecureSettings, getUserSecureSettings, getUserScheduleSettings, - updateUserScheduleSettings + updateUserScheduleSettings, + toggleCompletedOnboarding } from '../apis/users.api'; import { User, @@ -233,3 +234,22 @@ export const useUpdateUserRole = () => { } ); }; + +/** + * Custom React Hook to toggle the current user's completed onboarding status. + */ +export const useToggleCompletedOnboarding = () => { + const queryClient = useQueryClient(); + return useMutation<{ message: string }, Error>( + ['users', 'toggle-onboarding'], + async () => { + const { data } = await toggleCompletedOnboarding(); + return data; + }, + { + onSuccess: () => { + queryClient.invalidateQueries(['users']); + } + } + ); +}; diff --git a/src/frontend/src/pages/HomePage/OnboardingHomePage.tsx b/src/frontend/src/pages/HomePage/OnboardingHomePage.tsx index a9e8dd006d..a410a91fb8 100644 --- a/src/frontend/src/pages/HomePage/OnboardingHomePage.tsx +++ b/src/frontend/src/pages/HomePage/OnboardingHomePage.tsx @@ -1,19 +1,25 @@ -import React, { useEffect } from 'react'; -import { Box, Grid, Typography, useTheme } from '@mui/material'; +import { Box, Grid, Typography } from '@mui/material'; import PageLayout from '../../components/PageLayout'; import { useCurrentOrganization } from '../../hooks/organizations.hooks'; +import React, { useEffect, useState } from 'react'; import LoadingIndicator from '../../components/LoadingIndicator'; import ErrorPage from '../ErrorPage'; import { useHomePageContext } from '../../app/HomePageContext'; import ChecklistSection from './components/ChecklistSection'; import OnboardingInfoSection from './components/OnboardingInfoSection'; -import OnboardingProgressBar from '../../components/OnboardingProgressBar'; +import ConfirmOnboardingChecklistModal from './components/ConfirmOnboardingChecklistModal'; import { NERButton } from '../../components/NERButton'; +import { useToggleCompletedOnboarding } from '../../hooks/users.hooks'; +import { useToast } from '../../hooks/toasts.hooks'; const OnboardingHomePage = () => { const { data: organization, isError, error, isLoading } = useCurrentOrganization(); const { setCurrentHomePage } = useHomePageContext(); - const theme = useTheme(); + const [isModalOpen, setModalOpen] = useState(false); + + const toast = useToast(); + + const toggleCompletedOnboarding = useToggleCompletedOnboarding(); useEffect(() => { setCurrentHomePage('onboarding'); @@ -22,13 +28,31 @@ const OnboardingHomePage = () => { if (!organization || isLoading) return ; if (isError) return ; + const handleOpenModal = () => { + setModalOpen(true); + }; + + const handleCloseModal = () => { + setModalOpen(false); + }; + + const handleConfirmModal = async () => { + await toggleCompletedOnboarding.mutateAsync(); + toast.success('Role updated successfully!'); + setModalOpen(false); + }; + return ( - + Welcome to the {organization.name} Team - Finished? + + + Finished? + + { flexDirection: 'column' }} > - - - - + + Progress Bar - + { + {isModalOpen && ( + + )} ); }; diff --git a/src/frontend/src/pages/HomePage/components/ConfirmOnboardingChecklistModal.tsx b/src/frontend/src/pages/HomePage/components/ConfirmOnboardingChecklistModal.tsx new file mode 100644 index 0000000000..9394797ee3 --- /dev/null +++ b/src/frontend/src/pages/HomePage/components/ConfirmOnboardingChecklistModal.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { Typography, Box } from '@mui/material'; +import NERModal from '../../../components/NERModal'; + +interface ConfirmOnboardingChecklistModalProps { + open: boolean; + onHide: () => void; + onConfirm: () => void; + title?: string; + message?: string; +} + +const ConfirmOnboardingChecklistModal: React.FC = ({ + open, + onHide, + onConfirm, + title = 'Confirm Action' +}) => { + return ( + + + + Looks like you completed everything on the onboarding checklist! + + + + You sure you want to submit? + + + + (After you submit, you will be officially onboarded into NER!) + + + + ); +}; + +export default ConfirmOnboardingChecklistModal; diff --git a/src/frontend/src/utils/urls.ts b/src/frontend/src/utils/urls.ts index 1d9f59b128..9b4dc933f1 100644 --- a/src/frontend/src/utils/urls.ts +++ b/src/frontend/src/utils/urls.ts @@ -24,6 +24,7 @@ const userFavoriteProjects = (id: string) => `${usersById(id)}/favorite-projects const userSecureSettings = (id: string) => `${usersById(id)}/secure-settings`; const userScheduleSettings = (id: string) => `${usersById(id)}/schedule-settings`; const userScheduleSettingsSet = () => `${users()}/schedule-settings/set`; +const toggleCompletedOnboarding = () => `${users()}/toggle-completed-onboarding`; /**************** Projects Endpoints ****************/ const projects = () => `${API_URL}/projects`; @@ -221,6 +222,7 @@ export const apiUrls = { userSecureSettings, userScheduleSettings, userScheduleSettingsSet, + toggleCompletedOnboarding, projects, allProjects,