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

[BIOMAGE-1903] add two checkboxes for compliance #772

Merged
merged 40 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
051354f
TMP - Mock get auth info so it fetches the info from the fake userPoo…
cosa65 Jul 12, 2022
bb11357
Add basic layout
cosa65 Jul 12, 2022
78f0eda
Add functionality for PrivacyPolicyIntercept and connect it to cognito
cosa65 Jul 12, 2022
0a828f2
Some style updates to PrivacyPolicyIntercept
cosa65 Jul 13, 2022
74d3d5e
Minor fixes
cosa65 Jul 13, 2022
7693e91
Center vertically modal
cosa65 Jul 13, 2022
3060a0f
Add logout button
cosa65 Jul 13, 2022
48fbe73
Fix alignment of checkbox on updates
cosa65 Jul 13, 2022
b00800f
Add check on user attributes to decide whether to load data or not an…
cosa65 Jul 13, 2022
934a5ff
Use user from selector in UserButton
cosa65 Jul 13, 2022
e1894cc
Merge branch 'master' into 1903-add-two-checkboxes-for-compliance
cosa65 Jul 13, 2022
a0f2f84
Some cleanup
cosa65 Jul 14, 2022
eb00fa5
Remove console log
cosa65 Jul 14, 2022
eafe075
Fix contente wrappe tests
cosa65 Jul 14, 2022
e030491
Fix SamplesTable tests
cosa65 Jul 14, 2022
cd745de
Fix index of data-management
cosa65 Jul 14, 2022
11c4a6f
Update snapshots
cosa65 Jul 14, 2022
ebdd045
Merge branch 'master' into 1903-add-two-checkboxes-for-compliance
ivababukova Jul 14, 2022
de60c27
Fix profile test
cosa65 Jul 14, 2022
d0dadc6
Fix userButton tests
cosa65 Jul 14, 2022
c4b5f9a
Update snapshots
cosa65 Jul 14, 2022
09fc0e8
Fix based on comment
cosa65 Jul 15, 2022
cd5356e
Disable closing modal by clicking outside
cosa65 Jul 15, 2022
b6b8d97
Merge branch 'master' into 1903-add-two-checkboxes-for-compliance
cosa65 Jul 15, 2022
ef10bb5
Add 424 erros message
cosa65 Jul 15, 2022
d61c644
Add domainName loading from ssr
cosa65 Jul 15, 2022
5cdffd3
Make it include biomage staging
cosa65 Jul 15, 2022
6eedf1c
Minor refactoring
cosa65 Jul 15, 2022
7d15ea1
Fix _error.test.jsx
cosa65 Jul 15, 2022
9ae59ce
Fix
cosa65 Jul 15, 2022
f62cc54
Small fixes
cosa65 Jul 15, 2022
fc7b9d0
Remove unused import
cosa65 Jul 15, 2022
da9f7f9
Update text
cosa65 Jul 15, 2022
6b20c08
Fix networkResources hydration
cosa65 Jul 15, 2022
7cab480
Update snapshots
cosa65 Jul 15, 2022
483b740
Add privacyPolicyIsNotAccepted tsts
cosa65 Jul 18, 2022
3b257e3
Some minor refctoring and add tests fro ssrGetDeploymentINfo
cosa65 Jul 18, 2022
293abfd
Rever tmp changes, point back to real user pool
cosa65 Jul 18, 2022
47b2d2e
Update privacy policy
cosa65 Jul 19, 2022
6c5b8bc
Fix experiment switch carryover of user
cosa65 Jul 19, 2022
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
7 changes: 5 additions & 2 deletions src/__test__/components/ContentWrapper.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ jest.mock('next/router', () => ({
}));

jest.mock('@aws-amplify/auth', () => ({
currentAuthenticatedUser: jest.fn().mockImplementation(async () => true),
currentAuthenticatedUser: jest.fn().mockImplementation(async () => ({
attributes: {
'custom:agreed_terms': 'true',
},
})),
federatedSignIn: jest.fn(),
}));

Expand Down Expand Up @@ -124,7 +128,6 @@ describe('ContentWrapper', () => {
await store.dispatch(updateExperimentInfo({ experimentId, experimentName, sampleIds }));
});

// PROBLEMATIC
it('renders correctly', async () => {
getBackendStatus.mockImplementation(() => () => ({
loading: false,
Expand Down
10 changes: 9 additions & 1 deletion src/__test__/components/data-management/SamplesTable.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ import loadEnvironment from 'redux/actions/networkResources/loadEnvironment';
import { loadSamples } from 'redux/actions/samples';

import mockDemoExperiments from '__test__/test-utils/mockData/mockDemoExperiments.json';
import { loadUser } from 'redux/actions/user';

jest.mock('@aws-amplify/auth', () => ({
currentAuthenticatedUser: jest.fn(() => Promise.resolve({ attributes: { name: 'mockUserName' } })),
currentAuthenticatedUser: jest.fn(() => Promise.resolve({
attributes: {
name: 'mockUserName',
'custom:agreed_terms': 'true',
},
})),
federatedSignIn: jest.fn(),
}));

Expand Down Expand Up @@ -104,6 +110,8 @@ describe('Samples table', () => {
// Defaults to project with samples
await storeState.dispatch(setActiveExperiment(experimentWithSamplesId));
await storeState.dispatch(loadEnvironment('test'));

await storeState.dispatch(loadUser());
});

it('Does not show prompt to upload datasets if samples are available', async () => {
Expand Down
6 changes: 6 additions & 0 deletions src/__test__/pages/__snapshots__/_error.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ Array [
"saving": false,
},
},
"user": Object {
"current": null,
},
},
]
`;
Expand Down Expand Up @@ -290,6 +293,9 @@ Array [
"saving": false,
},
},
"user": Object {
"current": null,
},
},
]
`;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import _ from 'lodash';

import { render, screen, waitFor } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
import { Provider } from 'react-redux';
Expand All @@ -21,6 +21,7 @@ import userEvent from '@testing-library/user-event';

import { setActiveExperiment } from 'redux/actions/experiments';
import loadEnvironment from 'redux/actions/networkResources/loadEnvironment';
import { loadUser } from 'redux/actions/user';

jest.mock('utils/data-management/downloadFromUrl');
jest.mock('react-resize-detector', () => (props) => props.children({ width: 100, height: 100 }));
Expand All @@ -34,7 +35,12 @@ jest.mock('utils/AppRouteProvider', () => ({
}));

jest.mock('@aws-amplify/auth', () => ({
currentAuthenticatedUser: jest.fn(() => Promise.resolve({ attributes: { name: 'mockUserName' } })),
currentAuthenticatedUser: jest.fn(() => Promise.resolve({
attributes: {
name: 'mockUserName',
'custom:agreed_terms': 'true',
},
})),
federatedSignIn: jest.fn(),
}));

Expand All @@ -53,11 +59,6 @@ const experimentWithoutSamples = experiments.find(
(experiment) => experiment.samplesOrder.length === 0,
);

const expectedSampleNames = [
'Example 1',
'Another-Example no.2',
];

const experimentWithSamplesId = experimentWithSamples.id;
const experimentWithoutSamplesId = experimentWithoutSamples.id;

Expand All @@ -80,6 +81,8 @@ describe('Data Management page', () => {

storeState = makeStore();
storeState.dispatch(loadEnvironment('test'));

storeState.dispatch(loadUser());
});

it('Shows an empty project list if there are no projects', async () => {
Expand Down
24 changes: 11 additions & 13 deletions src/components/ContentWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import {
} from 'antd';
import { modules } from 'utils/constants';

import Auth from '@aws-amplify/auth';

import { useAppRouter } from 'utils/AppRouteProvider';

import calculateGem2sRerunStatus from 'utils/data-management/calculateGem2sRerunStatus';
Expand All @@ -36,15 +34,15 @@ import Error from 'pages/_error';
import integrationTestConstants from 'utils/integrationTestConstants';
import pipelineStatus from 'utils/pipelineStatusValues';
import BrowserAlert from 'components/BrowserAlert';
import { loadUser } from 'redux/actions/user';
import PrivacyPolicyIntercept from './data-management/PrivacyPolicyIntercept';

const { Sider, Footer } = Layout;

const { Paragraph, Text } = Typography;
const { Sider } = Layout;
const { Text } = Typography;

const ContentWrapper = (props) => {
const dispatch = useDispatch();

const [isAuth, setIsAuth] = useState(false);
const [collapsed, setCollapsed] = useState(false);

const { routeExperimentId, experimentData, children } = props;
Expand All @@ -53,6 +51,7 @@ const ContentWrapper = (props) => {
const currentExperimentIdRef = useRef(routeExperimentId);
const activeExperimentId = useSelector((state) => state?.experiments?.meta?.activeExperimentId);
const activeExperiment = useSelector((state) => state.experiments[activeExperimentId]);
const user = useSelector((state) => state.user.current);

const samples = useSelector((state) => state.samples);

Expand Down Expand Up @@ -138,15 +137,10 @@ const ContentWrapper = (props) => {
}, [gem2sBackendStatus, activeExperiment, samples, experiment]);

useEffect(() => {
Auth.currentAuthenticatedUser()
.then(() => setIsAuth(true))
.catch(() => {
setIsAuth(false);
Auth.federatedSignIn();
});
dispatch(loadUser());
}, []);

if (!isAuth) return <></>;
if (!user) return <></>;

const BigLogo = () => (
<div
Expand Down Expand Up @@ -330,8 +324,12 @@ const ContentWrapper = (props) => {
</Menu.Item>
);
};

if (!user) return <></>;

return (
<>
{user?.attributes['custom:agreed_terms'] !== 'true' ? <PrivacyPolicyIntercept user={user} onOk={() => dispatch(loadUser())} /> : <></>}
cosa65 marked this conversation as resolved.
Show resolved Hide resolved
<BrowserAlert />
<Layout style={{ minHeight: '100vh' }}>
<Sider
Expand Down
5 changes: 3 additions & 2 deletions src/components/data-management/ExampleExperimentsSpace.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ const ExampleExperimentsSpace = ({ introductionText, imageStyle }) => {
const dispatch = useDispatch();

const environment = useSelector((state) => state?.networkResources?.environment);
const user = useSelector((state) => state?.user?.current);

const [exampleExperiments, setExampleExperiments] = useState([]);

useEffect(() => {
if (!environment) return;
if (!environment || user?.attributes['custom:agreed_terms'] !== 'true') return;

fetchAPI('/v2/experiments/examples').then((experiments) => {
setExampleExperiments(experiments);
}).catch(() => { });
}, [environment]);
}, [environment, user]);

const cloneIntoCurrentExperiment = async (exampleExperimentId) => {
const url = `/v2/experiments/${exampleExperimentId}/clone`;
Expand Down
4 changes: 4 additions & 0 deletions src/components/data-management/PrivacyPolicyIntercept.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.ok-to-the-right-modal .ant-modal-confirm-btns .ant-btn {
float: right;
margin-left: 10px;
}
98 changes: 98 additions & 0 deletions src/components/data-management/PrivacyPolicyIntercept.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import Auth from '@aws-amplify/auth';

import {
Modal, Space, Checkbox, Typography,
} from 'antd';

import 'components/data-management/PrivacyPolicyIntercept.css';

import pushNotificationMessage from 'utils/pushNotificationMessage';
import endUserMessages from 'utils/endUserMessages';

const { Text } = Typography;

const agreedPrivacyPolicyKey = 'custom:agreed_terms';
const agreedEmailsKey = 'custom:agreed_emails';

const PrivacyPolicyIntercept = (props) => {
const { user, onOk } = props;

const {
attributes: {
[agreedPrivacyPolicyKey]: originalAgreedPrivacyPolicy,
[agreedEmailsKey]: originalAgreedEmails,
},
} = user;

const [agreedPrivacyPolicy, setAgreedPrivacyPolicy] = useState(originalAgreedPrivacyPolicy);
const [agreedEmails, setAgreedEmails] = useState(originalAgreedEmails ?? 'false');

const privacyPolicyUrl = 'https://static1.squarespace.com/static/5f355513fc75aa471d47455c/t/61f12e7b7266045b4cb137bc/1643196027265/Biomage_Privacy_Policy_Jan2022.pdf';

return (
<Modal
title='Agree to the Biomage privacy policy to continue using Cellenics'
ivababukova marked this conversation as resolved.
Show resolved Hide resolved
visible
centered
className='ok-to-the-right-modal'
cancelText='Sign out'
cancelButtonProps={{ danger: true }}
okButtonProps={{ disabled: agreedPrivacyPolicy !== 'true' }}
closable={false}
onOk={async () => {
await Auth.updateUserAttributes(
user,
{
[agreedPrivacyPolicyKey]: agreedPrivacyPolicy,
[agreedEmailsKey]: agreedEmails,
},
)
.then(() => {
pushNotificationMessage('success', endUserMessages.ACCOUNT_DETAILS_UPDATED, 3);
onOk();
})
.catch(() => pushNotificationMessage('error', endUserMessages.ERROR_SAVING, 3));
}}
onCancel={async () => Auth.signOut()}
>
<Space direction='vertical'>
<Space align='start'>
<Checkbox
defaultChecked={agreedPrivacyPolicy === 'true'}
onChange={(e) => setAgreedPrivacyPolicy(e.target.checked.toString())}
/>
<Text>
<span style={{ color: '#ff0000' }}>* </span>
I accept the terms of the
{' '}
<a href={privacyPolicyUrl} target='_blank' rel='noreferrer'> Biomage privacy policy</a>
.
</Text>
</Space>
<Space align='start'>
<Checkbox
defaultChecked={agreedEmails === 'true'}
onChange={(e) => setAgreedEmails(e.target.checked.toString())}
style={{ marginRight: 10 }}
/>
<Text>
I agree to receive updates about new features in Cellenics,
research done with Cellenics, and Cellenics community events. (No external marketing.)
</Text>
</Space>
</Space>
</Modal>
);
};

PrivacyPolicyIntercept.propTypes = {
user: PropTypes.object.isRequired,
onOk: PropTypes.func.isRequired,
};

PrivacyPolicyIntercept.defaultProps = {};

export default PrivacyPolicyIntercept;
17 changes: 7 additions & 10 deletions src/components/header/UserButton.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect } from 'react';
import {
Avatar,
Button,
Expand All @@ -11,24 +11,23 @@ import Auth from '@aws-amplify/auth';
import endUserMessages from 'utils/endUserMessages';
import { resetTrackingId } from 'utils/tracking';
import handleError from 'utils/http/handleError';
import { loadUser } from 'redux/actions/user';
import { useDispatch, useSelector } from 'react-redux';

const UserButton = () => {
const [user, setUser] = useState();
const dispatch = useDispatch();

const getUser = () => Auth.currentAuthenticatedUser()
.then((userData) => userData)
.catch((e) => console.log('error during getuser', e));
const user = useSelector((state) => state.user.current);

useEffect(() => {
Hub.listen('auth', ({ payload: { event } }) => {
switch (event) {
case 'signIn':
case 'cognitoHostedUI':
getUser().then((userData) => setUser(userData));
dispatch(loadUser());
break;
case 'signOut':
resetTrackingId();
setUser(null);
cosa65 marked this conversation as resolved.
Show resolved Hide resolved
break;
case 'signIn_failure':
case 'cognitoHostedUI_failure':
Expand All @@ -38,13 +37,11 @@ const UserButton = () => {
break;
}
});

getUser().then((userData) => setUser(userData));
}, []);

const content = () => (
<Menu>
<Menu.ItemGroup key='g1' title={`Signed in as ${user.attributes.name}`} />
<Menu.ItemGroup key='g1' title={`Signed in as ${user?.attributes.name}`} />
<Menu.Item key='profile' disabled>Your profile</Menu.Item>
<Menu.Item key='settings'>
<Link href='/settings/profile'>
Expand Down
9 changes: 6 additions & 3 deletions src/pages/data-management/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const DataManagementPage = () => {

const { activeExperimentId } = useSelector((state) => state.experiments.meta);
const experiments = useSelector(((state) => state.experiments));
const user = useSelector((state) => state.user.current);

const activeExperiment = experiments[activeExperimentId];
const { saving: experimentsSaving } = experiments.meta;
Expand All @@ -31,23 +32,25 @@ const DataManagementPage = () => {
const [newProjectModalVisible, setNewProjectModalVisible] = useState(false);

useEffect(() => {
if (user?.attributes['custom:agreed_terms'] !== 'true') return;

if (experiments.ids.length === 0) dispatch(loadExperiments());
}, []);
}, [user]);

const samplesAreLoaded = () => {
const loadedSampleIds = Object.keys(samples);
return activeExperiment.sampleIds.every((sampleId) => loadedSampleIds.includes(sampleId));
};

useEffect(() => {
if (!activeExperimentId) return;
if (!activeExperimentId || user?.attributes['custom:agreed_terms'] !== 'true') return;

dispatch(loadProcessingSettings(activeExperimentId));

if (!samplesAreLoaded()) dispatch(loadSamples(activeExperimentId));

dispatch(loadBackendStatus(activeExperimentId));
}, [activeExperimentId]);
}, [activeExperimentId, user]);

const PROJECTS_LIST = 'Projects';
const PROJECT_DETAILS = 'Project Details';
Expand Down
Loading