Explorar o código

Add support for new execution / tasks API statuses

Includes support the full list of statuses like: 'CANCELLING',
'DEADLOCKED', 'SCHEDULED' etc.
Sergiu Miclea %!s(int64=6) %!d(string=hai) anos
pai
achega
2e1c1c379f

+ 56 - 28
src/components/atoms/StatusIcon/StatusIcon.jsx

@@ -25,7 +25,7 @@ import errorImage from './images/error.svg'
 import progressWithBackgroundImage from './images/progress-background.svg'
 import progressImage from './images/progress.js'
 import successImage from './images/success.svg'
-import warningImage from './images/warning.svg'
+import warningImage from './images/warning.js'
 import pendingImage from './images/pending.svg'
 import successHollowImage from './images/success-hollow.svg'
 import errorHollowImage from './images/error-hollow.svg'
@@ -37,37 +37,68 @@ type Props = {
   secondary?: boolean,
 }
 
-const getRunningImageUrl = (props: Props) => {
-  const smallCircleColor = props.secondary ? Palette.grayscale[0] : Palette.primary
+const getSpinnerUrl = (smallCircleColor: string) => {
+  return css`url('data:image/svg+xml;utf8,${encodeURIComponent(progressImage(Palette.grayscale[3], smallCircleColor))}')`
+}
 
+const getRunningImageUrl = (props: Props) => {
   if (props.useBackground) {
     return css`url('${progressWithBackgroundImage}')`
   }
 
-  return css`url('data:image/svg+xml;utf8,${encodeURIComponent(progressImage(Palette.grayscale[3], smallCircleColor))}')`
+  const smallCircleColor = props.secondary ? Palette.grayscale[0] : Palette.primary
+  return getSpinnerUrl(smallCircleColor)
+}
+
+const getWarningUrl = (background: string) => {
+  return css`url('data:image/svg+xml;utf8,${encodeURIComponent(warningImage(background))}')`
 }
 
-const statuses = props => {
-  return {
-    COMPLETED: css`
+const statuses = (status, props) => {
+  switch (status) {
+    case 'COMPLETED':
+      return css`
       background-image: url('${props.hollow ? successHollowImage : successImage}');
-    `,
-    RUNNING: css`
-      background-image: ${getRunningImageUrl(props)};
-      ${StyleProps.animations.rotation}
-    `,
-    ERROR: css`
-      background-image: url('${props.hollow ? errorHollowImage : errorImage}');
-    `,
-    WARNING: css`
-      background-image: url('${warningImage}');
-    `,
-    CANCELED: css`
-      background-image: url('${warningImage}');
-    `,
-    PENDING: css`
-      background-image: url('${pendingImage}');
-    `,
+    `
+    case 'RUNNING':
+    case 'PENDING':
+      return css`
+        background-image: ${getRunningImageUrl(props)};
+        ${StyleProps.animations.rotation}
+      `
+    case 'CANCELLING':
+    case 'CANCELLING_AFTER_COMPLETION':
+      return css`
+        background-image: ${getSpinnerUrl(Palette.warning)};
+        ${StyleProps.animations.rotation}
+      `
+    case 'SCHEDULED':
+      return css`
+        background-image: url('${pendingImage}');
+      `
+    case 'ERROR':
+      return css`
+        background-image: url('${props.hollow ? errorHollowImage : errorImage}');
+      `
+    case 'WARNING':
+    case 'CANCELED':
+    case 'CANCELED_AFTER_COMPLETION':
+    case 'CANCELED_FOR_DEBUGGING':
+    case 'FORCE_CANCELED':
+      return css`
+        background-image: ${getWarningUrl(Palette.warning)};
+      `
+    case 'DEADLOCKED':
+    case 'STRANDED_AFTER_DEADLOCK':
+      return css`
+        background-image: ${getWarningUrl('#424242')};
+      `
+    case 'UNSCHEDULED':
+      return css`
+        background-image: ${getWarningUrl(Palette.grayscale[2])};
+      `
+    default:
+      return null
   }
 }
 
@@ -77,16 +108,13 @@ const Wrapper = styled.div`
   height: 16px;
   background-repeat: no-repeat;
   background-position: center;
-  ${props => statuses(props)[props.status]}
+  ${props => statuses(props.status, props)}
 `
 
 @observer
 class StatusIcon extends React.Component<Props> {
   render() {
     let status = this.props.status
-    if (status === 'CANCELED_FOR_DEBUGGING') {
-      status = 'ERROR'
-    }
     return (
       <Wrapper {...this.props} status={status} />
     )

+ 4 - 6
src/components/atoms/StatusIcon/images/warning.svg → src/components/atoms/StatusIcon/images/warning.js

@@ -1,15 +1,12 @@
-<?xml version="1.0" encoding="UTF-8"?>
+// @flow
+export default (background: string) => `
 <svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
-
-    <desc>Created with Sketch.</desc>
-    <defs></defs>
     <g id="Coriolis" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
         <g id="202-Replica-Executions" transform="translate(-392.000000, -871.000000)">
             <g id="Group-2" transform="translate(360.000000, 240.000000)">
                 <g id="Group-3" transform="translate(0.000000, 79.000000)">
                     <g id="Icon/Warning" transform="translate(32.000000, 552.000000)">
-                        <circle id="Oval-2" fill="#FFDC00" fill-rule="evenodd" cx="8" cy="8" r="8"></circle>
+                        <circle fill="${background}" fill-rule="evenodd" cx="8" cy="8" r="8"></circle>
                         <path d="M8,8 L8,4" id="Line-Copy" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></path>
                         <path d="M8,12.5 C8.69035594,12.5 9.25,11.9403559 9.25,11.25 C9.25,10.5596441 8.69035594,10 8,10 C7.30964406,10 6.75,10.5596441 6.75,11.25 C6.75,11.9403559 7.30964406,12.5 8,12.5 Z" id="Oval-3" fill="#FFFFFF" fill-rule="evenodd"></path>
                     </g>
@@ -18,3 +15,4 @@
         </g>
     </g>
 </svg>
+`

+ 38 - 17
src/components/atoms/StatusIcon/story.jsx

@@ -12,32 +12,53 @@ You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+// @flow
+
 import React from 'react'
+import styled from 'styled-components'
 import { storiesOf } from '@storybook/react'
 import StatusIcon from '.'
 
+const Wrapper = styled.div`
+  display: flex;
+  flex-wrap: wrap;
+`
+
+const STATUSES = [
+  'SCHEDULED',
+  'UNSCHEDULED',
+  'COMPLETED',
+  'RUNNING',
+  'PENDING',
+  'CANCELLING',
+  'CANCELLING_AFTER_COMPLETION',
+  'CANCELED',
+  'CANCELED_AFTER_COMPLETION',
+  'CANCELED_FOR_DEBUGGING',
+  'FORCE_CANCELED',
+  'WARNING',
+  'ERROR',
+  'DEADLOCKED',
+  'STRANDED_AFTER_DEADLOCK',
+]
+
 storiesOf('StatusIcon', module)
-  .add('completed', () => (
-    <StatusIcon status="COMPLETED" />
+  .add('all statuses', () => (
+    <Wrapper>
+      {STATUSES.map(status => (
+        <span style={{ marginLeft: '16px', marginBottom: '16px' }}>
+          {status}
+          <StatusIcon
+            key={status}
+            status={status}
+          />
+        </span>
+      ))}
+    </Wrapper>
   ))
   .add('completed hollow', () => (
     <StatusIcon status="COMPLETED" hollow />
   ))
-  .add('running', () => (
-    <StatusIcon status="RUNNING" />
-  ))
-  .add('error', () => (
-    <StatusIcon status="ERROR" />
-  ))
   .add('error hollow', () => (
     <StatusIcon status="ERROR" hollow />
   ))
-  .add('warning', () => (
-    <StatusIcon status="WARNING" />
-  ))
-  .add('canceled', () => (
-    <StatusIcon status="CANCELED" />
-  ))
-  .add('pending', () => (
-    <StatusIcon status="PENDING" />
-  ))

+ 83 - 37
src/components/atoms/StatusPill/StatusPill.jsx

@@ -21,39 +21,88 @@ import styled, { css } from 'styled-components'
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
 import runningImage from './images/running.svg'
+import cancellingImage from './images/cancelling.svg'
 
-const statuses = {
-  COMPLETED: css`
-    background: ${Palette.success};
-    color: white;
-    border-color: transparent;
-  `,
-  ERROR: css`
-    background: ${Palette.alert};
-    color: white;
-    border-color: transparent;
-  `,
-  CANCELED: css`
-    background: ${Palette.warning};
-    color: ${Palette.black};
-    border-color: transparent;
-  `,
-  PAUSED: css`
-    background: white;
-    color: ${Palette.primary};
-    border-color: ${Palette.primary};
-  `,
-  RUNNING: css`
-    background: url('${runningImage}');
-    animation: bgMotion 1s infinite linear;
-    color: white;
-    border-color: transparent;
-    @keyframes bgMotion {
-      0% { background-position: -12px -1px; }
-      100% { background-position: 0 -1px; }
-    }
-  `,
-  INFO: css``,
+const LABEL_MAP: { [string]: string } = {
+  CANCELED_FOR_DEBUGGING: 'DEBUG',
+  FORCE_CANCELED: 'CANCELED',
+  STRANDED_AFTER_DEADLOCK: 'DEADLOCKED',
+  CANCELED_AFTER_COMPLETION: 'CANCELED',
+  CANCELLING_AFTER_COMPLETION: 'CANCELLING',
+}
+
+const statuses = status => {
+  switch (status) {
+    case 'COMPLETED':
+      return css`
+        background: ${Palette.success};
+        color: white;
+        border-color: transparent;
+      `
+    case 'ERROR':
+      return css`
+        background: ${Palette.alert};
+        color: white;
+        border-color: transparent;
+      `
+    case 'CANCELED':
+    case 'CANCELED_FOR_DEBUGGING':
+    case 'CANCELED_AFTER_COMPLETION':
+    case 'FORCE_CANCELED':
+      return css`
+        background: ${Palette.warning};
+        color: ${Palette.black};
+        border-color: transparent;
+      `
+    case 'PAUSED':
+      return css`
+        background: white;
+        color: ${Palette.primary};
+        border-color: ${Palette.primary};
+      `
+    case 'RUNNING':
+    case 'PENDING':
+      return css`
+        background: url('${runningImage}');
+        animation: bgMotion 1s infinite linear;
+        color: white;
+        border-color: transparent;
+        @keyframes bgMotion {
+          0% { background-position: -12px -1px; }
+          100% { background-position: 0 -1px; }
+        }
+      `
+    case 'CANCELLING':
+    case 'CANCELLING_AFTER_COMPLETION':
+      return css`
+        background: url('${cancellingImage}');
+        animation: bgMotion 1s infinite linear;
+        color: ${Palette.black};
+        border-color: transparent;
+        @keyframes bgMotion {
+          0% { background-position: -12px -1px; }
+          100% { background-position: 0 -1px; }
+        }
+      `
+    case 'STRANDED_AFTER_DEADLOCK':
+    case 'DEADLOCKED':
+      return css`
+        background: #424242;
+        color: white;
+        border-color: transparent;
+      `
+    case 'UNSCHEDULED':
+      return css`
+        background: ${Palette.grayscale[2]};
+        color: ${Palette.black};
+        border-color: transparent;
+      `
+    case 'INFO':
+    case 'SCHEDULED':
+      return null
+    default:
+      return null
+  }
 }
 
 const primaryColors = css`
@@ -91,7 +140,7 @@ const Wrapper = styled.div`
   font-weight: ${StyleProps.fontWeights.medium};
   text-align: center;
   border-radius: 4px;
-  ${props => statuses[props.status]}
+  ${props => statuses(props.status)}
   ${props => props.status === 'INFO' ? getInfoStatusColor(props) : ''}
 `
 
@@ -115,10 +164,7 @@ class StatusPill extends React.Component<Props> {
     let label = this.props.label || this.props.status
     let status = this.props.status
 
-    if (status === 'CANCELED_FOR_DEBUGGING') {
-      status = 'ERROR'
-      label = 'DEBUG'
-    }
+    label = LABEL_MAP[label || ''] || label
 
     return (
       <Wrapper

+ 11 - 0
src/components/atoms/StatusPill/images/cancelling.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="128px" height="16px" viewBox="0 0 128 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <pattern id="pattern-1" width="12.4264706" height="12.4264706" x="-12.4264706" y="-12.4264706" patternUnits="userSpaceOnUse">
+      <use xlink:href="#image-2" transform="scale(0.155330882,0.155330882)"></use>
+    </pattern>
+    <image id="image-2" width="80" height="80" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABGdBTUEAALGPC/xhBQAAAeJJREFUeAHt3FtuwyAQBdCkq/EOmyWkO/RuWk+jtHlgDMMA87h8WUZ3BEdIlozxeV3X75NsuyzL8iVbkl9tm9/nlr7yK+STH/nu6t5QeKQjCRgOTxIwJJ4UYFg8CcDQeK2A4fFaAIFHelvjPIWBd7NjAQLvAY8ua1Yg8F7wagCBl8ArBQTeDl4JIPAyeEeAwDvAywECrwBvDxB4hXgpQOBV4L0CAq8S7xEQeAy8OyDwmHgUOzdkxaO9N4DEB3w6XdQAWsSj3UcVgFbxaEVPB7SMNx3QOt5UQA940wC94E0B9IQ3HNAb3lBAj3jDAL3iDQH0jNcd0DteV8AIeN0Ao+B1AYyEJw4YDU8UMCKeGGBUPBHAyHjNgNHxmgCBR3zMV/rAu+GxAIH3j1cNCLxnvCpA4L3jFQMCL41XBAi8fbxDQODl8bKAwDvG2wUEXhleEhB45XhvgMCrw3sCBF493h8g8Hh4v4DA4+NRko67dvspTdvQkml1H8TXnBdOzmjgTXV429yvVgBV4tHisQCoFs8CoGo87YDq8TQDmsDTCmgGTyOgKTxtgObwNAGaxNMCaBZPA6BpvNmA5vFmArrAmwXoBm8GoCu80YDu8EYCusQbBegWbwSga7zegO7xegKGwCPAH6jFkxG1FP01AAAAAElFTkSuQmCC"></image>
+  </defs>
+  <rect id="Base" stroke="none" fill="#FDC02F" fill-rule="evenodd" x="0" y="0" width="128" height="16"></rect>
+  <rect id="Base" stroke="none" fill="url(#pattern-1)" fill-rule="evenodd" opacity="1" style="mix-blend-mode: luminosity;" x="0" y="0" width="128" height="16"></rect>
+</svg>

+ 42 - 16
src/components/atoms/StatusPill/story.jsx

@@ -12,30 +12,56 @@ You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+// @flow
+
 import React from 'react'
+import styled from 'styled-components'
 import { storiesOf } from '@storybook/react'
 import StatusPill from '.'
 
+const Wrapper = styled.div`
+  display: flex;
+  flex-wrap: wrap;
+`
+const STATUSES = [
+  'SCHEDULED',
+  'UNSCHEDULED',
+  'COMPLETED',
+  'RUNNING',
+  'PENDING',
+  'CANCELLING',
+  'CANCELLING_AFTER_COMPLETION',
+  'CANCELED',
+  'CANCELED_AFTER_COMPLETION',
+  'CANCELED_FOR_DEBUGGING',
+  'FORCE_CANCELED',
+  'ERROR',
+  'DEADLOCKED',
+  'STRANDED_AFTER_DEADLOCK',
+]
+
+const renderAllStatuses = (small: ?boolean) => (
+  <Wrapper>
+    {STATUSES.map(status => (
+      <span style={{ marginLeft: '16px', marginBottom: '16px' }} key={status}>
+        {status}
+        <StatusPill
+          key={status}
+          status={status}
+          small={small || false}
+        />
+      </span>
+    ))}
+  </Wrapper>
+)
+
 storiesOf('StatusPill', module)
-  .add('completed', () => (
-    <StatusPill status="COMPLETED" />
-  ))
-  .add('completed small', () => (
-    <StatusPill status="COMPLETED" small />
-  ))
-  .add('running', () => (
-    <StatusPill status="RUNNING" />
-  ))
-  .add('error', () => (
-    <StatusPill status="ERROR" />
-  ))
-  .add('canceled', () => (
-    <StatusPill status="CANCELED" />
-  ))
+  .add('all statuses', () => renderAllStatuses())
+  .add('all statuses small', () => renderAllStatuses(true))
   .add('paused', () => (
     <StatusPill status="PAUSED" />
   ))
-  .add('info primary', () => (
+  .add('info', () => (
     <StatusPill status="INFO" />
   ))
   .add('info secondary', () => (

+ 27 - 4
src/components/organisms/Executions/story.jsx

@@ -22,7 +22,7 @@ let tasks = [
       { message: 'the task has a progress of 10%', created_at: new Date() },
     ],
     exception_details: 'Exception details',
-    status: 'COMPLETED',
+    status: 'RUNNING',
     created_at: new Date(),
     depends_on: ['depends on id'],
     id: 'abcdefg-abcdefg-abcdefg-abcdefg-abcdefg',
@@ -77,9 +77,32 @@ let tasks = [
       { message: 'the task has a progress of 50%', created_at: new Date() },
       { message: 'the task is almost done', created_at: new Date() },
     ],
-    exception_details: 'Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception details Long Exception',
-    // exception_details: 'NA',
-    status: 'RUNNING',
+    exception_details: 'Exception details',
+    status: 'UNSCHEDULED',
+    created_at: new Date(),
+    depends_on: ['depends on id'],
+    id: 'task-2',
+    task_type: 'Task name 2',
+  },
+  {
+    progress_updates: [
+      { message: 'the task has a progress of 50%', created_at: new Date() },
+      { message: 'the task is almost done', created_at: new Date() },
+    ],
+    exception_details: 'Exception details',
+    status: 'DEADLOCKED',
+    created_at: new Date(),
+    depends_on: ['depends on id'],
+    id: 'task-2',
+    task_type: 'Task name 2',
+  },
+  {
+    progress_updates: [
+      { message: 'the task has a progress of 50%', created_at: new Date() },
+      { message: 'the task is almost done', created_at: new Date() },
+    ],
+    exception_details: 'Exception details',
+    status: 'CANCELLING',
     created_at: new Date(),
     depends_on: ['abcdefg-abcdefg-abcdefg-abcdefg-abcdefg', 'hijklmn-hijklmn-hijklmn-hijklmn-hijklmn', 'opqrst-opqrst-opqrst-opqrst-opqrst', 'uvwxyz-uvwxyz-uvwxyz-uvwxyz-uvwxyz', '01234-01234-01234-01234-01234'],
     // depends_on: ['', 0, 's'],