Skip to content

Commit

Permalink
OpenConceptLab/ocl_issues#1348 | Source create new form
Browse files Browse the repository at this point in the history
  • Loading branch information
snyaggarwal committed Sep 2, 2022
1 parent 6115822 commit cdc003d
Show file tree
Hide file tree
Showing 20 changed files with 1,001 additions and 39 deletions.
12 changes: 10 additions & 2 deletions src/common/utils.js
Expand Up @@ -223,9 +223,17 @@ const handleLookupValuesResponse = (data, callback, attr) => {
callback(orderBy(uniqBy(map(data, cc => ({id: get(cc, _attr), name: get(cc, _attr)})), 'name')), 'name');
}

export const fetchLocales = callback => {
export const fetchLocales = (callback, includeRawName=false) => {
APIService.orgs('OCL').sources('Locales').appendToUrl('concepts/lookup/').get(null, null, {verbose: true}).then(response => {
callback(orderBy(map(response.data, l => ({id: l.id, name: `${l.display_name} [${l.id}]`, uuid: l.uuid})), 'name'));
const mapper = locale => {
let data = {id: locale.id, name: `${locale.display_name} [${locale.id}]`, uuid: locale.uuid}
if(includeRawName) {
data.name = locale.display_name
data.displayName = `${locale.display_name} [${locale.id}]`
}
return data
}
callback(orderBy(map(response.data, mapper), 'displayName'));
});
}

Expand Down
3 changes: 3 additions & 0 deletions src/components/app/App.scss
Expand Up @@ -1166,3 +1166,6 @@ div.custom-attributes-accordion-content {
font-size: 12px
}
}
.form-text-gray {
color: rgba(0, 0, 0, 0.6);
}
27 changes: 27 additions & 0 deletions src/components/common/CommonAccordion.jsx
@@ -0,0 +1,27 @@
import React from 'react';
import { Accordion, AccordionSummary, AccordionDetails, Typography } from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';


const CommonAccordion = ({title, subTitle, children, defaultStyle, ...accordionProps}) => {
return (
<div className='col-xs-12 no-side-padding'>
<Accordion style={defaultStyle ? {} : {borderTop: "1px solid rgba(0, 0, 0, 0.12)"}} {...(accordionProps || {})}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<div>
<Typography>{title}</Typography>
{
subTitle &&
<div className='form-text-gray'>{subTitle}</div>
}
</div>
</AccordionSummary>
<AccordionDetails style={{background: '#fafafa', display: 'inline-block', width: '100%'}}>
{children}
</AccordionDetails>
</Accordion>
</div>
)
}

export default CommonAccordion;
7 changes: 6 additions & 1 deletion src/components/common/ConceptContainerForm.jsx
Expand Up @@ -230,6 +230,11 @@ class ConceptContainerForm extends React.Component {
this.setFieldValue('selected_supported_locales', items)
}

onDefaultLocaleChange = (id, item) => {
this.setFieldValue(id, get(item, 'id', ''))
this.setFieldValue('selected_default_locale', item)
}

setFieldValue(id, value, setObject=false) {
const newState = {...this.state}
set(newState, id, value)
Expand Down Expand Up @@ -553,7 +558,7 @@ class ConceptContainerForm extends React.Component {
<LocaleAutoComplete
id="fields.default_locale"
label="Default Locale"
onChange={this.onAutoCompleteChange}
onChange={this.onDefaultLocaleChange}
required
selected={selected_default_locale}
error={Boolean(fieldErrors.default_locale)}
Expand Down
19 changes: 10 additions & 9 deletions src/components/common/ExtrasForm.jsx
Expand Up @@ -8,34 +8,35 @@ const ExtrasForm = ({ extra, index, onChange, onDelete }) => {
const value = get(extra, 'value', '');

return (
<div className='col-md-12' style={{border: '1px solid lightgray', borderRadius: '4px', paddingBottom: '15px', width: '100%'}}>
<div className='col-md-12 no-side-padding' style={{marginTop: '15px', width: '100%'}}>
<div className='col-md-5 no-left-padding'>
<div className='col-xs-12 no-side-padding'>
<div className='col-xs-12 no-side-padding flex-vertical-center' style={{marginTop: '15px', width: '100%'}}>
<div className='col-xs-5 no-left-padding'>
<TextField
id={`extras.${index}.key`}
label='Attribute Name'
label='Attribute name'
placeholder='Custom-name'
variant="outlined"
fullWidth
onChange={event => onChange(index, event.target.value, '__')}
size='small'
value={get(extra, 'key', '')}
/>
</div>
<div className='col-md-6 no-left-padding'>
<div className='col-xs-6 no-side-padding'>
<TextField
id={`extras.${index}.value`}
label='Value'
variant="outlined"
fullWidth
onChange={event => onChange(index, '__', jsonifySafe(event.target.value))}
size='small'
multiline
rows={2}
value={isObject(value) ? JSON.stringify(value) : value}
/>
</div>
<div className='col-md-1 no-left-padding' style={{textAlign: 'right', minWidth: '8.33%', width: '8.33%'}}>
<IconButton onClick={() => onDelete(index)} size="large"><DeleteIcon /></IconButton>
<div className='col-xs-1 no-left-padding' style={{textAlign: 'right', minWidth: '8.33%', width: '8.33%'}}>
<IconButton onClick={() => onDelete(index)} size="medium">
<DeleteIcon fontSize='inherit' />
</IconButton>
</div>
</div>
</div>
Expand Down
13 changes: 13 additions & 0 deletions src/components/common/FormTooltip.jsx
@@ -0,0 +1,13 @@
import React from 'react';
import { Tooltip } from '@mui/material';
import { InfoOutlined as Icon } from '@mui/icons-material';

const FormTooltip = ({ title, placement, style }) => (
<span style={{display: 'flex', ...(style || {})}}>
<Tooltip title={title} placement={placement || 'right'}>
<Icon color='default' />
</Tooltip>
</span>
)

export default FormTooltip;
57 changes: 36 additions & 21 deletions src/components/common/LocaleAutoComplete.jsx
@@ -1,47 +1,62 @@
import React from 'react';
import { TextField } from '@mui/material';
import Autocomplete from '@mui/material/Autocomplete';
import { TextField, Box, Divider, Autocomplete } from '@mui/material';
import { get, isEmpty } from 'lodash'
import { fetchLocales } from '../../common/utils';

const LocaleAutoComplete = ({ id, selected, multiple, required, onChange, label, error, size, ...rest }) => {
const LocaleAutoComplete = ({ id, selected, multiple, required, onChange, label, error, size, fullWidth, ...rest }) => {
const [locales, setLocales] = React.useState([])
const _fullWidth = !(fullWidth === false)

React.useEffect(() => fetchLocales(_locales => setLocales(_locales)), [])
React.useEffect(() => fetchLocales(_locales => setLocales(_locales), true), [])

return (
<Autocomplete
openOnFocus
fullWidth
fullWidth={_fullWidth}
blurOnSelect={!multiple}
disableCloseOnSelect={Boolean(multiple)}
multiple={Boolean(multiple)}
required={required}
isOptionEqualToValue={(option, value) => option.uuid === get(value, 'uuid')}
isOptionEqualToValue={(option, value) => get(value, 'uuid') ? (option.uuid === get(value, 'uuid')) : (option.id === get(value, 'id'))}
defaultValue={isEmpty(selected) ? (multiple ? [] : '') : selected}
value={selected}
id={id || 'localesAutoComplete'}
options={locales}
getOptionLabel={option => (option.name || '')}
getOptionLabel={option => (option.displayName || '')}
onChange={(event, item) => onChange(id || 'localesAutoComplete', item)}
renderOption={(props, option) => {
return (
<React.Fragment key={option.id + option.name}>
<Box component='li' {...props}>
<span style={{minWidth: '55px'}} className='form-text-gray'>
{option.id}
</span>
<span>
{option.name}
</span>
</Box>
<Divider style={{width: '100%'}}/>
</React.Fragment>
)
}}
renderInput={
params => (
<TextField
{...params}
size={size || 'medium'}
required={required}
label={label}
error={error}
variant="outlined"
fullWidth
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
size={size || 'medium'}
required={required}
label={label}
error={error}
variant="outlined"
fullWidth={_fullWidth}
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)
}
Expand Down
69 changes: 69 additions & 0 deletions src/components/common/OwnerSelectorButton.jsx
@@ -0,0 +1,69 @@
import React from 'react';
import { Button, Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material';
import { ArrowDropDown as DownIcon } from '@mui/icons-material';
import { map } from 'lodash';
import APIService from '../../services/APIService';
import { getCurrentUser } from '../../common/utils';
import { ORANGE, WHITE } from '../../common/constants';
import DynamicConfigResourceIcon from './DynamicConfigResourceIcon';

const OwnerSelectorButton = props => {
const getDefaultOwner = () => {
let _owner = {type: props.owner.type, url: props.owner.url}
if(_owner.type === 'User')
_owner.username = props.owner.username
else
_owner.id = props.owner.id
return _owner
}
const [anchorEl, setAnchorEl] = React.useState(null);
const [selected, setSelected] = React.useState(getDefaultOwner)
const [owners, setOwners] = React.useState([])
const user = getCurrentUser()
const getOwners = () => APIService
.user()
.orgs()
.get()
.then(response => setOwners(
[...map(response.data, org => ({id: org.id, type: org.type, url: org.url})), {username: user.username, type: 'User', url: user.url}]
))

React.useEffect(() => !props.disabled && getOwners(), [])

const onToggle = event => setAnchorEl(anchorEl ? null : event.currentTarget)

const onSelect = owner => {
setSelected(owner)
setAnchorEl(null)

props.onChange(owner)
}

return (
<React.Fragment>
<Button
startIcon={<DynamicConfigResourceIcon resource={selected.type.toLowerCase()} />}
endIcon={<DownIcon />}
variant='contained'
style={{backgroundColor: ORANGE, color: WHITE, ...(props.style || {})}}
onClick={props.disabled ? () => {} : onToggle}
>
{selected.username || selected.id}
</Button>
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={onToggle}>
{
map(owners, (owner, index) => (
<MenuItem key={index} onClick={() => onSelect(owner)}>
<ListItemIcon>
<DynamicConfigResourceIcon resource={owner.type.toLowerCase()} />
</ListItemIcon>
<ListItemText>{owner.username || owner.id}</ListItemText>
</MenuItem>
))
}
</Menu>
</React.Fragment>
)
}

export default OwnerSelectorButton;
9 changes: 6 additions & 3 deletions src/components/common/RTEditor.jsx
Expand Up @@ -22,9 +22,12 @@ class RTEditor extends React.Component {
const { label, placeholder } = this.props
return (
<React.Fragment>
<div className='col-md-12'>
<h3>{label}</h3>
</div>
{
label &&
<div className='col-md-12'>
<h3>{label}</h3>
</div>
}
<div className='col-md-12 no-side-padding'>
<ReactQuill
theme="snow"
Expand Down
2 changes: 1 addition & 1 deletion src/components/orgs/HomeTabs.jsx
Expand Up @@ -132,7 +132,7 @@ const HomeTabs = props => {
isOpen={sourceForm}
onClose={() => setSourceForm(false)}
formComponent={
<SourceForm onCancel={() => setSourceForm(false)} reloadOnSuccess={tab==1} parentURL={url} />
<SourceForm onCancel={() => setSourceForm(false)} reloadOnSuccess={tab==1} parentURL={url} owner={org} />
}
/>
<CommonFormDrawer
Expand Down
26 changes: 26 additions & 0 deletions src/components/sources/forms/AboutPage.jsx
@@ -0,0 +1,26 @@
import React from 'react';
import CommonAccordion from '../../common/CommonAccordion';
import RTEditor from '../../common/RTEditor';


const AboutPage = props => {
const configs = props.advanceSettings.about
const [text, setText] = React.useState('')
const onChange = value => {
setText(value)
props.onChange({text: value})
}
return (
<CommonAccordion square title={configs.title} subTitle={configs.subTitle}>
<div className='col-xs-12 no-side-padding' style={{marginTop: '10px'}}>
<RTEditor
onChange={onChange}
defaultValue={text}
placeholder={`About ${props.resource}`}
/>
</div>
</CommonAccordion>
)
}

export default AboutPage;
22 changes: 22 additions & 0 deletions src/components/sources/forms/AdvanceSettings.jsx
@@ -0,0 +1,22 @@
import React from 'react';
import FHIRSettings from './FHIRSettings';
import ResourceIDAssignmentSettings from './ResourceIDAssignmentSettings'
import CustomAttributes from './CustomAttributes'
import AboutPage from './AboutPage'

const AdvanceSettings = props => {
const configs = props.advanceSettings
return (
<div className='col-xs-12 no-side-padding'>
<div className='col-xs-12 no-side-padding'>
<h2>{configs.title}</h2>
</div>
<FHIRSettings {...props} />
<ResourceIDAssignmentSettings {...props} />
<CustomAttributes {...props} />
<AboutPage {...props} />
</div>
)
}

export default AdvanceSettings

0 comments on commit cdc003d

Please sign in to comment.