chore (litmus-portal): Added argo workflow representation using dagre d3 graph (#1975)

- Argo workflow representation using dagre d3
- Subscription fixes
- removed unnecessary re-rendering
- refactoring of few components
- renaming to appropriate names for few components

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>
This commit is contained in:
Arkajyoti Mukherjee 2020-09-07 20:28:33 +05:30 committed by GitHub
parent 85a0fede89
commit 7f7cb65980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1531 additions and 552 deletions

View File

@ -18,6 +18,15 @@
}
},
"rules": {
"new-cap": 0,
"no-underscore-dangle": 0,
"react/static-property-placement": [
"warn",
"property assignment",
{ "defaultProps": "static public field" }
],
"lines-between-class-members": 0,
"no-restricted-syntax": 0,
"no-nested-ternary": 0,
"no-plusplus": 0,
"dot-notation": 0,

View File

@ -1,8 +1,7 @@
/// <reference types="Cypress" />
import React from 'react';
import { mount } from 'cypress-react-unit-test';
import CustomSlider from '../../src/components/Sections/Workflow/WeightSlider';
import React from 'react';
import CustomSlider from '../../src/components/Sections/CreateWorkflow/WeightSlider';
describe('Testing Custom Slider with different values', () => {
[10, 5, 2].map((i) => {

View File

@ -2612,9 +2612,142 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
},
"@types/d3": {
"version": "3.5.43",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.43.tgz",
"integrity": "sha512-t9ZmXOcpVxywRw86YtIC54g7M9puRh8hFedRvVfHKf5YyOP6pSxA0TvpXpfseXSCInoW4P7bggTrSDiUOs4g5w==",
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-5.7.2.tgz",
"integrity": "sha512-7/wClB8ycneWGy3jdvLfXKTd5SoTg9hji7IdJ0RuO9xTY54YpJ8zlcFADcXhY1J3kCBwxp+/1jeN6a5OMwgYOw==",
"dev": true,
"requires": {
"@types/d3-array": "^1",
"@types/d3-axis": "*",
"@types/d3-brush": "*",
"@types/d3-chord": "*",
"@types/d3-collection": "*",
"@types/d3-color": "*",
"@types/d3-contour": "*",
"@types/d3-dispatch": "*",
"@types/d3-drag": "*",
"@types/d3-dsv": "*",
"@types/d3-ease": "*",
"@types/d3-fetch": "*",
"@types/d3-force": "*",
"@types/d3-format": "*",
"@types/d3-geo": "*",
"@types/d3-hierarchy": "*",
"@types/d3-interpolate": "*",
"@types/d3-path": "*",
"@types/d3-polygon": "*",
"@types/d3-quadtree": "*",
"@types/d3-random": "*",
"@types/d3-scale": "*",
"@types/d3-scale-chromatic": "*",
"@types/d3-selection": "*",
"@types/d3-shape": "*",
"@types/d3-time": "*",
"@types/d3-time-format": "*",
"@types/d3-timer": "*",
"@types/d3-transition": "*",
"@types/d3-voronoi": "*",
"@types/d3-zoom": "*"
}
},
"@types/d3-array": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.7.tgz",
"integrity": "sha512-51vHWuUyDOi+8XuwPrTw3cFqyh2Slg9y8COYkRfjCPG9TfYqY0hoNPzv/8BrcAy0FeQBzqEo/D/8Nk2caOQJnA==",
"dev": true
},
"@types/d3-axis": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.12.tgz",
"integrity": "sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg==",
"dev": true,
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-brush": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.1.1.tgz",
"integrity": "sha512-Exx14trm/q2cskHyMjCrdDllOQ35r1/pmZXaOIt8bBHwYNk722vWY3VxHvN0jdFFX7p2iL3+gD+cGny/aEmhlw==",
"dev": true,
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-chord": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.9.tgz",
"integrity": "sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw==",
"dev": true
},
"@types/d3-collection": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.8.tgz",
"integrity": "sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ==",
"dev": true
},
"@types/d3-color": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.2.2.tgz",
"integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw==",
"dev": true
},
"@types/d3-contour": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-1.3.0.tgz",
"integrity": "sha512-AUCUIjEnC5lCGBM9hS+MryRaFLIrPls4Rbv6ktqbd+TK/RXZPwOy9rtBWmGpbeXcSOYCJTUDwNJuEnmYPJRxHQ==",
"dev": true,
"requires": {
"@types/d3-array": "*",
"@types/geojson": "*"
}
},
"@types/d3-dispatch": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.8.tgz",
"integrity": "sha512-lCDtqoYez0TgFN3FljBXrz2icqeSzD0gufGook6DPBia+NOh2TBfogjHIsmNa/a+ZOewlHtq4cgLY80O1uLymw==",
"dev": true
},
"@types/d3-drag": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.3.tgz",
"integrity": "sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ==",
"dev": true,
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-dsv": {
"version": "1.0.36",
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.36.tgz",
"integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA==",
"dev": true
},
"@types/d3-ease": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.9.tgz",
"integrity": "sha512-U5ADevQ+W6fy32FVZZC9EXallcV/Mi12A5Tkd0My5MrC7T8soMQEhlDAg88XUWm0zoCQlB4XV0en/24LvuDB4Q==",
"dev": true
},
"@types/d3-fetch": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-1.1.5.tgz",
"integrity": "sha512-o9c0ItT5/Gl3wbNuVpzRnYX1t3RghzeWAjHUVLuyZJudiTxC4f/fC0ZPFWLQ2lVY8pAMmxpV8TJ6ETYCgPeI3A==",
"dev": true,
"requires": {
"@types/d3-dsv": "*"
}
},
"@types/d3-force": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.1.tgz",
"integrity": "sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA==",
"dev": true
},
"@types/d3-format": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.3.1.tgz",
"integrity": "sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A==",
"dev": true
},
"@types/d3-geo": {
@ -2626,6 +2759,142 @@
"@types/geojson": "*"
}
},
"@types/d3-hierarchy": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz",
"integrity": "sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg==",
"dev": true
},
"@types/d3-interpolate": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz",
"integrity": "sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ==",
"dev": true,
"requires": {
"@types/d3-color": "*"
}
},
"@types/d3-path": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz",
"integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==",
"dev": true
},
"@types/d3-polygon": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.7.tgz",
"integrity": "sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q==",
"dev": true
},
"@types/d3-quadtree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
"integrity": "sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q==",
"dev": true
},
"@types/d3-random": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.2.tgz",
"integrity": "sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA==",
"dev": true
},
"@types/d3-scale": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.0.tgz",
"integrity": "sha512-oQFanN0/PiR2oySHfj+zAAkK1/p4LD32Nt1TMVmzk+bYHk7vgIg/iTXQWitp1cIkDw4LMdcgvO63wL+mNs47YA==",
"dev": true,
"requires": {
"@types/d3-time": "*"
}
},
"@types/d3-scale-chromatic": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz",
"integrity": "sha512-9/D7cOBKdZdTCPc6re0HeSUFBM0aFzdNdmYggUWT9SRRiYSOa6Ys2xdTwHKgc1WS3gGfwTMatBOdWCS863REsg==",
"dev": true
},
"@types/d3-selection": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.2.tgz",
"integrity": "sha512-ksY8UxvTXpzD91Dy3D9zZg98yF2ZEPMKJd8ZQJlZt1QH3Xxr08s6fESEdC2l0Kbe6Xd9VhaoJX06cRaMR1lEnA==",
"dev": true
},
"@types/d3-shape": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.2.tgz",
"integrity": "sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w==",
"dev": true,
"requires": {
"@types/d3-path": "*"
}
},
"@types/d3-time": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.10.tgz",
"integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw==",
"dev": true
},
"@types/d3-time-format": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.1.tgz",
"integrity": "sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g==",
"dev": true
},
"@types/d3-timer": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.9.tgz",
"integrity": "sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ==",
"dev": true
},
"@types/d3-transition": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.6.tgz",
"integrity": "sha512-/F+O2r4oz4G9ATIH3cuSCMGphAnl7VDx7SbENEK0NlI/FE8Jx2oiIrv0uTrpg7yF/AmuWbqp7AGdEHAPIh24Gg==",
"dev": true,
"requires": {
"@types/d3-selection": "*"
}
},
"@types/d3-voronoi": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz",
"integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==",
"dev": true
},
"@types/d3-zoom": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.4.tgz",
"integrity": "sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA==",
"dev": true,
"requires": {
"@types/d3-interpolate": "*",
"@types/d3-selection": "*"
}
},
"@types/dagre": {
"version": "0.7.44",
"resolved": "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.44.tgz",
"integrity": "sha512-N6HD+79w77ZVAaVO7JJDW5yJ9LAxM62FpgNGO9xEde+KVYjDRyhIMzfiErXpr1g0JPon9kwlBzoBK6s4fOww9Q==",
"dev": true
},
"@types/dagre-d3": {
"version": "0.4.39",
"resolved": "https://registry.npmjs.org/@types/dagre-d3/-/dagre-d3-0.4.39.tgz",
"integrity": "sha512-JZySpfIQPRSTx38B4P5pPGeV4Pqgb0qE/aIiS60qD+j0c3mYP/AHiiwlsrrLCVZWY7OJdvD3dp+aD1HvpjPBzA==",
"dev": true,
"requires": {
"@types/d3": "^3",
"@types/dagre": "*"
},
"dependencies": {
"@types/d3": {
"version": "3.5.43",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.43.tgz",
"integrity": "sha512-t9ZmXOcpVxywRw86YtIC54g7M9puRh8hFedRvVfHKf5YyOP6pSxA0TvpXpfseXSCInoW4P7bggTrSDiUOs4g5w==",
"dev": true
}
}
},
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -2732,6 +3001,14 @@
"dev": true,
"requires": {
"@types/d3": "^3"
},
"dependencies": {
"@types/d3": {
"version": "3.5.43",
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.43.tgz",
"integrity": "sha512-t9ZmXOcpVxywRw86YtIC54g7M9puRh8hFedRvVfHKf5YyOP6pSxA0TvpXpfseXSCInoW4P7bggTrSDiUOs4g5w==",
"dev": true
}
}
},
"@types/prop-types": {
@ -6766,15 +7043,74 @@
}
},
"d3": {
"version": "3.5.17",
"resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
"integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz",
"integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==",
"requires": {
"d3-array": "1",
"d3-axis": "1",
"d3-brush": "1",
"d3-chord": "1",
"d3-collection": "1",
"d3-color": "1",
"d3-contour": "1",
"d3-dispatch": "1",
"d3-drag": "1",
"d3-dsv": "1",
"d3-ease": "1",
"d3-fetch": "1",
"d3-force": "1",
"d3-format": "1",
"d3-geo": "1",
"d3-hierarchy": "1",
"d3-interpolate": "1",
"d3-path": "1",
"d3-polygon": "1",
"d3-quadtree": "1",
"d3-random": "1",
"d3-scale": "2",
"d3-scale-chromatic": "1",
"d3-selection": "1",
"d3-shape": "1",
"d3-time": "1",
"d3-time-format": "2",
"d3-timer": "1",
"d3-transition": "1",
"d3-voronoi": "1",
"d3-zoom": "1"
}
},
"d3-array": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
},
"d3-axis": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
"integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
},
"d3-brush": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz",
"integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==",
"requires": {
"d3-dispatch": "1",
"d3-drag": "1",
"d3-interpolate": "1",
"d3-selection": "1",
"d3-transition": "1"
}
},
"d3-chord": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
"integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
"requires": {
"d3-array": "1",
"d3-path": "1"
}
},
"d3-collection": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
@ -6785,6 +7121,14 @@
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
"integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
},
"d3-contour": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
"integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
"requires": {
"d3-array": "^1.1.1"
}
},
"d3-dispatch": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
@ -6799,11 +7143,29 @@
"d3-selection": "1"
}
},
"d3-dsv": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz",
"integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==",
"requires": {
"commander": "2",
"iconv-lite": "0.4",
"rw": "1"
}
},
"d3-ease": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.6.tgz",
"integrity": "sha512-SZ/lVU7LRXafqp7XtIcBdxnWl8yyLpgOmzAk0mWBI9gXNzLDx5ybZgnRbH9dN/yY5tzVBqCQ9avltSnqVwessQ=="
},
"d3-fetch": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz",
"integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==",
"requires": {
"d3-dsv": "1"
}
},
"d3-force": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
@ -6815,6 +7177,11 @@
"d3-timer": "1"
}
},
"d3-format": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
"integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="
},
"d3-geo": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz",
@ -6841,11 +7208,43 @@
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
},
"d3-polygon": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz",
"integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ=="
},
"d3-quadtree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
"integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="
},
"d3-random": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
"integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
},
"d3-scale": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
"integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
"requires": {
"d3-array": "^1.2.0",
"d3-collection": "1",
"d3-format": "1",
"d3-interpolate": "1",
"d3-time": "1",
"d3-time-format": "2"
}
},
"d3-scale-chromatic": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz",
"integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==",
"requires": {
"d3-color": "1",
"d3-interpolate": "1"
}
},
"d3-selection": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.1.tgz",
@ -6859,6 +7258,19 @@
"d3-path": "1"
}
},
"d3-time": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
},
"d3-time-format": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
"requires": {
"d3-time": "1"
}
},
"d3-timer": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz",
@ -6877,6 +7289,11 @@
"d3-timer": "1"
}
},
"d3-voronoi": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
"integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
},
"d3-zoom": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz",
@ -6889,6 +7306,168 @@
"d3-transition": "1"
}
},
"dagre": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
"requires": {
"graphlib": "^2.1.8",
"lodash": "^4.17.15"
}
},
"dagre-d3": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/dagre-d3/-/dagre-d3-0.6.4.tgz",
"integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==",
"requires": {
"d3": "^5.14",
"dagre": "^0.8.5",
"graphlib": "^2.1.8",
"lodash": "^4.17.15"
},
"dependencies": {
"d3": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz",
"integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==",
"requires": {
"d3-array": "1",
"d3-axis": "1",
"d3-brush": "1",
"d3-chord": "1",
"d3-collection": "1",
"d3-color": "1",
"d3-contour": "1",
"d3-dispatch": "1",
"d3-drag": "1",
"d3-dsv": "1",
"d3-ease": "1",
"d3-fetch": "1",
"d3-force": "1",
"d3-format": "1",
"d3-geo": "1",
"d3-hierarchy": "1",
"d3-interpolate": "1",
"d3-path": "1",
"d3-polygon": "1",
"d3-quadtree": "1",
"d3-random": "1",
"d3-scale": "2",
"d3-scale-chromatic": "1",
"d3-selection": "1",
"d3-shape": "1",
"d3-time": "1",
"d3-time-format": "2",
"d3-timer": "1",
"d3-transition": "1",
"d3-voronoi": "1",
"d3-zoom": "1"
}
},
"d3-axis": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
"integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
},
"d3-brush": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz",
"integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==",
"requires": {
"d3-dispatch": "1",
"d3-drag": "1",
"d3-interpolate": "1",
"d3-selection": "1",
"d3-transition": "1"
}
},
"d3-chord": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
"integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
"requires": {
"d3-array": "1",
"d3-path": "1"
}
},
"d3-contour": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
"integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
"requires": {
"d3-array": "^1.1.1"
}
},
"d3-dsv": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz",
"integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==",
"requires": {
"commander": "2",
"iconv-lite": "0.4",
"rw": "1"
}
},
"d3-fetch": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz",
"integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==",
"requires": {
"d3-dsv": "1"
}
},
"d3-format": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
"integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="
},
"d3-polygon": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz",
"integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ=="
},
"d3-random": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
"integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
},
"d3-scale": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
"integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
"requires": {
"d3-array": "^1.2.0",
"d3-collection": "1",
"d3-format": "1",
"d3-interpolate": "1",
"d3-time": "1",
"d3-time-format": "2"
}
},
"d3-scale-chromatic": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz",
"integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==",
"requires": {
"d3-color": "1",
"d3-interpolate": "1"
}
},
"d3-time": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz",
"integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="
},
"d3-time-format": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
"requires": {
"d3-time": "1"
}
}
}
},
"damerau-levenshtein": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
@ -10169,6 +10748,14 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
},
"graphlib": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
"integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==",
"requires": {
"lodash": "^4.17.15"
}
},
"graphql": {
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-15.3.0.tgz",
@ -15700,6 +16287,13 @@
"topojson-client": "^3.1.0",
"webgl-context": "^2.2.0",
"world-calendars": "^1.0.3"
},
"dependencies": {
"d3": {
"version": "3.5.17",
"resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
"integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
}
}
},
"pn": {

View File

@ -19,6 +19,8 @@
"@material-ui/pickers": "^3.2.10",
"ace-builds": "^1.4.12",
"brace": "^0.11.1",
"d3": "^5.16.0",
"dagre-d3": "^0.6.4",
"date-fns": "^2.14.0",
"graphql": "^15.3.0",
"i18next": "^19.7.0",
@ -74,6 +76,8 @@
"not op_mini all"
],
"devDependencies": {
"@types/d3": "^5.7.2",
"@types/dagre-d3": "^0.4.39",
"@types/js-yaml": "^3.12.5",
"@types/jsonwebtoken": "^8.5.0",
"@types/plotly.js": "^1.50.15",

View File

@ -2,8 +2,8 @@ import Breadcrumbs from '@material-ui/core/Breadcrumbs';
import * as React from 'react';
import { useEffect } from 'react';
import { Link } from 'react-router-dom';
import useStyles from './styles';
import { history } from '../../redux/configureStore';
import useStyles from './styles';
interface CustomBreadcrumbsProps {
location: string;
@ -12,7 +12,7 @@ interface CustomBreadcrumbsProps {
const CustomBreadcrumbs: React.FC<CustomBreadcrumbsProps> = ({ location }) => {
const path: string[] = location.split('/');
let intermediatRoutes: string = '/';
let intermediateRoutes: string = '/';
const classes = useStyles();
@ -22,11 +22,12 @@ const CustomBreadcrumbs: React.FC<CustomBreadcrumbsProps> = ({ location }) => {
<Breadcrumbs aria-label="breadcrumb">
{path.map((path: string) => {
if (path) {
intermediatRoutes += path;
intermediateRoutes += path;
const link = (
<Link
to={intermediatRoutes}
key={path}
to={intermediateRoutes}
className={classes.breadCrumb}
onClick={() => {
history.push(`/${path}`);
@ -36,7 +37,7 @@ const CustomBreadcrumbs: React.FC<CustomBreadcrumbsProps> = ({ location }) => {
</Link>
);
intermediatRoutes += '/';
intermediateRoutes += '/';
return link;
}

View File

@ -0,0 +1,196 @@
import * as d3 from 'd3';
import dagreD3, { GraphLabel, Node } from 'dagre-d3';
import React, { Component, createRef } from 'react';
interface GraphProps {
nodes: d3Node[];
links: d3Link[];
zoomable?: boolean;
fitBoundaries?: boolean;
height?: string;
width?: string;
config?: GraphLabel;
animate?: number;
className?: string;
shape?: shapes;
onNodeClick?: nodeClick;
onRelationshipClick?: relationshipClick;
}
type shapes = 'rect' | 'circle' | 'ellipse';
type labelType = 'html' | 'svg' | 'string';
type nodeClick = (nodeData: {
d3node: Node;
original: d3Node | undefined;
}) => void;
type relationshipClick = (nodeData: {
d3source: Node;
source: d3Node | undefined;
d3target: Node;
target: d3Node | undefined;
}) => void;
export type d3Node = {
id: any;
label: string;
class?: string;
labelType?: labelType;
config?: object;
};
export type d3Link = {
source: string;
target: string;
class?: string;
label?: string;
config?: object;
};
type Relationship = {
v: any;
w: any;
};
class DagreGraph extends Component<GraphProps> {
svg = createRef<SVGSVGElement>();
innerG = createRef<SVGSVGElement>();
static defaultProps = {
zoomable: false,
fitBoundaries: false,
className: 'dagre-d3-react',
};
componentDidMount() {
this._drawChart();
}
componentDidUpdate() {
this._drawChart();
}
_drawChart = () => {
const {
nodes,
links,
zoomable,
fitBoundaries,
config,
animate,
shape,
onNodeClick,
onRelationshipClick,
} = this.props;
const g = new dagreD3.graphlib.Graph().setGraph(config || {});
nodes.forEach((node) =>
g.setNode(node.id, {
label: node.label,
class: node.class || '',
labelType: node.labelType || 'string',
...node.config,
})
);
if (shape) {
g.nodes().forEach((v) => {
g.node(v).shape = shape;
});
}
links.forEach((link) => {
g.setEdge(link.source, link.target, {
label: link.label || '',
class: link.class || '',
...link.config,
});
});
const render = new dagreD3.render();
const svg: any = d3.select(this.svg.current);
const inner: any = d3.select(this.innerG.current);
const zoom = d3
.zoom()
.on('zoom', () => inner.attr('transform', d3.event.transform));
if (zoomable) {
svg.call(zoom);
}
if (animate) {
g.graph().transition = function transition(selection) {
return selection.transition().duration(animate || 1000);
};
}
render(inner, g);
if (fitBoundaries) {
// @BertCh recommendation for fitting boundaries
const { width, height, x, y } = inner.node().getBBox();
const parent = inner.node().parentElement || inner.node().parentNode;
const fullWidth = parent.clientWidth || parent.parentNode.clientWidth;
const fullHeight = parent.clientHeight || parent.parentNode.clientHeight;
const midX = x + width / 2;
const midY = y + height / 2;
if (width === 0 || height === 0) return; // nothing to fit
const scale = 0.9 / Math.max(width / fullWidth, height / fullHeight);
const translate = [
fullWidth / 2 - scale * midX,
fullHeight / 2 - scale * midY,
];
const transform = d3.zoomIdentity
.translate(translate[0], translate[1])
.scale(scale);
svg
.transition()
.duration(animate || 0) // milliseconds
.call(zoom.transform, transform);
}
if (onNodeClick) {
svg.selectAll('g.node').on('click', (id: any) => {
const _node: Node = g.node(id);
const _original = this._getNodeData(id);
onNodeClick({ d3node: _node, original: _original });
});
}
if (onRelationshipClick) {
svg
.selectAll('g.edgeLabel, g.edgePath')
.on('click', (id: Relationship) => {
const _source = g.node(id.v);
const _original_source = this._getNodeData(id.v);
const _target = g.node(id.w);
const _original_target = this._getNodeData(id.w);
onRelationshipClick({
d3source: _source,
source: _original_source,
d3target: _target,
target: _original_target,
});
});
}
};
_getNodeData(id: any) {
const { nodes } = this.props;
return nodes.find((node) => node.id === id);
}
render() {
const { width, height, className } = this.props;
return (
<svg
width={width}
height={height}
ref={this.svg}
className={className || ''}
>
<g ref={this.innerG} />
</svg>
);
}
}
export default DagreGraph;

View File

@ -1,21 +1,21 @@
import React from 'react';
import {
Button,
IconButton,
Menu,
MenuItem,
Popover,
TableCell,
Typography,
IconButton,
MenuItem,
Menu,
Popover,
Button,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import moment from 'moment';
import useStyles from './styles';
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
import { history } from '../../../../redux/configureStore';
import React from 'react';
import { WorkflowRun } from '../../../../models/workflowData';
import { history } from '../../../../redux/configureStore';
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
import useStyles from './styles';
interface TableDataProps {
data: WorkflowRun;
@ -170,10 +170,7 @@ const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
<MenuItem
value="Workflow"
onClick={() =>
history.push({
pathname: '/schedule',
state: data,
})
history.push(`/workflows/${data.workflow_name}/schedule`)
}
>
<div className={classes.expDiv}>

View File

@ -1,64 +1,62 @@
import React, { useState } from 'react';
import { useQuery } from '@apollo/client';
import SearchIcon from '@material-ui/icons/Search';
import {
InputBase,
InputAdornment,
FormControl,
InputLabel,
Select,
MenuItem,
Typography,
TableContainer,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
TablePagination,
IconButton,
InputAdornment,
InputBase,
InputLabel,
MenuItem,
Select,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TablePagination,
TableRow,
Typography,
} from '@material-ui/core';
import ExpandLessIcon from '@material-ui/icons/ExpandLess';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import SearchIcon from '@material-ui/icons/Search';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import useStyles from './styles';
import { WORKFLOW_DETAILS } from '../../../../graphql';
import TableData from './TableData';
import {
ExecutionData,
Workflow,
WorkflowDataVars,
WorkflowRun,
ExecutionData,
} from '../../../../models/workflowData';
import Loader from '../../../Loader';
import { RootState } from '../../../../redux/reducers';
import {
sortAlphaAsc,
sortAlphaDesc,
sortNumAsc,
sortNumDesc,
} from '../../../../utils/sort';
import { UserData } from '../../../../models/user';
import { RootState } from '../../../../redux/reducers';
import Loader from '../../../Loader';
import useStyles from './styles';
import TableData from './TableData';
interface FilterOption {
search: string;
cluster: string;
}
interface PaginationData {
pageNo: number;
rowsPerPage: number;
}
interface SortData {
startDate: { sort: boolean; ascending: boolean };
name: { sort: boolean; ascending: boolean };
}
const ScheduleWorkflow = () => {
const BrowseSchedule = () => {
const classes = useStyles();
const userData: UserData = useSelector((state: RootState) => state.userData);
const { selectedProjectID } = userData;
const selectedProjectID = useSelector(
(state: RootState) => state.userData.selectedProjectID
);
// Apollo query to get the scheduled data
const { data, loading, error } = useQuery<Workflow, WorkflowDataVars>(
@ -329,4 +327,4 @@ const ScheduleWorkflow = () => {
);
};
export default ScheduleWorkflow;
export default BrowseSchedule;

View File

@ -1,15 +1,15 @@
import { Divider, Typography } from '@material-ui/core';
import React from 'react';
import { Typography, Divider } from '@material-ui/core';
import useStyles from './styles';
import { preDefinedWorkflowData } from '../../../../models';
import Head from './Head';
import ExperimentDetails from './ExperimentDetails';
import Recommendation from './Recommendation';
import Scaffold from '../../../../containers/layouts/Scaffold';
import ButtonOutlined from '../../../Button/ButtonOutline';
import { preDefinedWorkflowData } from '../../../../models/predefinedWorkflow';
import { LocationState } from '../../../../models/routerModel';
import { history } from '../../../../redux/configureStore';
import ButtonFilled from '../../../Button/ButtonFilled';
import { LocationState } from '../../../../models/routerModel';
import ButtonOutlined from '../../../Button/ButtonOutline';
import ExperimentDetails from './ExperimentDetails';
import Head from './Head';
import Recommendation from './Recommendation';
import useStyles from './styles';
interface LocationObjectProps {
workflowData: preDefinedWorkflowData;

View File

@ -1,18 +1,18 @@
import React from 'react';
import {
IconButton,
Menu,
MenuItem,
TableCell,
Typography,
IconButton,
MenuItem,
Menu,
} from '@material-ui/core';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import CustomStatus from '../CustomStatus/Status';
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
import useStyles from './styles';
import timeDifferenceForDate from '../../../../utils/datesModifier';
import React from 'react';
import { ExecutionData, WorkflowRun } from '../../../../models/workflowData';
import { history } from '../../../../redux/configureStore';
import { WorkflowRun, ExecutionData } from '../../../../models/workflowData';
import timeDifferenceForDate from '../../../../utils/datesModifier';
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
import CustomStatus from '../CustomStatus/Status';
import useStyles from './styles';
interface TableDataProps {
data: WorkflowRun;
@ -99,10 +99,7 @@ const TableData: React.FC<TableDataProps> = ({ data, exeData }) => {
<MenuItem
value="Workflow"
onClick={() =>
history.push({
pathname: '/workflows/workflow-underground',
state: data,
})
history.push(`/workflows/${data.workflow_name}/details`)
}
>
Show the workflow

View File

@ -10,13 +10,12 @@ import {
TableRow,
Typography,
} from '@material-ui/core';
import moment from 'moment';
import ExpandLessIcon from '@material-ui/icons/ExpandLess';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import moment from 'moment';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../../../graphql';
import { UserData } from '../../../../models/user';
import {
ExecutionData,
Workflow,
@ -25,16 +24,15 @@ import {
WorkflowSubscription,
} from '../../../../models/workflowData';
import { RootState } from '../../../../redux/reducers';
import {
sortAlphaAsc,
sortAlphaDesc,
sortNumAsc,
sortNumDesc,
} from '../../../../utils/sort';
import HeaderSection from './HeaderSection';
import useStyles from './styles';
import TableData from './TableData';
import HeaderSection from './headerSection';
interface FilterOptions {
search: string;
@ -61,8 +59,9 @@ interface DateData {
const BrowseWorkflow = () => {
const classes = useStyles();
const userData: UserData = useSelector((state: RootState) => state.userData);
const { selectedProjectID } = userData;
const selectedProjectID = useSelector(
(state: RootState) => state.userData.selectedProjectID
);
// Query to get workflows
const { subscribeToMore, data, error } = useQuery<Workflow, WorkflowDataVars>(

View File

@ -1,8 +1,8 @@
import React from 'react';
import PredifinedWorkflows from '../../../PredifinedWorkflows';
import workflowData from '../../../PredifinedWorkflows/data';
import { history } from '../../../../redux/configureStore';
import parsed from '../../../../utils/yamlUtils';
import PredifinedWorkflows from '../../../PredifinedWorkflows';
import workflowData from '../../../PredifinedWorkflows/data';
import useStyles from './styles';
const Templates = () => {
@ -24,7 +24,7 @@ const Templates = () => {
testWeights.push(10);
});
history.push({
pathname: `/workflows/${workflowData[index].title}`,
pathname: `/workflows/${workflowData[index].title}/template`,
state: {
workflowData: workflowData[index],
testNames,

View File

@ -1,100 +0,0 @@
import { AppBar, Box, Typography } from '@material-ui/core';
import { createStyles, withStyles } from '@material-ui/core/styles';
import Tab from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import React from 'react';
import { history } from '../../../../redux/configureStore';
import ButtonFilled from '../../../Button/ButtonFilled';
import BrowseWorkflow from '../BrowseWorkflow';
import useStyles from './styles';
import ScheduleWorkflow from '../ScheduleWorkflow';
import Templates from '../Templates';
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
const TabPanel: React.FC<TabPanelProps> = ({ children, index, value }) => {
return (
<div role="tabpanel" hidden={value !== index}>
{value === index && (
<Box p={3}>
<>{children}</>
</Box>
)}
</div>
);
};
interface StyledTabProps {
label: string;
}
const StyledTab = withStyles((theme) =>
createStyles({
root: {
textTransform: 'none',
color: 'rgba(0,0,0,0.5)',
fontSize: '0.95rem',
paddingTop: theme.spacing(1.875),
paddingBottom: theme.spacing(1.875),
width: '15.9375rem',
'&:focus': {
opacity: 1,
},
},
})
)((props: StyledTabProps) => <Tab {...props} />);
const CenteredTabs = () => {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
setValue(newValue);
};
return (
<>
<section className="Header section">
<div className={classes.header}>
<Typography variant="h4">Chaos Workflows</Typography>
<div className={classes.scheduleBtn}>
<ButtonFilled
isPrimary={false}
handleClick={() => history.push('/create-workflow')}
>
<>Schedule a workflow</>
</ButtonFilled>
</div>
</div>
</section>
<AppBar position="static" color="default" className={classes.appBar}>
<Tabs
value={value}
onChange={handleChange}
indicatorColor="secondary"
textColor="secondary"
variant="fullWidth"
>
<StyledTab label="Browse workflows" />
<StyledTab label="Schedules" />
<StyledTab label="Templates" />
<StyledTab label="Analytics" />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
<BrowseWorkflow />
</TabPanel>
<TabPanel value={value} index={1}>
<ScheduleWorkflow />
</TabPanel>
<TabPanel value={value} index={2}>
<Templates />
</TabPanel>
<TabPanel value={value} index={3}>
Item Four
</TabPanel>
</>
);
};
export default CenteredTabs;

View File

@ -1,20 +0,0 @@
import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles((theme) => ({
header: {
width: '100%',
display: 'flex',
flexDirection: 'row',
marginTop: theme.spacing(2.5),
marginBottom: theme.spacing(2.5),
},
scheduleBtn: {
marginLeft: 'auto',
},
appBar: {
background: 'transparent',
boxShadow: 'none',
},
}));
export default useStyles;

View File

@ -1,3 +1,4 @@
import { useLazyQuery } from '@apollo/client';
import {
FormControl,
FormControlLabel,
@ -6,16 +7,14 @@ import {
} from '@material-ui/core';
import Radio from '@material-ui/core/Radio';
import * as React from 'react';
import { useLazyQuery } from '@apollo/client';
import { useSelector } from 'react-redux';
import { GET_CLUSTER } from '../../../../graphql';
import useActions from '../../../../redux/actions';
import * as WorkflowActions from '../../../../redux/actions/workflow';
import { RootState } from '../../../../redux/reducers';
import ButtonFilled from '../../../Button/ButtonFilled';
import ButtonOutLine from '../../../Button/ButtonOutline';
import useStyles from './styles';
import { GET_CLUSTER } from '../../../../graphql';
import { RootState } from '../../../../redux/reducers';
import { UserData } from '../../../../models/user';
import useActions from '../../../../redux/actions';
import * as WorkflowActions from '../../../../redux/actions/workflow';
/*
Check is image which is used as
@ -41,7 +40,9 @@ const WorkflowCluster: React.FC<WorkflowClusterProps> = ({ gotoStep }) => {
const workflow = useActions(WorkflowActions);
const [isTragetSelected, setTarget] = React.useState(true);
const [isRegistered, setRegistration] = React.useState(true);
const userData: UserData = useSelector((state: RootState) => state.userData);
const selectedProjectID = useSelector(
(state: RootState) => state.userData.selectedProjectID
);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue((event.target as HTMLInputElement).value);
};
@ -51,7 +52,7 @@ const WorkflowCluster: React.FC<WorkflowClusterProps> = ({ gotoStep }) => {
if (data && data.getCluster.length !== 0) {
workflow.setWorkflowDetails({
clusterid: data.getCluster[0].cluster_id,
project_id: userData.selectedProjectID,
project_id: selectedProjectID,
});
gotoStep(1);
} else {
@ -63,7 +64,7 @@ const WorkflowCluster: React.FC<WorkflowClusterProps> = ({ gotoStep }) => {
const handleClick = () => {
getCluster({
variables: {
project_id: userData.selectedProjectID,
project_id: selectedProjectID,
cluster_type: 'internal',
},
});

View File

@ -0,0 +1,85 @@
import React, { useEffect, useState } from 'react';
import { Nodes } from '../../../../models/workflowData';
import useActions from '../../../../redux/actions';
import * as NodeSelectionActions from '../../../../redux/actions/nodeSelection';
import DagreGraph, { d3Link, d3Node } from '../../../DagreGraph';
import useStyles from './styles';
interface GraphData {
nodes: d3Node[];
links: d3Link[];
}
interface ArgoWorkflowProps {
nodes: Nodes;
}
const ArgoWorkflow: React.FC<ArgoWorkflowProps> = ({ nodes }) => {
const classes = useStyles();
// Redux action call for updating selected node
const nodeSelection = useActions(NodeSelectionActions);
const [graphData, setGraphData] = useState<GraphData>({
nodes: [],
links: [],
});
useEffect(() => {
const data: GraphData = {
nodes: [],
links: [],
};
for (const key of Object.keys(nodes)) {
const node = nodes[key];
data.nodes.push({
id: key,
class: `${node.phase} ${node.type}`,
label: node.type !== 'StepGroup' ? node.name : '',
labelType: 'html',
});
if (node.children) {
node.children.map((child) =>
data.links.push({
source: key,
target: child,
config: {
arrowheadStyle: 'display: arrowhead',
},
})
);
}
}
setGraphData({
nodes: [...data.nodes],
links: [...data.links],
});
}, [nodes]);
return graphData.nodes.length ? (
<DagreGraph
className={classes.dagreGraph}
nodes={graphData.nodes}
links={graphData.links}
config={{
ranker: 'tight-tree',
}}
animate={1000}
shape="rect"
fitBoundaries
zoomable
onNodeClick={({ original }) => {
const nodeID = Object.keys(nodes).filter(
(key) => key === original?.id
)[0];
nodeSelection.selectNode(nodes[nodeID]);
}}
/>
) : (
<div>Loading Graph...</div>
);
};
export default ArgoWorkflow;

View File

@ -0,0 +1,48 @@
import { makeStyles, Theme } from '@material-ui/core/styles';
const useStyles = makeStyles((theme: Theme) => ({
dagreGraph: {
width: '100%',
height: '100%',
// Styles for nodes
'& g g.nodes': {
'& g.node': {
cursor: 'pointer',
color: theme.palette.common.white,
'& rect': {
rx: '0.2rem',
ry: '0.2rem',
},
},
'& g.Succeeded': {
fill: theme.palette.primary.dark,
},
'& g.Running': {
fill: theme.palette.warning.main,
},
'& g.Pending': {
fill: theme.palette.customColors.gray,
},
'& g.Failed': {
fill: theme.palette.error.dark,
},
'& g.StepGroup': {
fill: theme.palette.customColors.gray,
cursor: 'default',
'& rect': {
rx: '0.625rem !important',
ry: '0.625rem !important',
transform: 'scale(0.5)',
},
},
},
// Styles for edges
'& g g.edgePaths': {
stroke: theme.palette.customColors.gray,
},
},
}));
export default useStyles;

View File

@ -0,0 +1,164 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// TODO: remove this after creating UI for node details sidebar
import { Typography } from '@material-ui/core';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { ExecutionData } from '../../../../models/workflowData';
import { RootState } from '../../../../redux/reducers';
import timeDifference from '../../../../utils/datesModifier';
import useStyles from './styles';
interface WorkflowInfoProps {
workflow_name: string;
execution_data: ExecutionData;
cluster_name: string;
}
interface SidebarState {
currentRunningNodes: string[];
executedNodes: string[];
}
const WorkflowInfo: React.FC<WorkflowInfoProps> = ({
workflow_name,
execution_data,
cluster_name,
}) => {
const classes = useStyles();
// Get selected node data from redux
const selectedNode = useSelector((state: RootState) => state.selectedNode);
const [duration, setDuration] = useState<number>(0);
const [data, setData] = useState<SidebarState>({
currentRunningNodes: [],
executedNodes: [],
});
useEffect(() => {
setDuration(
(parseInt(execution_data.finishedAt, 10) -
parseInt(execution_data.startedAt, 10)) /
60
);
// If the Workflow is Running [Data is being received through Subscription]
// Set the currently executed node in a local state
const executedNodes: string[] = [];
const currentRunningNodes: string[] = [];
for (const val of Object.values(execution_data.nodes))
if (val.type !== 'StepGroup' && val.phase === 'Running')
currentRunningNodes.push(val.name);
else if (val.type !== 'StepGroup' && val.phase === 'Succeeded')
executedNodes.push(val.name);
setData({
...data,
currentRunningNodes,
executedNodes,
});
}, [execution_data.nodes, execution_data.phase]);
return (
<div className={classes.root}>
{/* Workflow Information */}
<Typography className={classes.header} variant="h6">
<span className={classes.bold}>Workflow Information</span>
</Typography>
<hr className={classes.divider} />
<div className={classes.heightMaintainer}>
<Typography className={classes.workflowSpacing}>
<span className={classes.bold}>Workflow name:</span>
<br />
{workflow_name}
</Typography>
</div>
<hr />
{/* Workflow Details
@param State
@param Start Time
@param End Time
@param Duration
@param Namespace */}
<div className={classes.workflowSpacing}>
<div className={classes.heightMaintainer}>
<Typography>
<span className={classes.bold}>State:</span> {execution_data.phase}
</Typography>
<Typography>
<span className={classes.bold}>Start time:</span>{' '}
{timeDifference(execution_data.startedAt)}
</Typography>
{execution_data.phase !== 'Running' ? (
<>
<Typography>
<span className={classes.bold}>End time:</span>{' '}
{timeDifference(execution_data.finishedAt)}
</Typography>
<Typography>
<span className={classes.bold}>Duration:</span>{' '}
{`${duration.toFixed(1)} minutes`}
</Typography>
</>
) : (
<></>
)}
<Typography>
<span className={classes.bold}>Namespace:</span>{' '}
{execution_data.namespace}
</Typography>
</div>
</div>
<hr />
{/* Workflow Node Details
@param Currently Running Node
@param Executed Nodes */}
<div className={classes.workflowSpacing}>
<div className={classes.heightMaintainer}>
{execution_data.phase === 'Running' ? (
<Typography>
<span className={classes.bold}>Currently Running Nodes:</span>{' '}
<ul>
{data.currentRunningNodes.map((node) => (
<li key={node}>{node}</li>
))}
</ul>
</Typography>
) : (
<></>
)}
<Typography>
<span className={classes.bold}>Executed Nodes:</span>{' '}
{data.executedNodes.length ? (
<ul>
{data.executedNodes.map((node) => (
<li key={node}>{node}</li>
))}
</ul>
) : (
<Typography>No executed nodes</Typography>
)}
</Typography>
</div>
</div>
<hr />
{/* Cluster Details
@param Cluster Name */}
<div className={classes.workflowSpacing}>
<div className={classes.heightMaintainer}>
<Typography>
<span className={classes.bold}>Cluster:</span> {cluster_name}
</Typography>
</div>
</div>
</div>
);
};
export default WorkflowInfo;

View File

@ -1,199 +0,0 @@
/* eslint-disable */
import { Typography } from '@material-ui/core';
import React, { useEffect, useState } from 'react';
import { ExecutionData, Node } from '../../../../models/workflowData';
import timeDifference from '../../../../utils/datesModifier';
import useStyles from './styles';
interface WorkflowRepresentationProps {
workflow_name: string;
execution_data: string;
cluster_name: string;
}
interface SidebarProps extends ExecutionData {
current_running_node: string[];
executed_nodes: string[];
}
const SideBar: React.FC<WorkflowRepresentationProps> = ({
workflow_name,
execution_data,
cluster_name,
}) => {
const classes = useStyles();
const [duration, setDuration] = useState<number>(0);
const [data, setData] = useState<SidebarProps>({
creationTimestamp: '',
event_type: '',
finishedAt: '',
name: '',
namespace: '',
nodes: {},
current_running_node: [],
executed_nodes: [],
phase: '',
startedAt: '',
uid: '',
});
useEffect(() => {
if (execution_data !== undefined) {
const execData = JSON.parse(execution_data) as ExecutionData;
// Setting initial data to the data received from query/subscription
setData({
...data,
...execData,
});
}
}, [(JSON.parse(execution_data) as ExecutionData).phase]);
useEffect(() => {
setDuration(
(parseInt(data.finishedAt, 10) - parseInt(data.startedAt, 10)) / 60
);
// If the Workflow is Running [Data is being received through Subscription]
// Set the currently executed node in a local state
if (data.phase === 'Running' && data.nodes !== undefined) {
const dataObj: Node[] = Object.values(data.nodes as Node);
const currentlyRunningNodes: string[] = [];
dataObj.map((val) => {
if (val.type !== 'StepGroup' && val.phase === 'Running') {
currentlyRunningNodes.push(val.name);
}
setData({
...data,
current_running_node: [
...data.executed_nodes,
...currentlyRunningNodes,
],
});
});
} else {
// If the Workflow has Succeeded or Failed
// Store all the executed nodes in an array
const dataObj: Node[] = Object.values(data.nodes as Node);
const executedNodes: string[] = [];
dataObj.map((val) => {
if (val.name.charAt(0) === '[') {
val.name = 'Step Group ' + val.name.substring(1, val.name.length - 1);
}
executedNodes.push(val.name);
setData({
...data,
executed_nodes: [...data.executed_nodes, ...executedNodes],
});
});
}
}, [data.nodes]);
return (
<div className={classes.root}>
{/* Workflow Information */}
<Typography className={classes.header} variant="h6">
<span className={classes.bold}>Workflow Information</span>
</Typography>
<hr className={classes.divider} />
<div className={classes.heightMaintainer}>
<Typography className={classes.workflowSpacing}>
<span className={classes.bold}>Workflow name:</span>
<br />
{workflow_name}
</Typography>
</div>
<hr />
{/* Workflow Details
@param State
@param Start Time
@param End Time
@param Duration
@param Namespace */}
<div className={classes.workflowSpacing}>
<div className={classes.heightMaintainer}>
<Typography>
<span className={classes.bold}>State:</span> {data.phase}
</Typography>
<Typography>
<span className={classes.bold}>Start time:</span>{' '}
{timeDifference(data.startedAt)}
</Typography>
{data.phase !== 'Running' ? (
<>
<Typography>
<span className={classes.bold}>End time:</span>{' '}
{timeDifference(data.finishedAt)}
</Typography>
<Typography>
<span className={classes.bold}>Duration:</span>{' '}
{`${duration.toFixed(1)} minutes`}
</Typography>
</>
) : (
<></>
)}
<Typography>
<span className={classes.bold}>Namespace:</span> {data.namespace}
</Typography>
</div>
</div>
<hr />
{/* Workflow Node Details
@param Currently Running Node
@param Executed Nodes */}
<div className={classes.workflowSpacing}>
<div className={classes.heightMaintainer}>
{data.phase === 'Running' ? (
<Typography>
<span className={classes.bold}>Currently Running Nodes:</span>{' '}
{
<ul>
{data.current_running_node.map((node) => {
return <li>{node}</li>;
})}
</ul>
}
</Typography>
) : (
<Typography>
<span className={classes.bold}>Executed Nodes:</span>{' '}
{
<ul>
{data.executed_nodes.map((node) => {
return <li>{node}</li>;
})}
</ul>
}
</Typography>
)}
</div>
</div>
<hr />
{/* Cluster Details
@param Cluster Name */}
<div className={classes.workflowSpacing}>
<div className={classes.heightMaintainer}>
<Typography>
<span className={classes.bold}>Cluster:</span> {cluster_name}
</Typography>
</div>
</div>
</div>
);
};
export default SideBar;

View File

@ -0,0 +1,39 @@
import { Box, createStyles, Tab, withStyles } from '@material-ui/core';
import React from 'react';
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
const TabPanel: React.FC<TabPanelProps> = ({ children, index, value }) => {
return (
<div role="tabpanel" hidden={value !== index}>
{value === index && (
<Box p={3}>
<>{children}</>
</Box>
)}
</div>
);
};
interface StyledTabProps {
label: string;
}
const StyledTab = withStyles((theme) =>
createStyles({
root: {
textTransform: 'none',
color: 'rgba(0,0,0,0.5)',
fontSize: '0.95rem',
paddingTop: theme.spacing(1.875),
paddingBottom: theme.spacing(1.875),
width: '15.9375rem',
'&:focus': {
opacity: 1,
},
},
})
)((props: StyledTabProps) => <Tab {...props} />);
export { TabPanel, StyledTab };

View File

@ -1,32 +1,31 @@
import React from 'react';
import { useMutation } from '@apollo/client';
import Step from '@material-ui/core/Step';
import { StepIconProps } from '@material-ui/core/StepIcon';
import StepLabel from '@material-ui/core/StepLabel';
import Stepper from '@material-ui/core/Stepper';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { useSelector } from 'react-redux';
import { useMutation } from '@apollo/client';
import YAML from 'yaml';
import Unimodal from '../../containers/layouts/Unimodal';
import { CREATE_WORKFLOW } from '../../graphql';
import { experimentMap, WorkflowData } from '../../models/workflow';
import useActions from '../../redux/actions';
import * as WorkflowActions from '../../redux/actions/workflow';
import { history } from '../../redux/configureStore';
import { RootState } from '../../redux/reducers';
import parsed from '../../utils/yamlUtils';
import ButtonFilled from '../Button/ButtonFilled';
import ButtonOutline from '../Button/ButtonOutline';
import ReliablityScore from '../Sections/Workflow/ReliabilityScore';
import ScheduleWorkflow from '../Sections/Workflow/ScheduleWorkflow';
import VerifyCommit from '../Sections/Workflow/VerifyCommit';
import ChooseAWorkflowCluster from '../Sections/Workflow/WorkflowCluster';
import ChooseWorkflow from '../Sections/CreateWorkflow/ChooseWorkflow/index';
import ReliablityScore from '../Sections/CreateWorkflow/ReliabilityScore';
import ScheduleWorkflow from '../Sections/CreateWorkflow/ScheduleWorkflow';
import TuneWorkflow from '../Sections/CreateWorkflow/TuneWorkflow/index';
import VerifyCommit from '../Sections/CreateWorkflow/VerifyCommit';
import ChooseAWorkflowCluster from '../Sections/CreateWorkflow/WorkflowCluster';
import QontoConnector from './quontoConnector';
import useStyles from './styles';
import useQontoStepIconStyles from './useQontoStepIconStyles';
import TuneWorkflow from '../Sections/Workflow/TuneWorkflow/index';
import ChooseWorkflow from '../Sections/Workflow/ChooseWorkflow/index';
import { WorkflowData, experimentMap } from '../../models/workflow';
import { UserData } from '../../models/user';
import { RootState } from '../../redux/reducers';
import useActions from '../../redux/actions';
import * as WorkflowActions from '../../redux/actions/workflow';
import parsed from '../../utils/yamlUtils';
import { CREATE_WORKFLOW } from '../../graphql';
import Unimodal from '../../containers/layouts/Unimodal';
import { history } from '../../redux/configureStore';
function getSteps(): string[] {
return [
@ -127,12 +126,10 @@ const CustomStepper = () => {
clusterid,
} = workflowData;
const userData: UserData = useSelector((state: RootState) => state.userData);
const { selectedProjectID } = userData;
const selectedProjectID = useSelector(
(state: RootState) => state.userData.selectedProjectID
);
const workflow = useActions(WorkflowActions);
const steps = getSteps();
const handleNext = () => {

View File

@ -14,9 +14,7 @@ const ErrorPage = lazy(() => import('../../pages/ErrorPage'));
const Workflows = lazy(() => import('../../pages/Workflows'));
const CreateWorkflow = lazy(() => import('../../pages/CreateWorkflow'));
const LoginPage = lazy(() => import('../../pages/LoginPage'));
const WorkflowUnderground = lazy(() =>
import('../../pages/WorkflowUnderground')
);
const WorkflowDetails = lazy(() => import('../../pages/WorkflowDetails'));
const BrowseTemplate = lazy(() =>
import('../../components/Sections/ChaosWorkflows/BrowseTemplate')
);
@ -60,18 +58,27 @@ const Routes: React.FC<RoutesProps> = ({ userData, isProjectAvailable }) => {
<Route exact path="/login" component={LoginPage} />
<Route exact path="/workflows" component={Workflows} />
<Route exact path="/create-workflow" component={CreateWorkflow} />
<Route exact path="/schedule" component={SchedulePage} />
<Route
exact
path="/workflows/workflow-underground"
component={WorkflowUnderground}
/>
<Route exact path="/community" component={Community} />
<Route
exact
path="/workflows/:workflowName"
component={WorkflowDetails}
/>
<Route
exact
path="/workflows/:workflowName/details"
component={WorkflowDetails}
/>
<Route
exact
path="/workflows/:scheduleId/schedule"
component={SchedulePage}
/>
<Route
exact
path="/workflows/:templateName/template"
component={BrowseTemplate}
/>
<Route exact path="/community" component={Community} />
<Route exact path="/settings" component={Settings} />
<Route exact path="/404" component={ErrorPage} />
<Redirect to="/404" />

View File

@ -1,22 +1,22 @@
import { useQuery } from '@apollo/client';
import { Box, Divider } from '@material-ui/core';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import React, { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { useQuery } from '@apollo/client';
import CustomBreadCrumbs from '../../../components/BreadCrumbs';
import { GET_USER } from '../../../graphql';
import { Message, NotificationIds, Project } from '../../../models/header';
import {
UserData,
CurrentUserDetails,
CurrentUserDedtailsVars,
CurrentUserDetails,
UserData,
} from '../../../models/user';
import { RootState } from '../../../redux/reducers';
import CustomBreadCrumbs from '../../../components/BreadCrumbs';
import NotificationsDropdown from './NotificationDropdown';
import ProfileDropdownSection from './ProfileDropdownSection';
import useStyles from './styles';
import { GET_USER } from '../../../graphql';
const Header: React.FC = () => {
const classes = useStyles();
@ -44,7 +44,6 @@ const Header: React.FC = () => {
setSelectedProject(selectedProjectID);
// send POST request with #selectedProjectID to update active
// project on db or persist it in redux or cookie.
// window.location.reload(false);
};
const fetchRandomProjects = useCallback(() => {

View File

@ -1,7 +1,10 @@
import { AnalyticsAction } from './analytics';
import { NodeSelectionAction } from './nodeSelection';
import { UserAction } from './user';
import { WorkflowAction } from './workflow';
export * from './predefinedWorkflow';
export type Action = UserAction | AnalyticsAction | WorkflowAction;
export type Action =
| UserAction
| AnalyticsAction
| WorkflowAction
| NodeSelectionAction;

View File

@ -0,0 +1,15 @@
import { Node } from './workflowData';
export enum NodeSelectionActions {
SELECT_NODE = 'SELECT_NODE',
}
interface NodeSelectionActionType<T, P> {
type: T;
payload: P;
}
export type NodeSelectionAction = NodeSelectionActionType<
typeof NodeSelectionActions.SELECT_NODE,
Node
>;

View File

@ -23,6 +23,6 @@ export interface workflowDetails {
description: string;
}
export interface SelectWorkflowCallBackType {
interface SelectWorkflowCallBackType {
(selectedWorkflow: workflowDetails): void;
}

View File

@ -7,6 +7,10 @@ export interface Node {
type: string;
}
export interface Nodes {
[index: string]: Node;
}
export interface ExecutionData {
event_type: string;
uid: string;
@ -16,7 +20,7 @@ export interface ExecutionData {
phase: string;
startedAt: string;
finishedAt: string;
nodes: object;
nodes: Nodes;
}
export interface WorkflowRun {

View File

@ -9,20 +9,15 @@ import {
} from '@material-ui/core';
import React from 'react';
import { useTranslation } from 'react-i18next';
import CustomDate from '../../components/DateTime/CustomDate/index';
import CustomTime from '../../components/DateTime/CustomTime/index';
import SetTime from './SetTime/index';
import useStyles from './styles';
import Scaffold from '../../containers/layouts/Scaffold';
import ButtonFilled from '../../components/Button/ButtonFilled';
import ButtonOutline from '../../components/Button/ButtonOutline';
import CustomDate from '../../components/DateTime/CustomDate/index';
import CustomTime from '../../components/DateTime/CustomTime/index';
import Scaffold from '../../containers/layouts/Scaffold';
import SetTime from './SetTime/index';
import useStyles from './styles';
// To be changed to a Location Generic
interface WorkflowScheduleProps {
location: any;
}
const SchedulePage: React.FC<WorkflowScheduleProps> = () => {
const SchedulePage: React.FC = () => {
const { t } = useTranslation();
const start = 0;
const end = 10;

View File

@ -0,0 +1,112 @@
import { useQuery } from '@apollo/client';
import { Typography } from '@material-ui/core';
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import Loader from '../../components/Loader';
import ArgoWorkflow from '../../components/Sections/WorkflowDetails/ArgoWorkflow';
import WorkflowInfo from '../../components/Sections/WorkflowDetails/WorkflowInfo';
import Scaffold from '../../containers/layouts/Scaffold';
import { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../graphql';
import {
ExecutionData,
Workflow,
WorkflowDataVars,
WorkflowSubscription,
} from '../../models/workflowData';
import { RootState } from '../../redux/reducers';
import useStyles from './styles';
const WorkflowDetails: React.FC = () => {
const classes = useStyles();
const { pathname } = useLocation();
// Getting the workflow nome from the pathname
const workflowName = pathname.split('/')[2];
// get ProjectID
const selectedProjectID = useSelector(
(state: RootState) => state.userData.selectedProjectID
);
// Query to get workflows
const { subscribeToMore, data, error } = useQuery<Workflow, WorkflowDataVars>(
WORKFLOW_DETAILS,
{ variables: { projectID: selectedProjectID } }
);
const workflow = data?.getWorkFlowRuns.filter(
(w) => w.workflow_name === workflowName
)[0];
// Using subscription to get realtime data
useEffect(() => {
if (
workflow?.execution_data &&
(JSON.parse(workflow?.execution_data) as ExecutionData).phase ===
'Running'
) {
subscribeToMore<WorkflowSubscription>({
document: WORKFLOW_EVENTS,
variables: { projectID: selectedProjectID },
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev;
const modifiedWorkflows = prev.getWorkFlowRuns.slice();
const newWorkflow = subscriptionData.data.workflowEventListener;
// Updating the query data
let i = 0;
for (; i < modifiedWorkflows.length; i++) {
if (
modifiedWorkflows[i].workflow_run_id ===
newWorkflow.workflow_run_id
) {
modifiedWorkflows[i] = newWorkflow;
break;
}
}
if (i === modifiedWorkflows.length)
modifiedWorkflows.unshift(newWorkflow);
return { ...prev, getWorkFlowRuns: modifiedWorkflows };
},
});
}
}, [data]);
return (
<Scaffold>
{workflow ? (
<div className={classes.root}>
<div className={classes.workflowGraph}>
<Typography className={classes.heading}>
{workflow.workflow_name}
</Typography>
<Typography>
Click on test to see detailed log of your workflow
</Typography>
{/* Argo Workflow DAG Graph */}
<ArgoWorkflow
nodes={
(JSON.parse(workflow.execution_data) as ExecutionData).nodes
}
/>
</div>
<WorkflowInfo
workflow_name={workflow.workflow_name}
execution_data={
JSON.parse(workflow?.execution_data) as ExecutionData
}
cluster_name={workflow.cluster_name}
/>
</div>
) : error ? (
<Typography>An error has occurred while fetching the data</Typography>
) : (
<Loader />
)}
</Scaffold>
);
};
export default WorkflowDetails;

View File

@ -3,8 +3,8 @@ import { makeStyles, Theme } from '@material-ui/core/styles';
const useStyles = makeStyles((theme: Theme) => ({
root: {
display: 'flex',
flexDirection: 'row',
marginTop: theme.spacing(3),
height: '75vh',
},
heading: {
fontSize: '2rem',

View File

@ -1,67 +0,0 @@
import { useSubscription } from '@apollo/client';
import { Typography } from '@material-ui/core';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import SideBar from '../../components/Sections/WorkflowUnderground/WorkflowRepresentation';
import Scaffold from '../../containers/layouts/Scaffold';
import { WORKFLOW_EVENTS } from '../../graphql';
import { LocationState } from '../../models/routerModel';
import {
ExecutionData,
WorkflowRun,
WorkflowSubscription,
} from '../../models/workflowData';
import capitalize from '../../utils/capitalize';
import useStyles from './styles';
import { UserData } from '../../models/user';
import { RootState } from '../../redux/reducers';
interface WorkflowUndergroundProps {
location: LocationState<WorkflowRun>;
}
const WorkflowUnderground: React.FC<WorkflowUndergroundProps> = ({
location,
}) => {
const { t } = useTranslation();
const classes = useStyles();
const userData: UserData = useSelector((state: RootState) => state.userData);
const { selectedProjectID } = userData;
const [data, setData] = useState<WorkflowRun>(location.state);
const dataSub = useSubscription<WorkflowSubscription>(WORKFLOW_EVENTS, {
variables: { projectID: selectedProjectID },
});
useEffect(() => {
const workflowCompleted: boolean =
(JSON.parse(location.state.execution_data) as ExecutionData).phase ===
'Succeeded';
if (workflowCompleted && location.state.execution_data !== undefined)
setData(location.state);
else setData(dataSub.data?.workflowEventListener ?? location.state);
}, [location.state, dataSub.data, data.execution_data]);
return (
<Scaffold>
<div className={classes.root}>
<div className={classes.workflowGraph}>
<Typography className={classes.heading}>
{data.workflow_name
.split('-')
.map((text) => `${capitalize(text)} `)}
</Typography>
<Typography>{t('workflowUnderground.heading')}</Typography>
</div>
<SideBar
workflow_name={data.workflow_name}
execution_data={data.execution_data}
cluster_name={data.cluster_name}
/>
</div>
</Scaffold>
);
};
export default WorkflowUnderground;

View File

@ -1,11 +1,64 @@
import { AppBar, Typography } from '@material-ui/core';
import Tabs from '@material-ui/core/Tabs';
import React from 'react';
import WorkflowTabs from '../../components/Sections/ChaosWorkflows/WorkflowTabs';
import ButtonFilled from '../../components/Button/ButtonFilled';
import BrowseSchedule from '../../components/Sections/ChaosWorkflows/BrowseSchedule';
import BrowseWorkflow from '../../components/Sections/ChaosWorkflows/BrowseWorkflow';
import Templates from '../../components/Sections/ChaosWorkflows/Templates';
import { StyledTab, TabPanel } from '../../components/Tabs';
import Scaffold from '../../containers/layouts/Scaffold';
import { history } from '../../redux/configureStore';
import useStyles from './styles';
const Workflows = () => {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
setValue(newValue);
};
return (
<Scaffold>
<WorkflowTabs />
<section className="Header section">
<div className={classes.header}>
<Typography variant="h4">Chaos Workflows</Typography>
<div className={classes.scheduleBtn}>
<ButtonFilled
isPrimary={false}
handleClick={() => history.push('/create-workflow')}
>
<>Schedule a workflow</>
</ButtonFilled>
</div>
</div>
</section>
<AppBar position="static" color="default" className={classes.appBar}>
<Tabs
value={value}
onChange={handleChange}
indicatorColor="secondary"
textColor="secondary"
variant="fullWidth"
>
<StyledTab label="Browse workflows" />
<StyledTab label="Schedules" />
<StyledTab label="Templates" />
<StyledTab label="Analytics" />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
<BrowseWorkflow />
</TabPanel>
<TabPanel value={value} index={1}>
<BrowseSchedule />
</TabPanel>
<TabPanel value={value} index={2}>
<Templates />
</TabPanel>
<TabPanel value={value} index={3}>
Analytics comming soon
</TabPanel>
</Scaffold>
);
};

View File

@ -1,13 +1,19 @@
import { makeStyles } from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles((theme) => ({
heading: {
header: {
width: '100%',
display: 'flex',
flexDirection: 'row',
marginTop: theme.spacing(2.5),
marginLeft: theme.spacing(11),
marginBottom: theme.spacing(0),
fontFamily: 'Ubuntu',
fontSize: '2.25rem',
display: 'inline-block',
marginBottom: theme.spacing(2.5),
},
scheduleBtn: {
marginLeft: 'auto',
},
appBar: {
background: 'transparent',
boxShadow: 'none',
},
}));

View File

@ -0,0 +1,13 @@
/* eslint-disable import/prefer-default-export */
import {
NodeSelectionAction,
NodeSelectionActions,
} from '../../models/nodeSelection';
import { Node } from '../../models/workflowData';
export function selectNode(node: Node): NodeSelectionAction {
return {
type: NodeSelectionActions.SELECT_NODE,
payload: node,
};
}

View File

@ -25,7 +25,7 @@ if (dev) {
middleware = composeWithDevTools(middleware);
}
const persistedReducer = persistReducer(persistConfig, rootReducer(history));
const persistedReducer = persistReducer(persistConfig, rootReducer());
export default () => {
const store = createStore(persistedReducer, {}, middleware) as any;

View File

@ -1,9 +1,10 @@
import { History } from 'history'; // eslint-disable-line import/no-extraneous-dependencies
import { combineReducers } from 'redux';
import { CommunityData } from '../../models/analytics';
import { UserData } from '../../models/user';
import { WorkflowData } from '../../models/workflow';
import { Node } from '../../models/workflowData';
import * as analyticsReducer from './analytics';
import * as nodeSelectionReducer from './nodeSelection';
import * as userReducer from './user';
import * as workflowReducer from './workflow';
@ -11,13 +12,13 @@ export interface RootState {
communityData: CommunityData;
userData: UserData;
workflowData: WorkflowData;
selectedNode: Node;
}
export default (
history: History // eslint-disable-line @typescript-eslint/no-unused-vars
) =>
export default () =>
combineReducers({
...analyticsReducer,
...userReducer,
...workflowReducer,
...nodeSelectionReducer,
});

View File

@ -0,0 +1,26 @@
import {
NodeSelectionAction,
NodeSelectionActions,
} from '../../models/nodeSelection';
import { Node } from '../../models/workflowData';
import createReducer from './createReducer';
const initialState: Node = {
children: null,
finishedAt: '',
name: '',
phase: '',
startedAt: '',
type: '',
};
export const selectedNode = createReducer<Node>(initialState, {
[NodeSelectionActions.SELECT_NODE](state: Node, action: NodeSelectionAction) {
return {
...state,
...action.payload,
};
},
});
export default selectedNode;

View File

@ -24,6 +24,7 @@ declare module '@material-ui/core/styles/createPalette' {
customColors: {
white: (opacity: number) => string;
black: (opacity: number) => string;
gray: string;
};
input: {
disabled: string;
@ -34,6 +35,7 @@ declare module '@material-ui/core/styles/createPalette' {
customColors?: {
white?: (opacity: number) => string;
black?: (opacity: number) => string;
gray?: string;
};
input?: {
disabled?: string;
@ -80,15 +82,16 @@ function customTheme(options: ThemeOptions) {
white: (opacity: number): string => {
let op = opacity;
if (op < 0) op = 0;
if (op > 100) op = 100;
if (op > 1) op = 1;
return `rgba(255, 255, 255, ${op})`;
},
black: (opacity: number): string => {
let op = opacity;
if (op < 0) op = 0;
if (op > 100) op = 100;
if (op > 1) op = 1;
return `rgba(0, 0, 0, ${op})`;
},
gray: '#5D6173',
},
},
typography: {