diff --git a/src/constants/index.js b/src/constants/index.js
index 24530d12..e10a51f6 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -169,6 +169,12 @@ export const ACTION_TYPE = {
*/
AUTH_USER_SUCCESS: "AUTH_USER_SUCCESS",
AUTH_USER_ERROR: "AUTH_USER_ERROR",
+ // load team members for authentication/permission purposes
+ AUTH_LOAD_TEAM_MEMBERS: "AUTH_LOAD_TEAM_MEMBERS",
+ AUTH_LOAD_TEAM_MEMBERS_PENDING: "AUTH_LOAD_TEAM_MEMBERS_PENDING",
+ AUTH_LOAD_TEAM_MEMBERS_SUCCESS: "AUTH_LOAD_TEAM_MEMBERS_SUCCESS",
+ AUTH_LOAD_TEAM_MEMBERS_ERROR: "AUTH_LOAD_TEAM_MEMBERS_ERROR",
+ AUTH_CLEAR_TEAM_MEMBERS: "AUTH_CLEAR_TEAM_MEMBERS",
/*
Report Popup
@@ -204,7 +210,7 @@ export const ACTION_TYPE = {
};
/**
- * All fonr field types
+ * All form field types
*/
export const FORM_FIELD_TYPE = {
TEXT: "text",
diff --git a/src/constants/permissions.js b/src/constants/permissions.js
index 299dd8ec..c57a6d82 100644
--- a/src/constants/permissions.js
+++ b/src/constants/permissions.js
@@ -9,7 +9,7 @@
*
* Examples of CORRECT permission naming and meaning:
* - `VIEW_PROJECT`
- * - `EDIT_MILESTONE`
+ * - `UPDATE_MILESTONE`
* - `DELETE_WORK`
*
* Examples of INCORRECT permissions naming and meaning:
@@ -78,31 +78,43 @@ export const PROJECT_ROLE = {
export const PERMISSIONS = {
/**
- * Resource Booking
+ * Job
*/
- EDIT_RESOURCE_BOOKING: {
+ UPDATE_JOB_STATUS: {
meta: {
- group: "Resource Booking",
- title: "Edit Resource Booking",
+ group: "Job",
+ title: 'Edit Job "status"',
},
topcoderRoles: [TOPCODER_ROLE.BOOKING_MANAGER, TOPCODER_ROLE.ADMINISTRATOR],
},
- ACCESS_RESOURCE_BOOKING_MEMBER_RATE: {
+ /**
+ * Job Candidate
+ */
+ UPDATE_JOB_CANDIDATE: {
meta: {
- group: "Resource Booking",
- title: "Access Member Rate (view and edit)",
+ group: "Job Candidate",
+ title: 'Update Job Candidate',
},
+ projectRoles: true,
topcoderRoles: [TOPCODER_ROLE.BOOKING_MANAGER, TOPCODER_ROLE.ADMINISTRATOR],
},
/**
- * Job
+ * Resource Booking
*/
- EDIT_JOB_STATUS: {
+ UPDATE_RESOURCE_BOOKING: {
meta: {
- group: "Job",
- title: 'Edit Job "status"',
+ group: "Resource Booking",
+ title: "Edit Resource Booking",
+ },
+ topcoderRoles: [TOPCODER_ROLE.BOOKING_MANAGER, TOPCODER_ROLE.ADMINISTRATOR],
+ },
+
+ ACCESS_RESOURCE_BOOKING_MEMBER_RATE: {
+ meta: {
+ group: "Resource Booking",
+ title: "Access Member Rate (view and edit)",
},
topcoderRoles: [TOPCODER_ROLE.BOOKING_MANAGER, TOPCODER_ROLE.ADMINISTRATOR],
},
diff --git a/src/hoc/withAuthentication/actions/index.js b/src/hoc/withAuthentication/actions/index.js
index 5e89291f..904e2c25 100644
--- a/src/hoc/withAuthentication/actions/index.js
+++ b/src/hoc/withAuthentication/actions/index.js
@@ -2,6 +2,7 @@
* Auth User actions
*/
import { ACTION_TYPE } from "constants";
+import { getTeamMembers } from "services/teams";
/**
* Action to set auth user data
@@ -22,9 +23,28 @@ export const authUserError = (error) => ({
});
/**
- * Action to load project/team members
+ * Loads team members for authentication/permission purposes
+ *
+ * @param {string|number} teamId
+ *
+ * @returns {Promise} loaded members or error
*/
-export const loadTeamMembers = (error) => ({
- type: ACTION_TYPE.AUTH_USER_ERROR,
- payload: error,
+export const authLoadTeamMembers = (teamId) => ({
+ type: ACTION_TYPE.AUTH_LOAD_TEAM_MEMBERS,
+ payload: async () => {
+ const res = await getTeamMembers(teamId);
+ return res.data;
+ },
+ meta: {
+ teamId,
+ },
+});
+
+/**
+ * Clear team members for authentication/permission purposes
+ *
+ * We need this if we are going to some route which doesn't have `teamId`
+ */
+export const authClearTeamMembers = () => ({
+ type: ACTION_TYPE.AUTH_CLEAR_TEAM_MEMBERS,
});
diff --git a/src/hoc/withAuthentication/index.js b/src/hoc/withAuthentication/index.js
index a767fec7..a8de5ea3 100644
--- a/src/hoc/withAuthentication/index.js
+++ b/src/hoc/withAuthentication/index.js
@@ -2,49 +2,95 @@
* Authentication
*
* wrap component for authentication
+ *
+ * - checks if user is logged-in, and if not, then redirects to the login page
+ *
+ * Also, this component load important data for `hasPermission` method:
+ * - decodes user token and set in Redux Store `authUser.userId, handle, roles`
+ * - we need to know user `roles` to check if user user has Topcoder Roles
+ * - load team (project) members if current route has `:teamId` param
+ * - we need to know members of the team to check user users Project Roles
*/
-import React, { useState, useEffect } from "react";
+import React, { useEffect } from "react";
import _ from "lodash";
import { getAuthUserTokens, login } from "@topcoder/micro-frontends-navbar-app";
import LoadingIndicator from "../../components/LoadingIndicator";
-import { authUserSuccess, authUserError } from "./actions";
+import {
+ authUserSuccess,
+ authUserError,
+ authLoadTeamMembers,
+ authClearTeamMembers,
+} from "./actions";
import { decodeToken } from "tc-auth-lib";
import { useDispatch, useSelector } from "react-redux";
+import { useParams } from "@reach/router";
export default function withAuthentication(Component) {
const AuthenticatedComponent = (props) => {
const dispatch = useDispatch();
- const { isLoggedIn, authError } = useSelector((state) => state.authUser);
+ const { isLoggedIn, authError, teamId, teamMembersLoaded, teamMembersLoadingError } = useSelector(
+ (state) => state.authUser
+ );
+ const params = useParams();
+ /*
+ Check if user is logged-in or redirect ot the login page
+ */
useEffect(() => {
// prevent page redirecting to login page when unmount
let isUnmount = false;
- getAuthUserTokens()
- .then(({ tokenV3 }) => {
- if (!!tokenV3) {
- const tokenData = decodeToken(tokenV3);
- dispatch(
- authUserSuccess(_.pick(tokenData, ["userId", "handle", "roles"]))
- );
- } else if (!isUnmount) {
- login();
- }
- })
- .catch((error) => dispatch(authUserError(error)));
+
+ if (!isLoggedIn) {
+ getAuthUserTokens()
+ .then(({ tokenV3 }) => {
+ if (!!tokenV3) {
+ const tokenData = decodeToken(tokenV3);
+ dispatch(
+ authUserSuccess(
+ _.pick(tokenData, ["userId", "handle", "roles"])
+ )
+ );
+ } else if (!isUnmount) {
+ login();
+ }
+ })
+ .catch((error) => dispatch(authUserError(error)));
+ }
return () => {
isUnmount = true;
};
- }, [dispatch]);
+ }, [dispatch, isLoggedIn]);
+
+ /*
+ Load team (project) members if current URL has `:teamId` param
+ */
+ useEffect(() => {
+ // if we haven't loaded team members yet, or we if we've moved to a page for another team
+ // we have to load team members which we would use for checking permissions
+ if (
+ isLoggedIn &&
+ params.teamId &&
+ (!teamId || params.teamId !== teamId)
+ ) {
+ dispatch(authLoadTeamMembers(params.teamId));
+
+ // if we are going to some page without `teamId` then we have to clear team members
+ // if we had some
+ } else if (teamId && !params.teamId) {
+ dispatch(authClearTeamMembers());
+ }
+ }, [params.teamId, teamId, dispatch, isLoggedIn]);
return (
<>
{/* Show loading indicator until we know if user is logged-in or no.
+ Also, show loading indicator if we need to know team members but haven't loaded them yet.
In we got error during this process, show error */}
- {isLoggedIn === null &&