diff --git a/ui/README.md b/ui/README.md new file mode 100644 index 0000000000..ca4a146d73 --- /dev/null +++ b/ui/README.md @@ -0,0 +1,44 @@ +## Conductor UI +The UI is a standard `create-react-app` React Single Page Application (SPA). To get started, with Node 14 and `yarn` installed, first run `yarn install` from within the `/ui` directory to retrieve package dependencies. + +For more information regarding CRA configuration and usage, see the official [doc site](https://create-react-app.dev/). + +> ### For upgrading users +> The UI is designed to operate directly with the Conductor Server API. A Node `express` backend is no longer required. + +### Development Server +To run the UI on the bundled development server, run `yarn run start`. Navigate your browser to `http://localhost:5000`. + +#### Reverse Proxy configuration +The default setup expects that the Conductor Server API will be available at `localhost:8080/api`. You may select an alternate port and hostname, or rewrite the API path by editing `setupProxy.js`. Note that `setupProxy.js` is used ONLY by the development server. + +### Hosting for Production +There is no need to "build" the project unless you require compiled assets to host on a production web server. In this case, the project can be built with the command `yarn build`. The assets will be produced to `/build`. + +Your hosting environment should make the Conductor Server API available on the same domain. This avoids complexities regarding cross-origin data fetching. The default path prefix is `/api`. If a different prefix is desired, `plugins/fetch.js` can be modified to customize the API fetch behavior. + +See `docker/serverAndUI` for an `nginx` based example. + +### Customization Hooks +For ease of maintanence, a number of touch points for customization have been removed to `/plugins`. + +* `AppBarModules.jsx` +* `AppLogo.jsx` +* `env.js` +* `fetch.js` + +### Authentication +We recommend that authentication & authorization be de-coupled from the UI and handled at the web server/access gateway. + +#### Examples (WIP) +* Basic Auth (username/password) with `nginx` +* Commercial IAM Vendor +* Node `express` server with `passport.js` + + + + + + + + diff --git a/ui/build.sh b/ui/build.sh new file mode 100644 index 0000000000..f1f287c6c4 --- /dev/null +++ b/ui/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -ex +newt exec yarn install +newt exec yarn build \ No newline at end of file diff --git a/ui/dev.sh b/ui/dev.sh new file mode 100644 index 0000000000..7f5cfc68b0 --- /dev/null +++ b/ui/dev.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ex + +#Disable browser auto opening in CRA since it will be the wrong port +BROWSER=none yarn start \ No newline at end of file diff --git a/ui/public/index.html b/ui/public/index.html index dd93cd816e..2fa6155de2 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -2,18 +2,8 @@ - - Conductor UI diff --git a/ui/src/App.jsx b/ui/src/App.jsx index 66c0f574f8..196cb7d855 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -73,7 +73,7 @@ export default function App() { - + diff --git a/ui/src/components/DataTable.jsx b/ui/src/components/DataTable.jsx index fd2deb6fe5..cc64f153ae 100644 --- a/ui/src/components/DataTable.jsx +++ b/ui/src/components/DataTable.jsx @@ -99,7 +99,7 @@ export default function DataTable(props) { } if (renderer) { - internalOptions.format = (row) => renderer(_.get(row, name)); + internalOptions.format = row => renderer(_.get(row, name), row); } return { diff --git a/ui/src/pages/definitions/Task.jsx b/ui/src/pages/definitions/Task.jsx index 1decbd614d..f66ff6e3c0 100644 --- a/ui/src/pages/definitions/Task.jsx +++ b/ui/src/pages/definitions/Task.jsx @@ -66,6 +66,7 @@ export default function TaskDefinitions() { "ownerEmail", "timeoutPolicy", "retryCount", + "executions_link" ]} keyField="name" default diff --git a/ui/src/pages/definitions/Workflow.jsx b/ui/src/pages/definitions/Workflow.jsx index 3f1b8a0af1..d6126a2a25 100644 --- a/ui/src/pages/definitions/Workflow.jsx +++ b/ui/src/pages/definitions/Workflow.jsx @@ -115,7 +115,7 @@ export default function WorkflowDefinitions() { (execution ? new WorkflowDAG(execution) : null), diff --git a/ui/src/pages/execution/TaskList.jsx b/ui/src/pages/execution/TaskList.jsx index 0af16f17d7..3d1979064b 100644 --- a/ui/src/pages/execution/TaskList.jsx +++ b/ui/src/pages/execution/TaskList.jsx @@ -1,22 +1,28 @@ import React from "react"; +import { Link } from "@material-ui/core"; import { DataTable } from "../../components"; import _ from "lodash"; -const taskDetailFields = [ - { name: "seq" }, - { name: "workflowTask.name", label: "Name" }, - { name: "referenceTaskName", label: "Task Reference" }, - { name: "workflowTask.type", label: "Type" }, - { name: "scheduledTime", type: "date" }, - { name: "startTime", type: "date" }, - { name: "endTime", type: "date" }, - { name: "status" }, - { name: "updateTime", type: "date" }, - { name: "callbackAfterSeconds" }, - { name: "pollCount" }, -]; export default function TaskList({ selectedTask, tasks, dag, onClick }) { + const taskDetailFields = [ + { name: "seq", grow: 0.2 }, + { name: "taskId", renderer: (taskId, row, idx) => { + console.log(row) + return handleClick(row)}>{taskId} + }}, + { name: "workflowTask.name", id: "taskName", label: "Task Name" }, + { name: "referenceTaskName", label: "Ref" }, + { name: "workflowTask.type", id: "taskType" ,label: "Type", grow: 0.5 }, + { name: "scheduledTime", type: "date" }, + { name: "startTime", type: "date" }, + { name: "endTime", type: "date" }, + { name: "status", grow: 0.5 }, + { name: "updateTime", type: "date" }, + { name: "callbackAfterSeconds" }, + { name: "pollCount" }, + ]; + let selectedTaskIdx = -1; if (selectedTask) { const { ref, taskId } = selectedTask; @@ -32,10 +38,10 @@ export default function TaskList({ selectedTask, tasks, dag, onClick }) { if (selectedTaskIdx === -1) selectedTaskIdx = null; - const handleClick = (currentRowsSelected, allRowsSelected, rowsSelected) => { - if (!_.isEmpty(rowsSelected)) { + function handleClick (row) { + if (!_.isEmpty(row)) { if (onClick) { - const task = tasks[rowsSelected[0]]; + const task = row; const node = dag.graph.node(task.referenceTaskName); // If there are more than 1 task associated, use task ID @@ -62,21 +68,15 @@ export default function TaskList({ selectedTask, tasks, dag, onClick }) { columns={taskDetailFields} defaultShowColumns={[ "seq", - "workflowTask.name", + "taskId", + "taskName", "referenceTaskName", - "workflowTask.type", + "taskType", "startTime", "endTime", "status", ]} - localStorageKey="taskListTable" - options={{ - selectableRows: "single", - selectableRowsOnClick: true, - selectableRowsHideCheckboxes: true, - rowsSelected: [selectedTaskIdx], - onRowSelectionChange: handleClick, - }} + localStorageKey="taskListTable" /> ); } diff --git a/ui/src/plugins/constants.js b/ui/src/plugins/constants.js index 7e148d5980..e69de29bb2 100644 --- a/ui/src/plugins/constants.js +++ b/ui/src/plugins/constants.js @@ -1,2 +0,0 @@ -export const DEFAULT_TEST_STACK = "testintg"; -export const DEFAULT_PROD_STACK = "prod"; diff --git a/ui/test-karbon.sh b/ui/test-karbon.sh new file mode 100755 index 0000000000..e69de29bb2