Skip to content

Commit 8556698

Browse files
authored
Status email function (#182)
* user settings finished * user-account tests added * status E-mails 1st version * 3.5.5 - beta * timeout envs made optional * fixing error email * error email fix 2 * error email fix 3 * pending e-mail fix * message small fixes * add users to group fix * status.json for testing added * vitest update * api tracker tests added * frontend dependecies update
1 parent 78d522f commit 8556698

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2933
-2047
lines changed

envs/dev/backend.env

+5-4
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ JWT_SECRET='bar'
1818
EMAIL_SUFFIX=''
1919

2020
#SMTP configuration
21-
SMTP_HOST=''
21+
SMTP_HOST='mailhost.st-andrews.ac.uk'
2222
SMTP_PORT=587
2323
SMTP_SECURE=false
2424
SMTP_REQUIRE_TLS=true
25-
SMTP_USER=''
26-
SMTP_PASS=''
27-
SMTP_SENDER=''
25+
SMTP_USER='tl12'
26+
SMTP_PASS='T#c727p1z1'
27+
SMTP_SENDER='nomad@st-andrews.ac.uk'
2828

2929
#Set true if NOMAD submission is used
3030
SUBMIT_ON=true
@@ -34,3 +34,4 @@ DATASTORE_ON=true,
3434
DATASTORE_PATH='/app/datastore'
3535
#timeout for upload data route connection
3636
DATA_UPLOAD_TIMEOUT=30000
37+
PENDING_EMAIL_DELAY=1

envs/dev/client.env

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ STATUS_PATH= /app/status_files/status.html
33
HISTORY_PATH=/app/status_files/status.html
44
SERVER_URL=http://backend:8080
55
SUBMIT_PATH=/app/submit_files/
6-
UPLOAD_DELAY=10000
6+
UPLOAD_DELAY=10000
7+
NMR_DATA_PATH_AUTO=/app/nmr-data/auto
8+
NMR_DATA_PATH_MANUAL=/app/nmr-data/manual

nomad-front-end/package-lock.json

+1,029-961
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nomad-front-end/package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "nomad-front-end",
33
"private": false,
4-
"version": "3.5.4",
4+
"version": "3.5.5",
55
"type": "module",
66
"scripts": {
77
"start": "env-cmd -f ../env/frontend.env vite",
@@ -15,6 +15,7 @@
1515
"@ant-design/pro-layout": "^7.4.0",
1616
"antd": "^5.4.7",
1717
"axios": "^1.2.1",
18+
"cheminfo-font": "1.13.0",
1819
"dayjs": "^1.11.7",
1920
"history": "^5.3.0",
2021
"js-file-download": "^0.4.12",
@@ -28,22 +29,21 @@
2829
"react-copy-to-clipboard": "^5.1.0",
2930
"react-csv": "^2.2.2",
3031
"react-dom": "^18.2.0",
32+
"react-icons": "5.3.0",
3133
"react-ocl": "^7.0.2",
3234
"react-redux": "^9.0.4",
3335
"react-router": "^6.13.0",
3436
"react-router-dom": "^6.13.0",
3537
"redux": "^5.0.1",
3638
"redux-thunk": "^3.1.0",
3739
"socket.io-client": "^4.5.4",
38-
"uuid": "^11.0.3",
39-
"cheminfo-font": "1.13.0",
40-
"react-icons": "5.3.0"
40+
"uuid": "^11.0.3"
4141
},
4242
"devDependencies": {
4343
"@types/react": "^18.0.26",
4444
"@types/react-dom": "^18.0.9",
4545
"@vitejs/plugin-react": "^4.0.0",
4646
"env-cmd": "^10.1.0",
47-
"vite": "^5.4.10"
47+
"vite": "^6.0.11"
4848
}
49-
}
49+
}

nomad-front-end/src/App.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ const App = props => {
196196
}
197197
/>
198198
<Route
199-
path='/search-experiment'
199+
path='/search-experiment/:datasetName'
200200
element={
201201
import.meta.env.VITE_DATASTORE_ON === 'true' ? (
202202
<SearchExperiment />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React, { useEffect } from 'react'
2+
import { Modal, Form, Input, Checkbox, Flex, Divider, Button, Space } from 'antd'
3+
4+
const AccountSettingsModal = props => {
5+
const { authToken, fetchUserData, userSettings, onSave, setModalVisible } = props
6+
7+
const [form] = Form.useForm()
8+
9+
useEffect(() => {
10+
if (authToken) {
11+
fetchUserData(authToken)
12+
}
13+
}, [authToken])
14+
15+
useEffect(() => {
16+
form.setFieldsValue(userSettings)
17+
}, [userSettings])
18+
19+
return (
20+
<Modal title='User Account Settings' open={props.modalVisible} footer={null}>
21+
<Form
22+
form={form}
23+
onFinish={values => {
24+
onSave(authToken, values)
25+
setModalVisible(false)
26+
}}
27+
>
28+
<Form.Item label='Username' name='username' style={{ width: '200px', marginTop: '30px' }}>
29+
<Input disabled />
30+
</Form.Item>
31+
<Form.Item label='Full Name' name='fullName'>
32+
<Input />
33+
</Form.Item>
34+
<Divider> Send Status Email</Divider>
35+
<Flex justify='space-around'>
36+
<Form.Item label='Error' name='sendStatusError' valuePropName='checked'>
37+
<Checkbox />
38+
</Form.Item>
39+
<Form.Item label='Archived' name='sendStatusArchived' valuePropName='checked'>
40+
<Checkbox />
41+
</Form.Item>
42+
</Flex>
43+
<Form.Item style={{ textAlign: 'center' }}>
44+
<Space size='middle'>
45+
<Button type='primary' htmlType='submit'>
46+
Save
47+
</Button>
48+
<Button htmlType='button' onClick={() => setModalVisible(false)}>
49+
Close
50+
</Button>
51+
</Space>
52+
</Form.Item>
53+
</Form>
54+
</Modal>
55+
)
56+
}
57+
58+
export default AccountSettingsModal

nomad-front-end/src/components/NavBar/AuthAvatar/AuthAvatar.jsx

+27-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import { Avatar, Popover } from 'antd'
3-
import { UserOutlined } from '@ant-design/icons'
3+
import { UserOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons'
44
import classes from './AuthAvatar.module.css'
55

66
const AuthAvatar = props => {
@@ -33,24 +33,43 @@ const AuthAvatar = props => {
3333
<Popover
3434
placement='bottomRight'
3535
title={
36-
<>
36+
<div style={{ marginBottom: '10px' }}>
3737
Signed in as <strong>{props.username}</strong>
38-
</>
38+
</div>
3939
}
4040
content={
41-
<span className={classes.Popover} onClick={() => props.onClick(null)}>
42-
Sign out
43-
</span>
41+
<ul>
42+
<li className={classes.Popover}>
43+
<SettingOutlined />
44+
<span className={classes.Popover} onClick={() => props.setModalVisible(true)}>
45+
Account Settings
46+
</span>
47+
</li>
48+
<li className={classes.Popover}>
49+
<LogoutOutlined />
50+
<span className={classes.Popover} onClick={() => props.onClick(null)}>
51+
Sign out
52+
</span>
53+
</li>
54+
</ul>
4455
}
4556
>
46-
<Avatar size='large' className={assignedClasses.join(' ')} onClick={() => props.onClick(null)}>
57+
<Avatar
58+
size='large'
59+
className={assignedClasses.join(' ')}
60+
// onClick={() => props.onClick(null)}
61+
>
4762
{props.username[0].toUpperCase()}
4863
</Avatar>
4964
</Popover>
5065
)
5166
} else {
5267
avatarEl = (
53-
<Avatar size='large' className={assignedClasses.join(' ')} onClick={() => props.onClick(null)}>
68+
<Avatar
69+
size='large'
70+
className={assignedClasses.join(' ')}
71+
onClick={() => props.onClick(null)}
72+
>
5473
{<UserOutlined />}
5574
</Avatar>
5675
)
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,35 @@
11
.AuthAvatar {
2-
cursor: pointer;
3-
font-size: large;
4-
font-weight: 600;
2+
cursor: pointer;
3+
font-size: large;
4+
font-weight: 600;
55
}
66

77
.User {
8-
background-color: #096dd9;
8+
background-color: #096dd9;
99
}
1010
.UserA {
11-
background-color: #52c41a;
11+
background-color: #52c41a;
1212
}
1313

1414
.UserB {
15-
background-color: #5cdbd3;
15+
background-color: #5cdbd3;
1616
}
1717

1818
.Admin {
19-
background-color: #cf1322;
19+
background-color: #cf1322;
2020
}
2121

2222
.AdminB {
23-
background-color: #ffa940;
23+
background-color: #ffa940;
24+
}
25+
26+
.Popover {
27+
margin-left: 10px;
28+
margin-bottom: 10px;
2429
}
2530

2631
.Popover:hover {
27-
color: #096dd9;
28-
font-weight: 600;
29-
cursor: pointer;
32+
color: #096dd9;
33+
font-weight: 600;
34+
cursor: pointer;
3035
}

nomad-front-end/src/components/NavBar/MainMenu/MainMenu.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const MainMenu = props => {
5454
label: <span className={classes.MenuItem}>Search</span>,
5555
children: [
5656
{
57-
key: '/search-experiment',
57+
key: '/search-experiment/null',
5858
icon: <SearchOutlined style={{ fontSize: 20 }} />,
5959
label: <span className={classes.MenuItem}>Experiments</span>
6060
},

nomad-front-end/src/components/NavBar/NavBar.jsx

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react'
1+
import React, { useState } from 'react'
22
import { useNavigate, useLocation } from 'react-router-dom'
33
import { connect } from 'react-redux'
44
import { Tooltip } from 'antd'
@@ -9,15 +9,18 @@ import logoWideLight from '../../assets/logo-wide-light.png'
99
import PageHeader from './PageHeader/PageHeader'
1010
import AuthAvatar from './AuthAvatar/AuthAvatar'
1111
import MainMenu from './MainMenu/MainMenu'
12+
import AccountSettingsModal from './AccountSettingsModal/AccountSettingsModal'
1213

13-
import { openAuthModal } from '../../store/actions'
14+
import { getAccountSettings, openAuthModal, saveUserSettings } from '../../store/actions'
1415

1516
import classes from './NavBar.module.css'
1617

1718
const NavBar = props => {
1819
const location = useLocation()
1920
const navigate = useNavigate()
2021

22+
const [modalVisible, setModalVisible] = useState(false)
23+
2124
// Setting up components for left side of NavBar. Components dynamically change with state of admin sider menu.
2225
const toggleButton = props.collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />
2326
const navLeft =
@@ -65,9 +68,18 @@ const NavBar = props => {
6568
username={props.username}
6669
accessLevel={props.accessLevel}
6770
toggleAddSample={props.tglAddSample}
71+
setModalVisible={setModalVisible}
6872
/>
6973
</div>
7074
</div>
75+
<AccountSettingsModal
76+
modalVisible={modalVisible}
77+
setModalVisible={setModalVisible}
78+
authToken={props.authToken}
79+
fetchUserData={props.getUserSettings}
80+
userSettings={props.userAccountSettings}
81+
onSave={props.saveUserSettings}
82+
/>
7183
</nav>
7284
)
7385
}
@@ -76,13 +88,17 @@ const mapStateToProps = state => {
7688
return {
7789
username: state.auth.username,
7890
accessLevel: state.auth.accessLevel,
79-
manualAccess: state.auth.manualAccess
91+
manualAccess: state.auth.manualAccess,
92+
authToken: state.auth.token,
93+
userAccountSettings: state.userAccount.settings
8094
}
8195
}
8296

8397
const mapDispatchToProps = dispatch => {
8498
return {
85-
openModalHandler: () => dispatch(openAuthModal())
99+
openModalHandler: () => dispatch(openAuthModal()),
100+
getUserSettings: token => dispatch(getAccountSettings(token)),
101+
saveUserSettings: (token, data) => dispatch(saveUserSettings(token, data))
86102
}
87103
}
88104

nomad-front-end/src/components/NavBar/PageHeader/Controls/NMRiumControls.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const NMRiumControls = props => {
1818

1919
const addExperiments = () => {
2020
props.addExpsHandler()
21-
navigate('/search-experiment')
21+
navigate('/search-experiment/null')
2222
}
2323
const { dataset, token, saveHandler, accessLevel, username } = props
2424
const { molecules, spectra, correlations } = props.data.data

nomad-front-end/src/components/NavBar/PageHeader/PageHeader.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ const PageHeaderEl = props => {
233233

234234
break
235235

236-
case location.pathname === '/search-experiment':
236+
case location.pathname.includes('/search-experiment'):
237237
headerTitle = 'Search Experiments'
238238
avatarSrc = searchIcon
239239
extra = (

nomad-front-end/src/components/SearchComponents/SearchForm.jsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useState, useRef } from 'react'
2-
import { useLocation } from 'react-router-dom'
2+
import { useLocation, useParams } from 'react-router-dom'
33

44
import { connect } from 'react-redux'
55
import { Form, Input, DatePicker, Button, Select, Row, Col, Space, Tooltip, Switch } from 'antd'
@@ -45,6 +45,8 @@ const SearchForm = props => {
4545
const [form] = Form.useForm()
4646
const location = useLocation()
4747

48+
const { datasetName } = useParams()
49+
4850
const [instrumentId, setInstrumentId] = useState(null)
4951
const [groupList, setGroupList] = useState([])
5052
const [showEditor, setShowEditor] = useState(false)
@@ -77,9 +79,17 @@ const SearchForm = props => {
7779
setGrpUsr()
7880
}, [dataType])
7981

82+
//hook to search using dataset name extracted from status email link
83+
useEffect(() => {
84+
if (datasetName !== 'null') {
85+
form.setFieldValue('datasetName', datasetName)
86+
form.submit()
87+
}
88+
}, [datasetName])
89+
8090
//Effect to preserve form values. DateRange has to be in form of dayjs object.
8191
useEffect(() => {
82-
if (location.pathname === '/search-experiment') {
92+
if (location.pathname.includes('/search-experiment')) {
8393
const dateRangeNew =
8494
expSearchParams.dateRange && expSearchParams.dateRange.map(date => dayjs(date))
8595
form.setFieldsValue({ ...expSearchParams, dateRange: dateRangeNew })

nomad-front-end/src/index.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import claimReducer from './store/reducers/claim'
2424
import claimsHistoryReducer from './store/reducers/claimsHistory'
2525
import datasetsReducer from './store/reducers/datasets'
2626
import collectionsReducer from './store/reducers/collections'
27+
import userAccountReducer from './store/reducers/user-account'
2728

2829
import moment from 'moment'
2930
import momentDurationFormatSetup from 'moment-duration-format'
@@ -65,7 +66,8 @@ const rootReducer = combineReducers({
6566
claim: claimReducer,
6667
claimsHistory: claimsHistoryReducer,
6768
datasets: datasetsReducer,
68-
collections: collectionsReducer
69+
collections: collectionsReducer,
70+
userAccount: userAccountReducer
6971
})
7072

7173
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)))

0 commit comments

Comments
 (0)