Commit cd015203 authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Merge branch 'v1-misc-fixes' into 'v1.0.0'

Misc. fixes towards v1 release.

See merge request !547
parents 9e351c50 75370d4d
Pipeline #121002 canceled with stages
in 12 minutes and 3 seconds
......@@ -29,9 +29,9 @@ import {
Table,
TableRow,
TableCell,
Link
Link,
Box
} from '@material-ui/core'
import { Link as RouterLink } from 'react-router-dom'
import { DOI } from './dataset/DOI'
import ClipboardIcon from '@material-ui/icons/Assignment'
import { CopyToClipboard } from 'react-copy-to-clipboard'
......@@ -42,6 +42,7 @@ import NoData from './visualization/NoData'
import { formatNumber, formatTimestamp, authorList, serializeMetainfo } from '../utils'
import { Unit, toUnitSystem, useUnits } from '../units'
import { filterData } from './search/FilterRegistry'
import { RouteLink } from './nav/Routes'
/**
* Component for showing a metainfo quantity value together with a name and
......@@ -175,13 +176,14 @@ const Quantity = React.memo((props) => {
return null
}
if (finalValue && Array.isArray(finalValue)) {
finalValue = finalValue.join(', ')
}
clipboardContent = clipboardContent || finalValue
if (children && children.length !== 0) {
content = children
} else if (finalValue || finalValue === 0) {
if (Array.isArray(finalValue)) {
finalValue = finalValue.join(', ')
}
clipboardContent = clipboardContent || finalValue
content = <Typography noWrap={noWrap} variant={typography} className={valueClassName}>
{finalValue}
</Typography>
......@@ -297,7 +299,7 @@ const quantityPresets = {
<div>
{data.datasets.map(ds => (
<Typography key={ds.dataset_id}>
<Link component={RouterLink} to={`/dataset/id/${ds.dataset_id}`}>{ds.dataset_name}</Link>
<RouteLink path={`dataset/id/${ds.dataset_id}`}>{ds.dataset_name}</RouteLink>
{ds.doi ? <span>&nbsp;<DOI style={{display: 'inline'}} parentheses doi={ds.doi}/></span> : ''}
</Typography>))}
</div>
......@@ -324,7 +326,14 @@ const quantityPresets = {
},
upload_id: {
noWrap: true,
withClipboard: true
withClipboard: true,
render: (data) => (
<Box flexGrow={1}>
<Typography noWrap>
<RouteLink path={`upload/id/${data.upload_id}`}>{data.upload_id}</RouteLink>
</Typography>
</Box>
)
},
last_processing_time: {
noWrap: true,
......@@ -350,7 +359,14 @@ const quantityPresets = {
},
entry_id: {
noWrap: true,
withClipboard: true
withClipboard: true,
render: (data) => (
<Box flexGrow={1}>
<Typography noWrap>
<RouteLink path={`entry/id/${data.upload_id}/${data.entry_id}`}>{data.entry_id}</RouteLink>
</Typography>
</Box>
)
},
'results.material.material_id': {
noWrap: true,
......
......@@ -20,8 +20,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import { Route } from 'react-router'
import { CacheRoute, CacheSwitch } from 'react-router-cache-route'
import { matchPath, useLocation, Redirect, useHistory } from 'react-router-dom'
import { Button, makeStyles, Tooltip } from '@material-ui/core'
import { matchPath, useLocation, Redirect, useHistory, Link as RouterLink } from 'react-router-dom'
import { Button, Link, makeStyles, Tooltip } from '@material-ui/core'
import About from '../About'
import AIToolkitPage from '../aitoolkit/AIToolkitPage'
import TutorialsPage from '../aitoolkit/TutorialsPage'
......@@ -341,6 +341,7 @@ export const routes = [
},
...datasetRoutes,
...entryRoutes,
...uploadRoutes,
{
path: 'dev/datatable',
render: () => <DatatableExamples />
......@@ -428,6 +429,17 @@ export function getUrl(path, location) {
return `${url}/${path}`
}
export const RouteLink = React.forwardRef((props, ref) => {
const {path, children, ...moreProps} = props
const location = useLocation()
const to = getUrl(path, location)
return <Link component={RouterLink} to={to} {...moreProps}>{children}</Link>
})
RouteLink.propTypes = {
path: PropTypes.string.isRequired,
children: PropTypes.node
}
export const RouteButton = React.forwardRef((props, ref) => {
const {component, onClick, path, ...moreProps} = props
const location = useLocation()
......
......@@ -26,7 +26,6 @@ import {
ButtonBase,
Tooltip
} from '@material-ui/core'
import InputCheckbox from './InputCheckbox'
import InputHeader from './InputHeader'
import AspectRatio from '../../visualization/AspectRatio'
import { makeStyles } from '@material-ui/core/styles'
......@@ -367,13 +366,13 @@ const InputPeriodicTable = React.memo(({
</tbody>
</table>
</AspectRatio>
<div className={styles.formContainer}>
{/* <div className={styles.formContainer}>
<InputCheckbox
quantity="exclusive"
label="only composition that exclusively contain these atoms"
description="Search for entries with compositions that only (exclusively) contain the selected atoms. The default is to return all entries that have at least (inclusively) the selected atoms."
></InputCheckbox>
</div>
</div> */}
</div>
</div>
// eslint-disable-next-line react-hooks/exhaustive-deps
......
......@@ -18,7 +18,7 @@
import React, {useCallback, useContext, useMemo, useState, useReducer} from 'react'
import {
makeStyles, DialogTitle, DialogContent, Dialog, IconButton, Tooltip,
Box, Divider, TextField, MenuItem, Select, Typography, FormControl, InputLabel
Box, Divider, TextField, MenuItem, Select, Typography, FormControl, InputLabel, CircularProgress
} from '@material-ui/core'
import DialogContentText from '@material-ui/core/DialogContentText'
import MembersIcon from '@material-ui/icons/People'
......@@ -34,6 +34,124 @@ import DeleteIcon from '@material-ui/icons/Delete'
export const editMembersDialogContext = React.createContext()
const useInviteUserDialogStyles = makeStyles(theme => ({
button: {
marginLeft: theme.spacing(1)
},
dialog: {
width: '100%'
},
submitWrapper: {
margin: theme.spacing(1),
position: 'relative'
},
submitProgress: {
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -12,
marginLeft: -12
}
}))
const InviteUserDialog = React.memo(function InviteUserDialog(props) {
const classes = useInviteUserDialogStyles()
const [open, setOpen] = useState(false)
const [submitting, setSubmitting] = useState(false)
const [canSubmit, setCanSubmit] = useState(false)
const [error, setError] = useState(null)
const [data, setData] = useState({
first_name: '',
last_name: '',
email: '',
affiliation: ''
})
const {api} = useApi()
const handleClose = useCallback(() => {
setOpen(false)
}, [setOpen])
const handleSubmit = useCallback(() => {
setSubmitting(true)
api.inviteUser(data).then(() => {
setSubmitting(false)
setOpen(false)
}).catch(error => {
let message = '' + error
try {
message = JSON.parse(error.request.responseText).detail || '' + error
} catch (e) {}
setError(message)
setSubmitting(false)
setCanSubmit(false)
})
}, [data, setSubmitting, setOpen, setError, api])
const handleChange = useCallback((key, value) => {
const valid = value && !Object.keys(data).find(dataKey => !(key === dataKey || data[dataKey]))
setData({...data, [key]: value})
setCanSubmit(valid)
}, [setData, data, setCanSubmit])
const handleOpen = useCallback(() => {
setOpen(true)
}, [setOpen])
const input = (key, label) => <TextField
variant="filled"
label={label}
value={data[key]}
onChange={event => handleChange(key, event.target.value)}
margin="normal"
fullWidth
/>
return <React.Fragment>
<Button className={classes.button}
onClick={handleOpen}
color="secondary" disabled={submitting}
>
Invite new user
</Button>
<Dialog
classes={{paper: classes.dialog}}
open={open}
onClose={handleClose} disableBackdropClick disableEscapeKeyDown>
<DialogTitle>Invite a new user to NOMAD</DialogTitle>
<DialogContent>
<DialogContentText>
If you want to add a user as co-author or share your data with someone that
is not already a NOMAD user, you can invite this person here. We need just a few
details about this person. After your invite, the new user will receive an
Email that allows her to set a password and further details. Anyhow, you will
be able to add the user as co-author or someone to share with immediately after the
invite.
</DialogContentText>
{error && <DialogContentText color="error">
{error}
</DialogContentText>}
{input('email', 'Email')}
{input('first_name', 'First name')}
{input('last_name', 'Last name')}
{input('affiliation', 'Affiliation')}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} disabled={submitting}>
Cancel
</Button>
<div className={classes.submitWrapper}>
<Button onClick={handleSubmit} color="primary" disabled={!canSubmit}>
Submit
</Button>
{submitting && <CircularProgress size={24} className={classes.submitProgress} />}
</div>
</DialogActions>
</Dialog>
</React.Fragment>
})
InviteUserDialog.propTypes = {}
const useStyles = makeStyles(theme => ({
dialog: {
width: '100%'
......@@ -307,6 +425,7 @@ function EditMembersDialog({...props}) {
<MembersTable />
</DialogContent>
<DialogActions>
<InviteUserDialog />
<span style={{flexGrow: 1}} />
<Button onClick={onConfirm} color="secondary">
Cancel
......
......@@ -44,24 +44,7 @@ export const commentContext = React.createContext()
export const referencesContext = React.createContext()
export const datasetsContext = React.createContext()
const useStyles = makeStyles(theme => ({
dialog: {
width: '100%'
},
comment: {
width: '100%'
},
datasets: {
display: 'flex',
justifyContent: 'left',
flexWrap: 'wrap',
listStyle: 'none',
padding: 'initial'
}
}))
function EditComments() {
const classes = useStyles()
const {data, setComment, setIsCommentChanged} = useContext(editMetaDataDialogContext)
const [newComment, setNewComment] = useState('')
const [defaultComment, setDefaultComment] = useState('')
......@@ -88,7 +71,7 @@ function EditComments() {
Comments
</Typography>
</Box>
<TextField className={classes.comment} id='outlined-multiline-static' label='Comment'
<TextField fullWidth id='outlined-multiline-static' label='Comment'
multiline rows={6} value={newComment} variant='filled' size='small'
onChange={handleTextFieldChange}
/>
......@@ -501,9 +484,15 @@ DatasetsActions.propTypes = {
data: PropTypes.object.isRequired
}
const useEditMetaDataDialogStyles = makeStyles(theme => ({
dialog: {
width: '100%'
}
}))
function EditMetaDataDialog({...props}) {
const {isIcon, selectedEntries} = props
const classes = useStyles()
const classes = useEditMetaDataDialogStyles()
const {api} = useApi()
const {raiseError} = useErrors()
const {upload, setUpload, data} = useContext(uploadPageContext)
......
......@@ -284,6 +284,11 @@ class Keycloak():
try:
self._admin_client.create_user(keycloak_user)
except KeycloakGetError as e:
try:
return json.loads(e.response_body)['errorMessage']
except Exception:
return str(e)
except Exception as e:
return str(e)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment