fe: add routing + proto incident hist page
This commit is contained in:
parent
a0b6c8f01d
commit
d66aa77aa7
10 changed files with 125 additions and 15 deletions
34
priv/frontend/package-lock.json
generated
34
priv/frontend/package-lock.json
generated
|
@ -34,6 +34,18 @@
|
|||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"@reach/router": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@reach/router/-/router-1.1.1.tgz",
|
||||
"integrity": "sha1-JKWyDxzJ5V4svNxFT7gslNtHmoE=",
|
||||
"requires": {
|
||||
"create-react-context": "^0.2.1",
|
||||
"invariant": "^2.2.3",
|
||||
"prop-types": "^15.6.1",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"warning": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"abab": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
|
||||
|
@ -2258,6 +2270,15 @@
|
|||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"create-react-context": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.2.tgz",
|
||||
"integrity": "sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==",
|
||||
"requires": {
|
||||
"fbjs": "^0.8.0",
|
||||
"gud": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||
|
@ -4645,6 +4666,11 @@
|
|||
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz",
|
||||
|
@ -10838,6 +10864,14 @@
|
|||
"makeerror": "1.0.x"
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
|
||||
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"watch": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"@fortawesome/fontawesome-svg-core": "^1.2.0-14",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.1.0-11",
|
||||
"@fortawesome/react-fontawesome": "0.1.0-11",
|
||||
"@reach/router": "^1.1.1",
|
||||
"classnames": "^2.2.6",
|
||||
"ms": "^2.1.1",
|
||||
"nanoevents": "^1.0.5",
|
||||
|
|
15
priv/frontend/src/components/App.js
Normal file
15
priv/frontend/src/components/App.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React from 'react'
|
||||
|
||||
import { Router } from '@reach/router'
|
||||
|
||||
import Dashboard from './Dashboard'
|
||||
import Incidents from './Incidents'
|
||||
|
||||
export default function App () {
|
||||
return (
|
||||
<Router>
|
||||
<Dashboard path="/"/>
|
||||
<Incidents path="/incidents/"/>
|
||||
</Router>
|
||||
)
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import React, { Component } from 'react'
|
||||
|
||||
import './Dashboard.css'
|
||||
import Service from './Service.js'
|
||||
import ServicePlaceholder from './ServicePlaceholder.js'
|
||||
import DegradedNotice from './DegradedNotice.js'
|
||||
import StreamingClient from '../ws/client'
|
||||
import Status from './Status'
|
||||
import Page from './Page'
|
||||
import { log, objectFromEntries, strictFetch } from '../util.js'
|
||||
import { domain as DOMAIN } from '../config.json'
|
||||
|
||||
|
@ -164,10 +164,10 @@ export default class Dashboard extends Component {
|
|||
|
||||
render () {
|
||||
return (
|
||||
<div className="dashboard">
|
||||
<Page>
|
||||
<h1>elstatus</h1>
|
||||
{this.renderDashboardContent()}
|
||||
</div>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,13 @@ const OUTAGE_TYPES = {
|
|||
|
||||
export default function Incident ({ incident }) {
|
||||
const { content, end_date: end, start_date: start, title, type, stages } = incident
|
||||
|
||||
const startDate = new Date(start * 1000)
|
||||
const endDate = end ? new Date(end * 1000) : null
|
||||
const tooltip = `Started ${startDate.toLocaleString()}` +
|
||||
(endDate ? `, ended ${endDate.toLocaleString()}` : '')
|
||||
const ago = ms(Date.now() - start * 1000)
|
||||
const agoEnd = end ? ms(Date.now - end * 1000) : null
|
||||
const agoEnd = end ? ms(Date.now() - end * 1000) : null
|
||||
|
||||
const stageNodes = stages.map(
|
||||
(stage) => <Stage key={stage.title} stage={stage}/>
|
||||
|
@ -25,7 +30,7 @@ export default function Incident ({ incident }) {
|
|||
<div className="incident">
|
||||
<h2>{OUTAGE_TYPES[type] || type}: {title}</h2>
|
||||
<p>{content}</p>
|
||||
<footer>
|
||||
<footer title={tooltip}>
|
||||
Started {ago} ago
|
||||
{end ? `, ended ${agoEnd} ago` : null}
|
||||
</footer>
|
||||
|
|
41
priv/frontend/src/components/Incidents.js
Normal file
41
priv/frontend/src/components/Incidents.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import React from 'react'
|
||||
|
||||
import Page from './Page'
|
||||
import Incident from './Incident'
|
||||
import { strictFetch } from '../util'
|
||||
import { domain as DOMAIN } from '../config.json'
|
||||
|
||||
export default class Incidents extends React.Component {
|
||||
state = {
|
||||
incidents: null,
|
||||
page: 0,
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.fetchIncidents()
|
||||
}
|
||||
|
||||
async fetchIncidents () {
|
||||
const resp = await strictFetch(`${DOMAIN}/api/incidents/${this.state.page}`)
|
||||
this.setState({ incidents: await resp.json() })
|
||||
}
|
||||
|
||||
renderIncidents () {
|
||||
if (!this.state.incidents) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.state.incidents.map(
|
||||
(incident) => <Incident key={incident.id} incident={incident}/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Page>
|
||||
<h1>Incidents</h1>
|
||||
{this.renderIncidents()}
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.dashboard {
|
||||
.page {
|
||||
max-width: 40em;
|
||||
margin: 0 auto;
|
||||
}
|
16
priv/frontend/src/components/Page.js
Normal file
16
priv/frontend/src/components/Page.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import './Page.css'
|
||||
|
||||
export default function Page ({ children }) {
|
||||
return (
|
||||
<div className="page">
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Page.propTypes = {
|
||||
children: PropTypes.any,
|
||||
}
|
|
@ -6,16 +6,14 @@ import './Status.css'
|
|||
import Incident from './Incident'
|
||||
|
||||
export default function Status ({ incident }) {
|
||||
let view = null
|
||||
if (incident) {
|
||||
view = <Incident incident={incident}/>
|
||||
} else {
|
||||
view = 'All systems operational'
|
||||
}
|
||||
const incidentOngoing = incident && incident.ongoing === 1
|
||||
const view = incidentOngoing
|
||||
? <Incident incident={incident}/>
|
||||
: 'All systems operational'
|
||||
|
||||
const className = classnames(
|
||||
'status',
|
||||
incident == null ? 'status-good' : 'status-bad',
|
||||
incidentOngoing ? 'status-bad' : 'status-good',
|
||||
)
|
||||
return (
|
||||
<div className={className}>
|
||||
|
|
|
@ -2,12 +2,12 @@ import React from 'react'
|
|||
import ReactDOM from 'react-dom'
|
||||
|
||||
import './index.css'
|
||||
import Dashboard from './components/Dashboard'
|
||||
import App from './components/App'
|
||||
import register from './icons.js'
|
||||
|
||||
register()
|
||||
|
||||
ReactDOM.render(
|
||||
<Dashboard/>,
|
||||
<App/>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue