|
@@ -19,22 +19,69 @@ import { observer } from 'mobx-react'
|
|
|
import styled, { injectGlobal } from 'styled-components'
|
|
import styled, { injectGlobal } from 'styled-components'
|
|
|
import NotificationSystem from 'react-notification-system'
|
|
import NotificationSystem from 'react-notification-system'
|
|
|
import { observe } from 'mobx'
|
|
import { observe } from 'mobx'
|
|
|
|
|
+import type { AxiosXHRConfig } from 'axios'
|
|
|
|
|
|
|
|
import notificationStore from '../../../stores/NotificationStore'
|
|
import notificationStore from '../../../stores/NotificationStore'
|
|
|
-import type { AlertInfo } from '../../../types/NotificationItem'
|
|
|
|
|
|
|
|
|
|
|
|
+import CopyMultilineValue from '../../atoms/CopyMultilineValue'
|
|
|
|
|
+import Button from '../../atoms/Button'
|
|
|
|
|
+import Modal from '../../molecules/Modal'
|
|
|
|
|
+
|
|
|
|
|
+import StyleProps from '../../styleUtils/StyleProps'
|
|
|
|
|
+import Palette from '../../styleUtils/Palette'
|
|
|
import NotificationsStyle from './style.js'
|
|
import NotificationsStyle from './style.js'
|
|
|
|
|
+import DomUtils from '../../../utils/DomUtils'
|
|
|
|
|
+
|
|
|
|
|
+import type { AlertInfo } from '../../../types/NotificationItem'
|
|
|
|
|
|
|
|
injectGlobal`
|
|
injectGlobal`
|
|
|
${NotificationsStyle}
|
|
${NotificationsStyle}
|
|
|
`
|
|
`
|
|
|
|
|
|
|
|
const Wrapper = styled.div``
|
|
const Wrapper = styled.div``
|
|
|
|
|
+const ErrorInfoWrapper = styled.div`
|
|
|
|
|
+ margin: 32px;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
+`
|
|
|
|
|
+const ErrorInfoRequest = styled.div``
|
|
|
|
|
+const ErrorInfoRequestItem = styled.div`
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+`
|
|
|
|
|
+const ErrorInfoRequestLabel = styled.div`
|
|
|
|
|
+ font-size: 10px;
|
|
|
|
|
+ font-weight: ${StyleProps.fontWeights.medium};
|
|
|
|
|
+ color: ${Palette.grayscale[3]};
|
|
|
|
|
+ text-transform: uppercase;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+`
|
|
|
|
|
+const ErrorInfoRequestData = styled.pre`
|
|
|
|
|
+ word-break: break-word;
|
|
|
|
|
+ white-space: pre-wrap;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ .key { color: #053997; }
|
|
|
|
|
+ .number { color: #107947; }
|
|
|
|
|
+ .string { color: #92000C; }
|
|
|
|
|
+ .boolean { color: #0000FF; }
|
|
|
|
|
+ .null { color: #000A5D; }
|
|
|
|
|
+`
|
|
|
|
|
+const ButtonWrapper = styled.div`
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ margin-bottom: 32px;
|
|
|
|
|
+`
|
|
|
|
|
+
|
|
|
|
|
+type State = {
|
|
|
|
|
+ errorInfo: ?{ error: { message: ?string, status: ?string }, request: AxiosXHRConfig<any> },
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const MAX_NOTIFICATIONS = 3
|
|
const MAX_NOTIFICATIONS = 3
|
|
|
|
|
|
|
|
@observer
|
|
@observer
|
|
|
-class Notifications extends React.Component<{}> {
|
|
|
|
|
|
|
+class Notifications extends React.Component<{}, State> {
|
|
|
|
|
+ state = {
|
|
|
|
|
+ errorInfo: null,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
notificationSystem: NotificationSystem
|
|
notificationSystem: NotificationSystem
|
|
|
notificationsCount = 0
|
|
notificationsCount = 0
|
|
|
activeNotifications: any[] = []
|
|
activeNotifications: any[] = []
|
|
@@ -49,15 +96,27 @@ class Notifications extends React.Component<{}> {
|
|
|
if (!alerts.length || alerts.length <= this.notificationsCount) {
|
|
if (!alerts.length || alerts.length <= this.notificationsCount) {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
let lastNotification = alerts[alerts.length - 1]
|
|
let lastNotification = alerts[alerts.length - 1]
|
|
|
|
|
+ let action = lastNotification.options ? lastNotification.options.action : null
|
|
|
|
|
+ let autoDismiss = lastNotification.message.length < 150 ? 10 : 30
|
|
|
|
|
+ if (action && lastNotification.level === 'error') {
|
|
|
|
|
+ let errorInfo = action.callback()
|
|
|
|
|
+ action = {
|
|
|
|
|
+ ...action,
|
|
|
|
|
+ callback: () => {
|
|
|
|
|
+ this.setState({ errorInfo })
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+ autoDismiss = 0
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
this.notificationSystem.addNotification({
|
|
this.notificationSystem.addNotification({
|
|
|
title: lastNotification.title || lastNotification.message,
|
|
title: lastNotification.title || lastNotification.message,
|
|
|
message: lastNotification.title ? lastNotification.message : null,
|
|
message: lastNotification.title ? lastNotification.message : null,
|
|
|
level: lastNotification.level || 'info',
|
|
level: lastNotification.level || 'info',
|
|
|
position: 'br',
|
|
position: 'br',
|
|
|
- autoDismiss: lastNotification.message.length < 150 ? 10 : 30,
|
|
|
|
|
- action: lastNotification.options ? lastNotification.options.action : null,
|
|
|
|
|
|
|
+ autoDismiss,
|
|
|
|
|
+ action,
|
|
|
onAdd: notification => {
|
|
onAdd: notification => {
|
|
|
this.activeNotifications.push(notification)
|
|
this.activeNotifications.push(notification)
|
|
|
for (let i = 0; i < this.activeNotifications.length - MAX_NOTIFICATIONS; i += 1) {
|
|
for (let i = 0; i < this.activeNotifications.length - MAX_NOTIFICATIONS; i += 1) {
|
|
@@ -73,9 +132,55 @@ class Notifications extends React.Component<{}> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
render() {
|
|
|
|
|
+ let error = this.state.errorInfo
|
|
|
|
|
+ let jsonData = error && error.request.data
|
|
|
|
|
+ try {
|
|
|
|
|
+ jsonData = JSON.stringify(jsonData, null, 2)
|
|
|
|
|
+ jsonData = DomUtils.jsonSyntaxHighlight(jsonData)
|
|
|
|
|
+ // eslint-disable-next-line no-empty
|
|
|
|
|
+ } catch (err) { }
|
|
|
return (
|
|
return (
|
|
|
<Wrapper>
|
|
<Wrapper>
|
|
|
<NotificationSystem ref={(n) => { this.notificationSystem = n }} />
|
|
<NotificationSystem ref={(n) => { this.notificationSystem = n }} />
|
|
|
|
|
+ {error ? (
|
|
|
|
|
+ <Modal
|
|
|
|
|
+ title="Error Details"
|
|
|
|
|
+ isOpen
|
|
|
|
|
+ onRequestClose={() => { this.setState({ errorInfo: null }) }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <ErrorInfoWrapper>
|
|
|
|
|
+ <ErrorInfoRequest>
|
|
|
|
|
+ <ErrorInfoRequestItem>
|
|
|
|
|
+ <ErrorInfoRequestLabel>Request URL</ErrorInfoRequestLabel>
|
|
|
|
|
+ <CopyMultilineValue value={error.request.url} />
|
|
|
|
|
+ </ErrorInfoRequestItem>
|
|
|
|
|
+ <ErrorInfoRequestItem>
|
|
|
|
|
+ <ErrorInfoRequestLabel>Request Method</ErrorInfoRequestLabel>
|
|
|
|
|
+ <CopyMultilineValue value={error.request.method || 'GET'} />
|
|
|
|
|
+ </ErrorInfoRequestItem>
|
|
|
|
|
+ {error.request.data ? (
|
|
|
|
|
+ <ErrorInfoRequestItem>
|
|
|
|
|
+ <ErrorInfoRequestLabel>Request Data</ErrorInfoRequestLabel>
|
|
|
|
|
+ <ErrorInfoRequestData dangerouslySetInnerHTML={{ __html: jsonData }} />
|
|
|
|
|
+ </ErrorInfoRequestItem>
|
|
|
|
|
+ ) : null}
|
|
|
|
|
+ <ErrorInfoRequestItem>
|
|
|
|
|
+ <ErrorInfoRequestLabel>Response Status</ErrorInfoRequestLabel>
|
|
|
|
|
+ <CopyMultilineValue value={error.error.status || '-'} />
|
|
|
|
|
+ </ErrorInfoRequestItem>
|
|
|
|
|
+ {error.error.message ? (
|
|
|
|
|
+ <ErrorInfoRequestItem>
|
|
|
|
|
+ <ErrorInfoRequestLabel>Response Message</ErrorInfoRequestLabel>
|
|
|
|
|
+ <CopyMultilineValue value={error.error.message} />
|
|
|
|
|
+ </ErrorInfoRequestItem>
|
|
|
|
|
+ ) : null}
|
|
|
|
|
+ </ErrorInfoRequest>
|
|
|
|
|
+ </ErrorInfoWrapper>
|
|
|
|
|
+ <ButtonWrapper>
|
|
|
|
|
+ <Button secondary onClick={() => { this.setState({ errorInfo: null }) }}>Dismiss</Button>
|
|
|
|
|
+ </ButtonWrapper>
|
|
|
|
|
+ </Modal>
|
|
|
|
|
+ ) : null}
|
|
|
</Wrapper>
|
|
</Wrapper>
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|