Skip to content

Commit

Permalink
concept home | versions | links | copy icon
Browse files Browse the repository at this point in the history
  • Loading branch information
snyaggarwal committed Nov 13, 2020
1 parent 41f570a commit 589cbde
Show file tree
Hide file tree
Showing 12 changed files with 224 additions and 49 deletions.
8 changes: 8 additions & 0 deletions src/common/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*eslint no-process-env: 0*/
import alertifyjs from 'alertifyjs';
import moment from 'moment';
import { filter, difference } from 'lodash';
import { DATE_FORMAT, DATETIME_FORMAT } from './constants';
Expand Down Expand Up @@ -81,3 +82,10 @@ export const toFullAPIURL = uri => {
const APIURL = window.API_URL || process.env.API_URL;
return APIURL + uri;
}

export const copyURL = url => {
if(url) {
navigator.clipboard.writeText(url);
alertifyjs.success('Copied URL to clipboard!')
}
}
24 changes: 8 additions & 16 deletions src/components/app/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,43 +39,35 @@ class App extends Component {
}
<Switch>
<Route
exact
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
path="/users/:user([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
<Route
exact
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)/:conceptVersion([a-zA-Z0-9\-\.\_]+)"
path="/orgs/:org([a-zA-Z0-9\-\.\_]+)/sources/:source([a-zA-Z0-9\-\.\_]+)/:version([a-zA-Z0-9\-\.\_]+)/concepts/:concept([a-zA-Z0-9\-\.\_]+)"
component={ConceptHome}
/>
</Switch>
Expand Down
4 changes: 4 additions & 0 deletions src/components/app/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ td.pagination-center {
.home-icon {
width: 48px;
color: #e5e5e5;
cursor: pointer;
&.concept {
color: rgb(51, 115, 170)
}
svg {
font-size: 32pt;
width: 48px;
Expand Down
3 changes: 2 additions & 1 deletion src/components/common/ConceptButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { Button } from '@material-ui/core';
import { LocalOffer as LocalOfferIcon } from '@material-ui/icons';
import { BLUE, WHITE, RED, BLACK } from '../../common/constants';

const ConceptButton = ({label, onClick, retired}) => {
const ConceptButton = ({label, onClick, retired, href}) => {
const style = retired ?
{background: 'lightgray', color: RED, boxShadow: 'none', textDecoration: 'line-through', textDecorationColor: BLACK} :
{background: BLUE, color: WHITE, boxShadow: 'none'};
return (
<Button
href={href}
variant='contained'
startIcon={<LocalOfferIcon />}
onClick={onClick}
Expand Down
16 changes: 12 additions & 4 deletions src/components/concepts/ConceptDetailsLocale.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import React from 'react';
import { Flag as FlagIcon } from '@material-ui/icons';
import { Chip, Tooltip } from '@material-ui/core';
import { Flag as FlagIcon, FileCopy as CopyIcon } from '@material-ui/icons';
import { Chip, Tooltip, IconButton } from '@material-ui/core';
import { get } from 'lodash';
import ExternalIdLabel from '../common/ExternalIdLabel';
import { toFullAPIURL, copyURL } from '../../common/utils';


const ConceptDetailsLocale = ({ locale, isDescription }) => {
const ConceptDetailsLocale = ({ locale, isDescription, url }) => {
const typeAttr = isDescription ? 'description_type' : 'name_type'
const nameAttr = isDescription ? 'description' : 'name'
const type = get(locale, typeAttr);
const onCopyClick = () => copyURL(toFullAPIURL(url))

return (
<div className='col-md-12' style={{marginBottom: '10px'}}>
<div className='col-md-12 flex-vertical-center' style={{marginBottom: '10px'}}>
<div className='col-md-11 no-left-padding'>
<div className='col-md-12 no-side-padding flex-vertical-center'>
<span style={{marginRight: '10px'}}>{ get(locale, nameAttr) }</span>
Expand All @@ -36,6 +39,11 @@ const ConceptDetailsLocale = ({ locale, isDescription }) => {
}
</div>
<div className='col-md-1 no-right-padding'>
<Tooltip title='Copy Link' placement='right'>
<IconButton onClick={onCopyClick} color='primary' size='small'>
<CopyIcon fontSize='inherit' />
</IconButton>
</Tooltip>
</div>
</div>
);
Expand Down
72 changes: 64 additions & 8 deletions src/components/concepts/ConceptHome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ class ConceptHome extends React.Component {
this.state = {
isLoading: true,
concept: {},
tab: 0,
versions: [],
tab: this.getDefaultTabIndex(),
}
}

Expand All @@ -19,36 +20,91 @@ class ConceptHome extends React.Component {
}

componentDidUpdate(prevProps) {
if(prevProps.location.pathname !== this.props.location.pathname)
if(prevProps.location.pathname !== this.props.location.pathname) {
this.refreshDataByURL()
this.onTabChange(null, this.getDefaultTabIndex())
}
}

getDefaultTabIndex() {
const { location } = this.props;

if(location.pathname.indexOf('/mappings') > -1)
return 1;
if(location.pathname.indexOf('/history') > -1)
return 2;

return 0;
}

getConceptURLFromPath() {
const { location, match } = this.props;
if(location.pathname.indexOf('/history') > -1)
return location.pathname.split('/history')[0] + '/'
if(location.pathname.indexOf('/mappings') > -1)
return location.pathname.split('/mappings')[0] + '/'
if(match.params.conceptVersion)
return location.pathname.split('/').slice(0, 8).join('/') + '/';
return this.getVersionedObjectURLFromPath();
}

getVersionedObjectURLFromPath() {
const { location } = this.props;

return location.pathname.split('/').slice(0, 7).join('/') + '/';
}

refreshDataByURL() {
this.setState({isLoading: true}, () => {
APIService.new()
.overrideURL(this.props.location.pathname)
.overrideURL(this.getConceptURLFromPath())
.get(null, null, {includeInverseMappings: true})
.then(response => {
this.setState({isLoading: false, concept: response.data})
this.setState({isLoading: false, concept: response.data}, () => {
if(this.state.tab === 2)
this.getVersions()
})
})

})
}

getVersions() {
APIService.new()
.overrideURL(this.getVersionedObjectURLFromPath() + 'versions/')
.get()
.then(response => {
this.setState({versions: response.data})
})
}

onTabChange = (event, value) => {
this.setState({tab: value})
this.setState({tab: value}, () => {
if(value === 2)
this.getVersions()
})
}

isVersionedObject() {
return !this.props.match.params.conceptVersion
}

render() {
const { concept, isLoading, tab } = this.state;
const { concept, versions, isLoading, tab } = this.state;
const currentURL = this.getConceptURLFromPath()
return (
<div style={isLoading ? {textAlign: 'center', marginTop: '40px'} : {}}>
{
isLoading ?
<CircularProgress color='primary' /> :
<div className='col-md-12 home-container no-side-padding'>
<ConceptHomeHeader concept={concept} />
<ConceptHomeTabs tab={tab} onChange={this.onTabChange} concept={concept} />
<ConceptHomeHeader
concept={concept}
isVersionedObject={this.isVersionedObject()}
versionedObjectURL={this.getVersionedObjectURLFromPath()}
currentURL={currentURL}
/>
<ConceptHomeTabs tab={tab} onChange={this.onTabChange} concept={concept} versions={versions} currentURL={currentURL} />
</div>
}
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/components/concepts/ConceptHomeDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ const ACCORDIAN_HEADING_STYLES = {
fontWeight: 'bold',
}
const ACCORDIAN_DETAILS_STYLES = {
maxHeight: '300px', overflow: 'scroll',
maxHeight: '300px', overflow: 'scroll', display: 'inline-block', width: '100%',
}

const None = () => {
return <div style={{margin: '5px', fontWeight: '300'}}>None</div>
}

const ConceptHomeDetails = ({ concept }) => {
const ConceptHomeDetails = ({ concept, currentURL }) => {
const directMappings = getDirectMappings(concept.mappings, concept.id);
const indirectMappings = getIndirectMappings(concept.mappings, concept.id);
const names = get(concept, 'names', [])
Expand All @@ -41,7 +41,7 @@ const ConceptHomeDetails = ({ concept }) => {
isEmpty(names) ?
None() :
map(names, name => (
<ConceptDetailsLocale locale={name} key={name.uuid} />
<ConceptDetailsLocale locale={name} key={name.uuid} url={`${currentURL}names/${name.uuid}/`} />
))
}
</AccordionDetails>
Expand All @@ -59,7 +59,7 @@ const ConceptHomeDetails = ({ concept }) => {
isEmpty(descriptions) ?
None() :
map(descriptions, description => (
<ConceptDetailsLocale locale={description} isDescription={true} key={description.uuid} />
<ConceptDetailsLocale locale={description} isDescription={true} key={description.uuid} url={`${currentURL}descriptions/${description.uuid}/`} />
))
}
</AccordionDetails>
Expand Down
21 changes: 17 additions & 4 deletions src/components/concepts/ConceptHomeHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,40 @@ import React from 'react';
import {
LocalOffer as LocalOfferIcon
} from '@material-ui/icons';
import { Tooltip } from '@material-ui/core';
import { toFullAPIURL, copyURL } from '../../common/utils';
import OwnerButton from '../common/OwnerButton';
import SourceButton from '../common/SourceButton';
import ConceptButton from '../common/ConceptButton';
import LastUpdatedOnLabel from '../common/LastUpdatedOnLabel';
import ExternalIdLabel from '../common/ExternalIdLabel';

const ConceptHomeHeader = ({concept}) => {
const ConceptHomeHeader = ({concept, isVersionedObject, versionedObjectURL, currentURL}) => {
const isRetired = concept.retired;
const onIconClick = () => {
copyURL(toFullAPIURL(currentURL))
}

return (
<header className='home-header col-md-12'>
<div className='col-md-12 container' style={{paddingTop: '10px'}}>
<div className='no-side-padding col-md-1 home-icon'>
<LocalOfferIcon />
<div className='no-side-padding col-md-1 home-icon concept'>
<Tooltip title='Copy URL'>
<LocalOfferIcon onClick={onIconClick} />
</Tooltip>
</div>
<div className='col-md-11'>
<div className='col-md-12 no-side-padding flex-vertical-center'>
<OwnerButton {...concept} onClick={() => {}} />
<span className='separator'>/</span>
<SourceButton label={concept.source} onClick={() => {}} />
<span className='separator'>/</span>
<ConceptButton label={concept.id} retired={isRetired} onClick={() => {}} />
<ConceptButton label={concept.id} retired={isRetired} href={`#${versionedObjectURL}`} />

{
!isVersionedObject &&
<span style={{marginLeft: '10px'}}>[{concept.version}]</span>
}
</div>
<div className='col-md-12 no-side-padding flex-vertical-center' style={{paddingTop: '5px'}}>
<span style={{marginRight: '10px'}} className={isRetired ? 'retired': ''}>
Expand Down
6 changes: 3 additions & 3 deletions src/components/concepts/ConceptHomeMappings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const ACCORDIAN_HEADING_STYLES = {
fontWeight: 'bold',
}
const ACCORDIAN_DETAILS_STYLES = {
maxHeight: '300px', overflow: 'scroll', display: 'inline-block'
maxHeight: '300px', overflow: 'scroll', display: 'inline-block', width: '100%'
}

const None = () => {
Expand All @@ -36,15 +36,15 @@ const ConceptHomeMappings = ({ concept }) => {
isEmpty(mappingsDistribution) ?
None() :
map(mappingsDistribution, (mappings, category) => (
<div className='col-md-12 no-side-padding'>
<div className='col-md-12 no-side-padding' key={category}>
<div className='col-md-12'>
<div className='col-md-2 no-left-padding text-muted small' style={{paddingTop: '15px'}}>
{category}
</div>
<div className='col-md-10 no-right-padding'>
{
map(mappings, (mapping, index) => (
<div className='col-md-12 no-side-padding'>
<div className='col-md-12 no-side-padding' key={index}>
<Mapping key={mapping.uuid} {...mapping} conceptContext={concept.id} />
{
(index + 1) < mappings.length &&
Expand Down
7 changes: 4 additions & 3 deletions src/components/concepts/ConceptHomeTabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import React from 'react';
import { Tabs, Tab } from '@material-ui/core';
import ConceptHomeDetails from './ConceptHomeDetails';
import ConceptHomeMappings from './ConceptHomeMappings';
import ConceptHomeVersions from './ConceptHomeVersions';

const ConceptHomeTabs = props => {
const { tab, onChange, concept } = props;
const { tab, onChange, concept, versions, currentURL } = props;
return (
<div className='col-md-12 sub-tab'>
<Tabs className='sub-tab-header' value={tab} onChange={onChange} aria-label="concept-home-tabs" indicatorColor='none'>
Expand All @@ -13,9 +14,9 @@ const ConceptHomeTabs = props => {
<Tab label="History" />
</Tabs>
<div className='sub-tab-container' style={{display: 'flex'}}>
{ tab === 0 && <ConceptHomeDetails concept={concept} /> }
{ tab === 0 && <ConceptHomeDetails concept={concept} currentURL={currentURL} /> }
{ tab === 1 && <ConceptHomeMappings concept={concept} /> }
{ tab === 2 && <span>History</span> }
{ tab === 2 && <ConceptHomeVersions versions={versions} /> }
</div>
</div>
);
Expand Down
Loading

0 comments on commit 589cbde

Please sign in to comment.