Skip to content

Commit

Permalink
feat(app): Populate FileInfo page with JSON protocol metadata (#2278)
Browse files Browse the repository at this point in the history
Closes #2129
  • Loading branch information
mcous authored Sep 14, 2018
1 parent 54f679f commit 995038a
Show file tree
Hide file tree
Showing 19 changed files with 630 additions and 307 deletions.
78 changes: 60 additions & 18 deletions app/src/components/FileInfo/InformationCard.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,74 @@
// @flow
import * as React from 'react'
import {connect} from 'react-redux'
import moment from 'moment'

import {
getProtocolName,
getProtocolAuthor,
getProtocolLastUpdated,
getProtocolMethod,
getProtocolDescription,
} from '../../protocol'
import {LabeledValue} from '@opentrons/components'
import InfoSection from './InfoSection'
import {SectionContentHalf} from '../layout'
import {SectionContentHalf, CardRow} from '../layout'

import styles from './styles.css'
import type {State} from '../../types'

type Props = {
name: string,
name: ?string,
author: ?string,
lastUpdated: ?number,
method: ?string,
description: ?string,
}
const TITLE = 'Information'

export default function InformationCard (props: Props) {
const {name} = props
const INFO_TITLE = 'Information'
const DESCRIPTION_TITLE = 'Description'
const DATE_FORMAT = 'DD MMM Y, hh:mmA'

let createdBy = 'Opentrons API'
if (name.endsWith('.json')) {
createdBy = 'Protocol Designer'
}
export default connect(mapStateToProps)(InformationCard)

function InformationCard (props: Props) {
const {name, author, method, description} = props
const lastUpdated = props.lastUpdated
? moment(props.lastUpdated).format(DATE_FORMAT)
: '-'

return (
<InfoSection title={TITLE}>
<SectionContentHalf>
<LabeledValue label={'Protocol Name'} value={name} />
</SectionContentHalf>
<SectionContentHalf className={styles.align_left}>
<LabeledValue label={'Creation Method'} value={createdBy} />
</SectionContentHalf>
</InfoSection>
<React.Fragment>
<InfoSection title={INFO_TITLE}>
<CardRow>
<SectionContentHalf>
<LabeledValue label="Protocol Name" value={name || '-'} />
</SectionContentHalf>
<SectionContentHalf>
<LabeledValue label="Organization/Author" value={author || '-'} />
</SectionContentHalf>
</CardRow>
<SectionContentHalf>
<LabeledValue label="Last Updated" value={lastUpdated} />
</SectionContentHalf>
<SectionContentHalf>
<LabeledValue label="Creation Method" value={method || '-'} />
</SectionContentHalf>
</InfoSection>
{description && (
<InfoSection title={DESCRIPTION_TITLE}>
<p>{description}</p>
</InfoSection>
)}
</React.Fragment>
)
}

function mapStateToProps (state: State): Props {
return {
name: getProtocolName(state),
author: getProtocolAuthor(state),
lastUpdated: getProtocolLastUpdated(state),
method: getProtocolMethod(state),
description: getProtocolDescription(state),
}
}
4 changes: 0 additions & 4 deletions app/src/components/FileInfo/LabwareTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ import * as React from 'react'
import styles from './styles.css'

type Props = {
title: string,
children: React.Node,
}

export default function LabwareTable (props: Props) {
return (
<div>
<h3 className={styles.title}>{props.title}</h3>
<table className={styles.labware_table}>
<tbody>
<tr>
Expand All @@ -20,6 +17,5 @@ export default function LabwareTable (props: Props) {
{props.children}
</tbody>
</table>
</div>
)
}
19 changes: 12 additions & 7 deletions app/src/components/FileInfo/ProtocolLabwareCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@
import * as React from 'react'
import {connect} from 'react-redux'
import countBy from 'lodash/countBy'
import type {State} from '../../types'
import type {Labware} from '../../robot'

import {selectors as robotSelectors} from '../../robot'
import InfoSection from './InfoSection'
import LabwareTable from './LabwareTable'

import type {State} from '../../types'
import type {Labware} from '../../robot'

type Props = {
labware: Array<Labware>,
}

const TITLE = 'Required Labware'

export default connect(mapStateToProps, null)(ProtocolLabwareCard)
export default connect(mapStateToProps)(ProtocolLabwareCard)

function ProtocolLabwareCard (props: Props) {
const {labware} = props
const labwareCount = countBy(labware, 'name')

if (labware.length === 0) return null

const labwareCount = countBy(labware, 'name')
const labwareList = Object.keys(labwareCount).map(type => (
<tr key={type}>
<td>{type}</td>
Expand All @@ -28,9 +33,9 @@ function ProtocolLabwareCard (props: Props) {
))

return (
<LabwareTable title={TITLE}>
{labwareList}
</LabwareTable>
<InfoSection title={TITLE}>
<LabwareTable>{labwareList}</LabwareTable>
</InfoSection>
)
}
function mapStateToProps (state: State): Props {
Expand Down
104 changes: 52 additions & 52 deletions app/src/components/FileInfo/ProtocolModulesCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,57 @@
import * as React from 'react'
import {connect} from 'react-redux'

import type {State} from '../../types'
import type {Robot, SessionModule} from '../../robot'

import {selectors as robotSelectors} from '../../robot'
import {makeGetRobotModules, fetchModules, type FetchModulesResponse} from '../../http-api-client'
import {
makeGetRobotModules,
fetchModules,
type FetchModulesResponse,
} from '../../http-api-client'

import {RefreshWrapper} from '../Page'
import InfoSection from './InfoSection'
import {SectionContentHalf} from '../layout'
import InstrumentItem from './InstrumentItem'
import InstrumentWarning from './InstrumentWarning'

import type {State} from '../../types'
import type {Robot, SessionModule} from '../../robot'

type OP = {
robot: Robot,
}

type SP = {
modules: Array<SessionModule>,
actualModules: ?FetchModulesResponse,
_robot: ?Robot,
attachModulesUrl: string,
}

type DP = {dispatch: Dispatch}

type Props = SP & {
attachModulesUrl: string,
type DP = {
fetchModules: () => mixed,
}

type Props = OP & SP & DP

const TITLE = 'Required Modules'

export default connect(makeMapStateToProps, null, mergeProps)(ProtocolModulesCard)
export default connect(
makeMapStateToProps,
mapDispatchToProps
)(ProtocolModulesCard)

function ProtocolModulesCard (props: Props) {
const {
modules,
actualModules,
fetchModules,
attachModulesUrl,
} = props

const moduleInfo = modules.map((module) => {
let displayName = module.name === 'tempdeck'
? 'Temperature Module'
: 'Magnetic Bead Module'

const actualModel = actualModules && actualModules.modules.find((m) => m.name === module.name)
const {modules, actualModules, fetchModules, attachModulesUrl} = props

if (modules.length < 1) return null

const moduleInfo = modules.map(module => {
const displayName =
module.name === 'tempdeck' ? 'Temperature Module' : 'Magnetic Bead Module'

const actualModel =
actualModules && actualModules.modules.find(m => m.name === module.name)

const modulesMatch = actualModel != null && actualModel.name === module.name

return {
Expand All @@ -55,51 +63,43 @@ function ProtocolModulesCard (props: Props) {
}
})

const modulesMatch = moduleInfo.every((m) => m.modulesMatch)

if (modules.length < 1) return null
const modulesMatch = moduleInfo.every(m => m.modulesMatch)

return (
<RefreshWrapper
refresh={fetchModules}
>
<InfoSection title={TITLE}>
<SectionContentHalf>
{moduleInfo.map((m) => (
<InstrumentItem key={m.slot} match={m.modulesMatch}>{m.displayName} </InstrumentItem>
))}
</SectionContentHalf>
{!modulesMatch && (
<InstrumentWarning instrumentType='module' url={attachModulesUrl}/>
)}
</InfoSection>
<RefreshWrapper refresh={fetchModules}>
<InfoSection title={TITLE}>
<SectionContentHalf>
{moduleInfo.map(m => (
<InstrumentItem key={m.slot} match={m.modulesMatch}>
{m.displayName}{' '}
</InstrumentItem>
))}
</SectionContentHalf>
{!modulesMatch && (
<InstrumentWarning instrumentType="module" url={attachModulesUrl} />
)}
</InfoSection>
</RefreshWrapper>
)
}

function makeMapStateToProps (): (state: State) => SP {
function makeMapStateToProps (): (state: State, ownProps: OP) => SP {
const getActualModules = makeGetRobotModules()

return (state, props) => {
const _robot = robotSelectors.getConnectedRobot(state)
const modulesCall = _robot && getActualModules(state, _robot)
return (state, ownProps) => {
const {robot} = ownProps
const modulesCall = getActualModules(state, robot)

return {
_robot,
modules: robotSelectors.getModules(state),
actualModules: modulesCall && modulesCall.response,
attachModulesUrl: `/robots/${robot.name}/instruments`,
}
}
}

function mergeProps (stateProps: SP, dispatchProps: DP): Props {
const {dispatch} = dispatchProps
const {_robot} = stateProps
const attachModulesUrl = _robot ? `/robots/${_robot.name}/instruments` : '/robots'

function mapDispatchToProps (dispatch: Dispatch, ownProps: OP): DP {
return {
...stateProps,
attachModulesUrl,
fetchModules: () => _robot && dispatch(fetchModules(_robot)),
fetchModules: () => dispatch(fetchModules(ownProps.robot)),
}
}
Loading

0 comments on commit 995038a

Please sign in to comment.