/*
Copyright (C) 2017 Cloudbase Solutions SRL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
// @flow
import React from 'react'
import { observer } from 'mobx-react'
import styled, { css } from 'styled-components'
import { Collapse } from 'react-collapse'
import type { Task } from '../../../types/Task'
import StatusIcon from '../../atoms/StatusIcon'
import Arrow from '../../atoms/Arrow'
import StatusPill from '../../atoms/StatusPill'
import CopyValue from '../../atoms/CopyValue'
import ProgressBar from '../../atoms/ProgressBar'
import CopyButton from '../../atoms/CopyButton'
import notificationStore from '../../../stores/NotificationStore'
import DomUtils from '../../../utils/DomUtils'
import Palette from '../../styleUtils/Palette'
import StyleProps from '../../styleUtils/StyleProps'
import DateUtils from '../../../utils/DateUtils'
const Wrapper = styled.div`
cursor: pointer;
border-bottom: 1px solid white;
transition: all ${StyleProps.animations.swift};
${props => props.open ? `background: ${Palette.grayscale[0]};` : ''}
&:hover {
background: ${Palette.grayscale[0]};
}
`
const ArrowStyled = styled(Arrow)`
position: absolute;
left: -24px;
`
const Header = styled.div`
display: flex;
padding: 8px;
position: relative;
&:hover ${ArrowStyled} {
opacity: 1;
}
`
const HeaderData = styled.div`
display: block;
${props => props.capitalize ? 'text-transform: capitalize;' : ''}
width: ${props => props.width};
color: ${props => props.black ? Palette.black : Palette.grayscale[4]};
padding-right: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
position: relative;
`
const Title = styled.div`
display: flex;
`
const TitleText = styled.div`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`
const Body = styled.div`
display: flex;
flex-direction: column;
padding: 24px 8px;
`
const Row = styled.div`
display: flex;
margin-bottom: 24px;
&:last-child {
margin-bottom: 0;
}
`
const RowData = styled.div`
${props => props.width ? css`width: ${props.width};` : ''}
&:first-child {
padding-left: 24px;
${props => css`width: calc(${props.width} - 24px);`}
}
`
const Label = styled.div`
text-transform: uppercase;
font-size: 10px;
font-weight: ${StyleProps.fontWeights.medium};
color: ${Palette.grayscale[5]};
margin-bottom: 4px;
`
const Value = styled.div`
${props => props.width ? css`width: ${props.width};` : ''}
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
${props => props.primary ? css`color: ${Palette.primary};` : ''}
&:hover {
${props => props.primaryOnHover ? css`color: ${Palette.primary};` : ''}
}
`
const ExceptionText = styled.div`
cursor: pointer;
&:hover > span {
opacity: 1;
}
> span {
background-position-y: 4px;
margin-left: 4px;
}
`
const ProgressUpdates = styled.div`
color: ${Palette.black};
`
const ProgressUpdate = styled.div`
display: flex;
color: ${props => props.secondary ? Palette.grayscale[5] : 'inherit'};
`
const ProgressUpdateDate = styled.div`
min-width: ${props => props.width || 'auto'};
& > span {margin-left: 24px;}
`
const ProgressUpdateValue = styled.div`
width: 100%;
margin-right: 32px;
`
type Props = {
columnWidths: string[],
item: Task,
open: boolean,
onDependsOnClick: (id: string) => void,
}
@observer
class TaskItem extends React.Component {
getLastMessage() {
let message
if (this.props.item.progress_updates.length) {
message = this.props.item.progress_updates[0].message
} else {
message = '-'
}
return message
}
getMessageProgress(message: string) {
let match = message.match(/.*progress.*?(100|\d{1,2})%/)
return match && match[1]
}
handleExceptionTextClick(exceptionText: string) {
let succesful = DomUtils.copyTextToClipboard(exceptionText)
if (succesful) {
notificationStore.alert('The message has been copied to clipboard.')
}
}
renderHeader() {
let date = this.props.item.updated_at ? this.props.item.updated_at : this.props.item.created_at
return (
{this.props.item.task_type.replace(/_/g, ' ').toLowerCase()}
{this.props.item.instance}
{this.getLastMessage()}
{date ? DateUtils.getLocalTime(date).format('YYYY-MM-DD HH:mm:ss') : '-'}
)
}
renderDependsOnValue() {
if (this.props.item.depends_on && this.props.item.depends_on.length > 0 && this.props.item.depends_on[0]) {
return (
{ e.stopPropagation(); this.props.onDependsOnClick(this.props.item.depends_on[0]) }}
onMouseDown={e => { e.stopPropagation() }}
onMouseUp={e => { e.stopPropagation() }}
>{this.props.item.depends_on[0]})
}
return N/A
}
renderProgressUpdates() {
let naValue = N/A
if (!this.props.item.progress_updates.length) {
return naValue
}
return (
{this.props.item.progress_updates.map((update, i) => {
if (!update) {
return N/A
}
let messageProgress = this.getMessageProgress(update.message)
return (
{DateUtils.getLocalTime(update.created_at).format('YYYY-MM-DD HH:mm:ss')}
{update.message}
{messageProgress && }
)
})}
)
}
renderExceptionDetails() {
let exceptionsText = (this.props.item.exception_details && this.props.item.exception_details.length
&& this.props.item.exception_details)
let valueField
if (!exceptionsText) {
valueField = N/A
} else {
valueField = (
{ e.stopPropagation(); this.handleExceptionTextClick(exceptionsText) }}
onMouseDown={e => { e.stopPropagation() }}
onMouseUp={e => { e.stopPropagation() }}
>{exceptionsText})
}
return valueField
}
renderBody() {
return (
{this.renderDependsOnValue()}
{this.renderExceptionDetails()}
{this.renderProgressUpdates()}
)
}
render() {
return (
{this.renderHeader()}
{this.renderBody()}
)
}
}
export default TaskItem