Skip to content

Commit 3e735dd

Browse files
authored
Merge pull request #417 from bcgsc/feat/DEVSU-2179-report-specific-appendix
[DEVSU-2179] report specific appendix
2 parents 165244d + 5e857c1 commit 3e735dd

File tree

21 files changed

+372
-133
lines changed

21 files changed

+372
-133
lines changed

app/components/AuthenticatedRoute/index.jsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/* eslint-disable react/display-name */
22
import { PropTypes } from 'prop-types';
3-
import React, { useContext, useMemo } from 'react';
3+
import React, { useMemo } from 'react';
44
import {
55
Redirect,
66
Route,
77
} from 'react-router-dom';
88

9-
import SecurityContext from '@/context/SecurityContext';
9+
import useSecurity from '@/hooks/useSecurity';
1010
import useResource from '@/hooks/useResource';
1111
import { isAuthorized } from '@/services/management/auth';
1212

@@ -16,7 +16,7 @@ import { isAuthorized } from '@/services/management/auth';
1616
const AuthenticatedRoute = ({
1717
component: Component, adminRequired, showNav, onToggleNav, ...rest
1818
}) => {
19-
const { authorizationToken } = useContext(SecurityContext);
19+
const { authorizationToken } = useSecurity();
2020
const { adminAccess } = useResource();
2121
const authOk = isAuthorized(authorizationToken);
2222

app/components/IPRWYSIWYGEditor/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ const IPRWYSIWYGEditor = ({
186186

187187
const handleOnSave = useCallback(() => {
188188
if (editor) {
189-
onClose(editor.getHTML());
189+
onClose(editor.isEmpty ? '' : editor.getHTML());
190190
}
191191
}, [editor, onClose]);
192192

app/components/NavBar/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import MenuIcon from '@mui/icons-material/Menu';
1212
import PersonIcon from '@mui/icons-material/Person';
1313

1414
import { logout } from '@/services/management/auth';
15-
import SecurityContext from '@/context/SecurityContext';
15+
import useSecurity from '@/hooks/useSecurity';
1616
import SidebarContext from '@/context/SidebarContext';
1717
import FeedbackDialog from './components/FeedbackDialog';
1818

1919
import './index.scss';
2020

2121
const NavBar = (): JSX.Element => {
22-
const { userDetails } = useContext(SecurityContext);
22+
const { userDetails } = useSecurity();
2323
const { sidebarMaximized, setSidebarMaximized } = useContext(SidebarContext);
2424

2525
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>();

app/context/ReportContext/types.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type ReportType = {
5555
kbDiseaseMatch: string;
5656
m1m2Score: number;
5757
captiv8Score: number;
58+
appendix?: string;
5859
} & RecordDefaults;
5960

6061
type ReportContextType = {

app/context/ResourceContext/index.tsx

+38-20
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React, {
2-
createContext, ReactChild, useContext, useState, useEffect, useMemo,
2+
createContext, ReactChild, useState, useEffect, useMemo, useContext,
33
} from 'react';
4-
import SecurityContext from '@/context/SecurityContext';
5-
64
import { checkAccess, ALL_ROLES } from '@/utils/checkAccess';
5+
import useSecurity from '@/hooks/useSecurity';
76
import ResourceContextType from './types';
7+
import ReportContext from '../ReportContext';
88

99
const GERMLINE_ACCESS = ['admin', 'analyst', 'bioinformatician', 'projects', 'manager'];
1010
const GERMLINE_BLOCK = ALL_ROLES;
@@ -13,42 +13,57 @@ const REPORTS_BLOCK = [];
1313
const ADMIN_ACCESS = ['admin'];
1414
const ADMIN_BLOCK = ALL_ROLES;
1515

16-
type UseResourcesReturnType = {
17-
germlineAccess: boolean;
18-
reportsAccess: boolean;
19-
adminAccess: boolean;
20-
reportSettingAccess: boolean;
21-
};
22-
23-
const useResources = (): UseResourcesReturnType => {
24-
const { userDetails } = useContext(SecurityContext);
16+
const useResources = (): ResourceContextType => {
17+
const { userDetails: { groups, ident: userIdent } } = useSecurity();
18+
const { report } = useContext(ReportContext);
2519

2620
const [germlineAccess, setGermlineAccess] = useState(false);
2721
const [reportsAccess, setReportsAccess] = useState(false);
22+
const [reportEditAccess, setReportEditAccess] = useState(false);
2823
const [adminAccess, setAdminAccess] = useState(false);
2924
const [reportSettingAccess, setReportSettingAccess] = useState(false);
3025

26+
// Check user group first to see which resources they can access
3127
useEffect(() => {
32-
if (userDetails?.groups) {
33-
if (checkAccess(userDetails.groups, GERMLINE_ACCESS, GERMLINE_BLOCK)) {
28+
if (groups) {
29+
if (checkAccess(groups, GERMLINE_ACCESS, GERMLINE_BLOCK)) {
3430
setGermlineAccess(true);
3531
}
3632

37-
if (checkAccess(userDetails.groups, REPORTS_ACCESS, REPORTS_BLOCK)) {
33+
if (checkAccess(groups, REPORTS_ACCESS, REPORTS_BLOCK)) {
3834
setReportsAccess(true);
3935
}
4036

41-
if (checkAccess(userDetails.groups, ADMIN_ACCESS, ADMIN_BLOCK)) {
37+
if (checkAccess(groups, ADMIN_ACCESS, ADMIN_BLOCK)) {
4238
setAdminAccess(true);
4339
}
44-
if (checkAccess(userDetails.groups, [...ADMIN_ACCESS, 'manager'], ADMIN_BLOCK)) {
40+
41+
if (checkAccess(groups, [...ADMIN_ACCESS, 'manager'], ADMIN_BLOCK)) {
4542
setReportSettingAccess(true);
43+
setReportEditAccess(true);
44+
}
45+
}
46+
}, [groups]);
47+
48+
/**
49+
* Check report specific permissions if user isn't admin
50+
*/
51+
useEffect(() => {
52+
if (!adminAccess) {
53+
if (report && report.users.some(({ ident: i }) => i === userIdent)) {
54+
setReportEditAccess(true);
55+
} else {
56+
setReportEditAccess(false);
4657
}
4758
}
48-
}, [userDetails?.groups]);
59+
}, [report, userIdent, adminAccess]);
4960

5061
return {
51-
germlineAccess, reportsAccess, adminAccess, reportSettingAccess,
62+
germlineAccess,
63+
reportsAccess,
64+
adminAccess,
65+
reportSettingAccess,
66+
reportEditAccess,
5267
};
5368
};
5469

@@ -57,6 +72,7 @@ const ResourceContext = createContext<ResourceContextType>({
5772
reportsAccess: false,
5873
adminAccess: false,
5974
reportSettingAccess: false,
75+
reportEditAccess: false,
6076
});
6177

6278
type ResourceContextProviderProps = {
@@ -65,19 +81,21 @@ type ResourceContextProviderProps = {
6581

6682
const ResourceContextProvider = ({ children }: ResourceContextProviderProps): JSX.Element => {
6783
const {
68-
germlineAccess, reportsAccess, adminAccess, reportSettingAccess,
84+
germlineAccess, reportsAccess, adminAccess, reportSettingAccess, reportEditAccess,
6985
} = useResources();
7086

7187
const providerValue = useMemo(() => ({
7288
germlineAccess,
7389
reportsAccess,
7490
adminAccess,
7591
reportSettingAccess,
92+
reportEditAccess,
7693
}), [
7794
germlineAccess,
7895
reportsAccess,
7996
adminAccess,
8097
reportSettingAccess,
98+
reportEditAccess,
8199
]);
82100

83101
return (

app/context/ResourceContext/types.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ type ResourceContextType = {
33
reportsAccess: boolean;
44
adminAccess: boolean;
55
reportSettingAccess: boolean;
6+
reportEditAccess: boolean;
67
};
78

89
export default ResourceContextType;

app/context/SecurityContext/index.js

-10
This file was deleted.

app/context/SecurityContext/index.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { UserType } from '@/common';
2+
import React from 'react';
3+
4+
type SecurityContextType = {
5+
authorizationToken: string;
6+
setAuthorizationToken: (token: string) => void;
7+
userDetails: Partial<UserType>;
8+
setUserDetails: (userDetails: UserType) => void;
9+
};
10+
11+
const SecurityContext = React.createContext<SecurityContextType>({
12+
authorizationToken: '',
13+
setAuthorizationToken: () => {},
14+
userDetails: {},
15+
setUserDetails: () => {},
16+
});
17+
18+
export default SecurityContext;
19+
20+
export {
21+
SecurityContextType,
22+
SecurityContext,
23+
};

app/context/UserContext/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, {
22
createContext, ReactChild, useMemo, useContext,
33
} from 'react';
4-
import SecurityContext from '@/context/SecurityContext';
4+
import useSecurity from '@/hooks/useSecurity';
55
import { checkAccess } from '@/utils/checkAccess';
66
import UserContextInterface from './interfaces';
77

@@ -18,7 +18,7 @@ type UserContextProviderProps = {
1818
};
1919

2020
const UserContextProvider = ({ children }: UserContextProviderProps): JSX.Element => {
21-
const { userDetails } = useContext(SecurityContext);
21+
const { userDetails } = useSecurity();
2222

2323
const canEdit = useMemo(() => {
2424
if (userDetails && checkAccess(userDetails.groups, EDIT_ACCESS, EDIT_BLOCK)) {

app/hooks/useExternalMode.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { useState, useEffect, useContext } from 'react';
2-
3-
import SecurityContext from '@/context/SecurityContext';
1+
import { useState, useEffect } from 'react';
2+
import useSecurity from '@/hooks/useSecurity';
43

54
const EXTERNAL_GROUPS = ['clinician', 'collaborator'];
65

76
const useExternalMode = (): boolean => {
8-
const { userDetails } = useContext(SecurityContext);
7+
const { userDetails } = useSecurity();
98

109
const [isExternalMode, setIsExternalMode] = useState<boolean>(null);
1110

app/hooks/useSecurity.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useContext } from 'react';
2+
import { SecurityContext } from '@/context/SecurityContext';
3+
4+
const useSecurity = () => {
5+
const context = useContext(SecurityContext);
6+
7+
if (!context) {
8+
throw new Error('useSecurity must be used within a SecurityProvider');
9+
}
10+
11+
return context;
12+
};
13+
14+
export default useSecurity;

app/utils/checkAccess.ts

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const checkAccess = (
2727
blockList: string[] = ALL_ROLES,
2828
): boolean => {
2929
const groupNames = groups.map((group) => group.name.toLowerCase());
30-
3130
const isAllowed = allowList.includes('*') || allowList.some((group) => groupNames.includes(group.toLowerCase()));
3231
const isBlocked = blockList.some((group) => groupNames.includes(group.toLowerCase()));
3332
return isAllowed || !isBlocked;

app/views/LoginView/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { useContext, useEffect } from 'react';
1+
import { useEffect } from 'react';
22
import fetchIntercept from 'fetch-intercept';
33
import { RouteChildrenProps } from 'react-router-dom';
44

5-
import SecurityContext from '@/context/SecurityContext';
5+
import useSecurity from '@/hooks/useSecurity';
66
import {
77
login, isAuthorized, getReferrerUri, keycloak,
88
} from '@/services/management/auth';
@@ -18,7 +18,7 @@ const Login = (props: RouteChildrenProps): null => {
1818
authorizationToken,
1919
setAuthorizationToken,
2020
setUserDetails,
21-
} = useContext(SecurityContext);
21+
} = useSecurity();
2222

2323
let from;
2424

app/views/MainView/index.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717

1818
import AuthenticatedRoute from '@/components/AuthenticatedRoute';
1919
import SidebarContext from '@/context/SidebarContext';
20-
import SecurityContext from '@/context/SecurityContext';
20+
import { SecurityContext, SecurityContextType } from '@/context/SecurityContext';
2121
import { UserContextProvider } from '@/context/UserContext';
2222
import { ResourceContextProvider } from '@/context/ResourceContext';
2323
import NavBar from '@/components/NavBar';
@@ -125,11 +125,21 @@ const TimeoutModal = memo(({ authorizationToken, setAuthorizationToken }: Timeou
125125
*/
126126
const Main = (): JSX.Element => {
127127
const [authorizationToken, setAuthorizationToken] = useState('');
128-
const [userDetails, setUserDetails] = useState('');
128+
const [userDetails, setUserDetails] = useState<SecurityContextType['userDetails']>({
129+
firstName: null,
130+
lastName: null,
131+
username: null,
132+
groups: [],
133+
email: null,
134+
deletedAt: null,
135+
lastLogin: null,
136+
projects: [],
137+
type: null,
138+
});
129139
const [sidebarMaximized, setSidebarMaximized] = useState(false);
130140
const [isNavVisible, setIsNavVisible] = useState(true);
131141

132-
const secContextVal = useMemo(() => ({
142+
const secContextVal = useMemo<SecurityContextType>(() => ({
133143
authorizationToken, setAuthorizationToken, userDetails, setUserDetails,
134144
}), [authorizationToken, setAuthorizationToken, userDetails, setUserDetails]);
135145

0 commit comments

Comments
 (0)