Skip to content

Commit

Permalink
Merge pull request #1021 from HubSpot/reactify-racks-and-slaves-pages
Browse files Browse the repository at this point in the history
Reactify racks and slaves pages
  • Loading branch information
Tom Petr committed Jun 7, 2016
2 parents 7639355 + 0b6a46b commit 75f4294
Show file tree
Hide file tree
Showing 14 changed files with 607 additions and 349 deletions.
Expand Up @@ -18,10 +18,16 @@ TimeStamp = React.createClass
timeObject = moment @props.prop.timestamp
return <div className={@props.prop.className}>{ timeObject.format window.config.timestampFormat }</div>

duration: ->
return <div className={@props.prop.className}>{ moment.duration(@props.prop.timestamp).humanize() }</div>

render: ->
# Feel free to add more options if you need them
if @props.prop.display is 'timeStampFromNow'
return @timeStampFromNow()
else if @props.prop.display is 'absoluteTimestamp'
return @absoluteTimestamp()
else if @props.prop.display is 'duration'
return @duration()

module.exports = TimeStamp
49 changes: 49 additions & 0 deletions SingularityUI/app/components/machines/MachinesPage.cjsx
@@ -0,0 +1,49 @@
React = require 'react'
Table = require '../common/Table'

MachinesPage = React.createClass

propTypes:
header: React.PropTypes.string.isRequired # Eg. 'Slaves', 'Racks'
states: React.PropTypes.arrayOf(React.PropTypes.shape({
stateName: React.PropTypes.string.isRequired # Eg. 'Active', 'Frozen', etc
stateTableColumnMetadata: React.PropTypes.arrayOf(React.PropTypes.shape({
data: React.PropTypes.string
className: React.PropTypes.string
doSort: React.PropTypes.func
sortable: React.PropTypes.boolean
sortAttr: React.PropTypes.string
})).isRequired
# Host info, in a format that can be passed into a table
hostsInState: React.PropTypes.arrayOf(React.PropTypes.shape({
dataId: React.PropTypes.string.isRequired
className: React.PropTypes.string
data: React.PropTypes.arrayOf(React.PropTypes.shape({
component: React.PropTypes.func.isRequired
prop: React.PropTypes.object
id: React.PropTypes.string
className: React.PropTypes.string
})).isRequired
})).isRequired
emptyTableMessage: React.PropTypes.string.isRequired
})).isRequired

renderState: (state, key) ->
<div key={key}>
<h2> {state.stateName} </h2>
<Table
noPages = {true}
tableClassOpts = "table-striped"
columnHeads = {state.stateTableColumnMetadata}
tableRows = {state.hostsInState}
emptyTableMessage = {state.emptyTableMessage}
/>
</div>

render: ->
<div>
<h1> {@props.header} </h1>
{@props.states.map @renderState}
</div>

module.exports = MachinesPage
208 changes: 208 additions & 0 deletions SingularityUI/app/components/machines/Racks.cjsx
@@ -0,0 +1,208 @@
React = require 'react'
MachinesPage = require './MachinesPage'
PlainText = require '../common/atomicDisplayItems/PlainText'
TimeStamp = require '../common/atomicDisplayItems/TimeStamp'
Link = require '../common/atomicDisplayItems/Link'
Glyphicon = require '../common/atomicDisplayItems/Glyphicon'
Utils = require '../../utils'
RacksCollection = require '../../collections/Racks'

Racks = React.createClass

typeName: {
'active': 'Activated By'
'frozen': 'Frozen By'
'decommissioning': 'Decommissioned By'
}

showUser: (rack) ->
rack.state in ['ACTIVE', 'DECOMMISSIONING', 'DECOMMISSIONED', 'STARTING_DECOMMISSION']

columnHeads: (type) ->
heads = [
{
data: 'ID'
},
{
data: 'Current State'
},
{
data: 'Uptime'
}
]
if @typeName[type]
heads.push {
data: @typeName[type]
}
heads.push { data: 'Message' }
heads.push {} # Reactivate button and Decommission or Remove button
heads

refresh: () -> @props.racks.fetch().done => @forceUpdate()

promptReactivate: (event, rackModel) ->
event.preventDefault()
rackModel.promptReactivate () => @refresh()

promptDecommission: (event, rackModel) ->
event.preventDefault()
rackModel.promptDecommission () => @refresh()

promptRemove: (event, rackModel) ->
event.preventDefault()
rackModel.promptRemove () => @refresh()

getMaybeReactivateButton: (rackModel) ->
rack = rackModel.attributes
if rack.state in ['DECOMMISSIONING', 'DECOMMISSIONED', 'STARTING_DECOMMISSION']
<Link
prop = {{
text: <Glyphicon
iconClass = 'new-window'
/>
onClickFn: (event) => @promptReactivate event, rackModel
title: 'Reactivate'
altText: "Reactivate #{rack.id}"
overlayTrigger: true
overlayTriggerPlacement: 'top'
overlayToolTipContent: "Reactivate #{rack.id}"
overlayId: "reactivate#{rack.id}"
}}
/>
else
return null

getDecommissionOrRemoveButton: (rackModel) ->
rack = rackModel.attributes
if rack.state is 'ACTIVE'
<Link
prop = {{
text: <Glyphicon
iconClass = 'trash'
/>
onClickFn: (event) => @promptDecommission event, rackModel
title: 'Decommission'
altText: "Decommission #{rack.id}"
overlayTrigger: true
overlayTriggerPlacement: 'top'
overlayToolTipContent: "Decommission #{rack.id}"
overlayId: "decommission#{rack.id}"
}}
/>
else
<Link
prop = {{
text: <Glyphicon
iconClass = 'remove'
/>
onClickFn: (event) => @promptRemove event, rackModel
title: 'Remove'
altText: "Remove #{rack.id}"
overlayTrigger: true
overlayTriggerPlacement: 'top'
overlayToolTipContent: "Remove #{rack.id}"
overlayId: "remove#{rack.id}"
}}
/>

getData: (type, rackModel) ->
rack = rackModel.attributes
data = [
{
component: PlainText
prop: {
text: rack.id
}
},
{
component: PlainText
prop: {
text: Utils.humanizeText rack.state
}
},
{
component: TimeStamp
prop: {
display: 'duration'
timestamp: rack.uptime
}
}
]
if @typeName[type]
data.push {
component: PlainText
prop: {
text: if @showUser(rack) and rack.user then rack.user else ''
}
}
data.push {
component: PlainText
prop: {
text: rack.currentState.message or ''
}
}
data.push {
component: PlainText
className: 'actions-column'
prop: {
text: <div>{@getMaybeReactivateButton rackModel} {@getDecommissionOrRemoveButton rackModel} </div>
}
}
data

getRacks: (type, racks) ->
tableifiedRacks = []
racks.map (rack) =>
tableifiedRacks.push {
dataId: rack.id
data: @getData type, rack
}
tableifiedRacks

getActiveRacks: ->
return new RacksCollection(
@props.racks.filter (model) ->
model.get('state') in ['ACTIVE']
)

getDecommissioningRacks: ->
return new RacksCollection(
@props.racks.filter (model) ->
model.get('state') in ['DECOMMISSIONING', 'DECOMMISSIONED', 'STARTING_DECOMMISSION']
)

getInactiveRacks: ->
return new RacksCollection(
@props.racks.filter (model) ->
model.get('state') in ['DEAD', 'MISSING_ON_STARTUP']
)

getStates: ->
[
{
stateName: "Active"
emptyTableMessage: "No Active Racks"
stateTableColumnMetadata: @columnHeads 'active'
hostsInState: @getRacks 'active', @getActiveRacks()
},
{
stateName: "Decommissioning"
emptyTableMessage: "No Decommissioning Racks"
stateTableColumnMetadata: @columnHeads 'decommissioning'
hostsInState: @getRacks 'decommissioning', @getDecommissioningRacks()
},
{
stateName: "Inactive"
emptyTableMessage: "No Inactive Racks"
stateTableColumnMetadata: @columnHeads 'inactive'
hostsInState: @getRacks 'inactive', @getInactiveRacks()
}
]

render: ->
<MachinesPage
header = "Racks"
states = {@getStates()}
/>

module.exports = Racks

0 comments on commit 75f4294

Please sign in to comment.