Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track 210 - Add new editing permissions to Status and Issues tabs. #2435

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bf172a2
initial updates
Marilynn-Stone Nov 8, 2024
795fc9c
Merge pull request #1 from Marilynn-Stone/develop
Marilynn-Stone Nov 8, 2024
4a02c5a
Update editing permissions on Issues tab.
Marilynn-Stone Nov 10, 2024
190cc1c
Update editing permissions on Status tab.
Marilynn-Stone Nov 10, 2024
3962f18
Change reference of WORK_LEAD to TEAM_LEAD.
Marilynn-Stone Nov 10, 2024
602b693
Add exception to allow Roles other than what is in Keycloak to work w…
Marilynn-Stone Nov 13, 2024
5f2f1bf
Finalize permission settings. Remove console logs. Add comments.
Marilynn-Stone Nov 13, 2024
a8fa65e
Allow only Active Team Co-Leads to have editing permissions.
Marilynn-Stone Nov 13, 2024
869b0a7
Merge branch 'bcgov:develop' into TRACK-210
Marilynn-Stone Nov 13, 2024
4ce9cd1
Fix linting errors
Marilynn-Stone Nov 13, 2024
46a74e2
Merge branch 'bcgov:develop' into TRACK-210
Marilynn-Stone Nov 13, 2024
1ca855e
Remove unnecessary changes.
Marilynn-Stone Nov 13, 2024
3ad1efc
Merge branch 'develop' of github.com:Marilynn-Stone/EPIC.track into T…
Marilynn-Stone Nov 13, 2024
dc84092
Merge branch 'TRACK-210' of github.com:Marilynn-Stone/EPIC.track into…
Marilynn-Stone Nov 13, 2024
d4c29c7
Removing more unnecessary changes that I missed last commit. :(
Marilynn-Stone Nov 13, 2024
0a0c9d5
Merge branch 'bcgov:develop' into TRACK-210
Marilynn-Stone Nov 13, 2024
029324f
Fix linting issue
Marilynn-Stone Nov 13, 2024
bb8b9dc
Merge branch 'TRACK-210' of github.com:Marilynn-Stone/EPIC.track into…
Marilynn-Stone Nov 13, 2024
e0ed62c
Create utility functions. Refactor code.
Marilynn-Stone Nov 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions epictrack-api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ services:
links:
- eao-reports-api-db
ports:
- '3002:8080'
- '3200:8080'
environment:
- JWT_OIDC_ISSUER:${JWT_OIDC_ISSUER}
- JWT_OIDC_WELL_KNOWN_CONFIG:${JWT_OIDC_WELL_KNOWN_CONFIG}
Expand All @@ -48,7 +48,7 @@ services:

networks:
- eao-network


networks:
eao-network:
Expand Down
File renamed without changes.
28 changes: 28 additions & 0 deletions epictrack-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@ import {
ROLES,
} from "../../../../constants/application-constant";
import { Restricted } from "../../../shared/restricted";
import { useAppSelector } from "hooks";
import { WorkplanContext } from "components/workPlan/WorkPlanContext";
import { useIsTeamMember, useUserHasRole } from "../../utils";

const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
const latestUpdate = issue.updates[0];
const CheckCircleIcon: React.FC<IconProps> = icons["CheckCircleIcon"];
const PencilEditIcon: React.FC<IconProps> = icons["PencilEditIcon"];
const AddIcon: React.FC<IconProps> = icons["AddIcon"];

const { team } = React.useContext(WorkplanContext);
const isTeamMember = useIsTeamMember();
const userHasRole = useUserHasRole();

const {
setEditIssueUpdateFormIsOpen,
Expand All @@ -37,9 +36,6 @@ const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
setNewIssueUpdateFormIsOpen,
} = React.useContext(IssuesContext);

const { email } = useAppSelector((state) => state.user.userDetail);
const isTeamMember = team?.some((member) => member.staff.email === email);

const handleApproveIssue = () => {
approveIssue(issue.id, latestUpdate.id);
setIssueToApproveId(null);
Expand Down Expand Up @@ -154,8 +150,10 @@ const IssueDetails = ({ issue }: { issue: WorkIssue }) => {
allowed={[
latestUpdate.is_approved ? ROLES.EXTENDED_EDIT : ROLES.EDIT,
]}
exception={!latestUpdate.is_approved && isTeamMember}
errorProps={{ disabled: true }}
exception={
(!latestUpdate.is_approved && isTeamMember) || userHasRole
}
>
<Button
data-cy="edit-issue-update-button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import {
} from "../../../../../constants/application-constant";
import { Restricted } from "../../../../shared/restricted";
import { EmptyIssueHistory } from "./EmptyIssueHistory";
import { useUserHasRole } from "../../../utils";

const IssueHistory = ({ issue }: { issue: WorkIssue }) => {
const theme = useTheme();
const userHasRole = useUserHasRole();

const { setUpdateToEdit, setEditIssueUpdateFormIsOpen } =
useContext(IssuesContext);

Expand Down Expand Up @@ -95,6 +98,7 @@ const IssueHistory = ({ issue }: { issue: WorkIssue }) => {
<Restricted
allowed={[ROLES.EXTENDED_EDIT]}
errorProps={{ disabled: true }}
exception={userHasRole}
>
<Button
data-cy="edit-history-update-button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@ import icons from "../../../icons";
import { IconProps } from "../../../icons/type";
import { IssuesContext } from "../IssuesContext";
import { Restricted } from "components/shared/restricted";
import { WorkplanContext } from "components/workPlan/WorkPlanContext";
import { useAppSelector } from "hooks";
import { useUserHasRole } from "../../utils";

const IssueSummary = ({ issue }: { issue: WorkIssue }) => {
const { setEditIssueFormIsOpen, setIssueToEdit } =
React.useContext(IssuesContext);
const { team } = React.useContext(WorkplanContext);
const { email } = useAppSelector((state) => state.user.userDetail);
const isTeamMember = team?.some((member) => member.staff.email === email);

const EditIcon: React.FC<IconProps> = icons["PencilEditIcon"];
const userHasRole = useUserHasRole();

return (
<Grid
container
Expand Down Expand Up @@ -66,9 +64,9 @@ const IssueSummary = ({ issue }: { issue: WorkIssue }) => {
<Grid item xs={"auto"} container justifyContent={"flex-end"}>
<AccordionSummaryItem title="Actions" enableTooltip={true}>
<Restricted
allowed={[ROLES.EDIT]}
allowed={[ROLES.EXTENDED_EDIT]}
errorProps={{ disabled: true }}
exception={isTeamMember}
exception={userHasRole}
>
<Button
variant="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ROLES,
} from "../../../../constants/application-constant";
import { Restricted } from "../../../shared/restricted";
import { useAppSelector } from "hooks";
import { useIsTeamMember, useUserHasRole } from "../../utils";

const CheckCircleIcon: React.FC<IconProps> = Icons["CheckCircleIcon"];
const PencilEditIcon: React.FC<IconProps> = Icons["PencilEditIcon"];
Expand All @@ -27,9 +27,9 @@ const RecentStatus = () => {
setStatus,
setShowApproveStatusDialog,
} = React.useContext(StatusContext);
const { team } = React.useContext(WorkplanContext);
const { email } = useAppSelector((state) => state.user.userDetail);
const isTeamMember = team?.some((member) => member.staff.email === email);

const isTeamMember = useIsTeamMember();
const userHasRole = useUserHasRole();

return (
<GrayBox
Expand Down Expand Up @@ -137,7 +137,7 @@ const RecentStatus = () => {
<Restricted
allowed={[statuses[0].is_approved ? ROLES.EXTENDED_EDIT : ROLES.EDIT]}
errorProps={{ disabled: true }}
exception={!statuses[0].is_approved && isTeamMember}
exception={(!statuses[0].is_approved && isTeamMember) || userHasRole}
>
<Button
startIcon={<PencilEditIcon />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Box, Button, Collapse, Grid, useTheme } from "@mui/material";
import { StatusContext } from "../../StatusContext";
import { Restricted } from "../../../../shared/restricted";
import { EmptyStatusHistory } from "./EmptyStatusHistory";
import { useUserHasRole } from "../../../utils";

const ExpandIcon: React.FC<IconProps> = Icons["ExpandIcon"];
const PencilEditIcon: React.FC<IconProps> = Icons["PencilEditIcon"];
Expand All @@ -34,14 +35,14 @@ const StatusHistory = () => {
const { setShowStatusForm, setStatus } = useContext(StatusContext);
const [expand, setExpand] = useState(false);
const theme = useTheme();

const approvedStatuses = statuses.filter(
(status) => status.is_approved && status.id != statuses?.[0]?.id
(status) => status.is_approved && status.id !== statuses?.[0]?.id
);
const highlightFirstInTimeLineApproved = !statuses?.[0]?.is_approved;

const SHOW_MORE_THRESHOLD = 3;

const userHasRole = useUserHasRole();

if (approvedStatuses.length === 0) {
return <EmptyStatusHistory />;
}
Expand Down Expand Up @@ -83,6 +84,7 @@ const StatusHistory = () => {
<Restricted
allowed={[ROLES.EXTENDED_EDIT]}
errorProps={{ disabled: true }}
exception={userHasRole}
>
<Button
variant="text"
Expand Down
32 changes: 32 additions & 0 deletions epictrack-web/src/components/workPlan/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ROLES } from "../../constants/application-constant";
import { useContext } from "react";
import { useAppSelector } from "hooks";
import { WorkplanContext } from "./WorkPlanContext";

// Get the active team members
export const useActiveTeam = () => {
const { team } = useContext(WorkplanContext);
return team?.filter((member) => member.is_active) || [];
};

// Check if the current user is a team member
export const useIsTeamMember = () => {
const { team } = useContext(WorkplanContext);
const { email } = useAppSelector((state) => state.user.userDetail);
return team?.some((member) => member.staff.email === email) || false;
};

// Check if the current user has a specific role in the active team
export const useUserHasRole = () => {
const activeTeam = useActiveTeam();
const { email } = useAppSelector((state) => state.user.userDetail);
const rolesArray = [
ROLES.RESPONSIBLE_EPD,
ROLES.TEAM_LEAD,
ROLES.TEAM_CO_LEAD,
];
return activeTeam.some(
(member) =>
member.staff.email === email && rolesArray.includes(member.role.name)
);
};
3 changes: 3 additions & 0 deletions epictrack-web/src/constants/application-constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ export const ROLES = {
DEFAULT_ROLES_EAO_EPIC: "default-roles-eao-epic",
MANAGE_USERS: "manage_users",
EXTENDED_EDIT: "extended_edit",
RESPONSIBLE_EPD: "Responsible EPD",
TEAM_LEAD: "Team Lead",
TEAM_CO_LEAD: "Team Co-Lead",
};

export enum SpecialFieldEntityEnum {
Expand Down
2 changes: 1 addition & 1 deletion epictrack-web/src/services/userService/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const initKeycloak = async (dispatch: Dispatch<AnyAction>) => {
KeycloakData.tokenParsed?.resource_access?.[AppConfig.keycloak.clientId]
?.roles ?? [];
const roles = [...realmAccessRoles, ...clientLevelRoles];
console.log("My Roles:", roles);

const userDetail = new UserDetail(
userInfo["sub"],
userInfo["preferred_username"],
Expand Down
Loading