/* 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 (
<StatusIcon status={this.props.item.status} style={{ marginRight: '8px' }} /> <TitleText>{this.props.item.task_type.replace(/_/g, ' ').toLowerCase()}</TitleText> {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