diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f57dba1c3d..cec72e55a0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Features 1. [11677](https://github.com/influxdata/influxdb/pull/11677): Add instructions button to view `$INFLUX_TOKEN` setup for telegraf configs 1. [11693](https://github.com/influxdata/influxdb/pull/11693): Save the $INFLUX_TOKEN environmental variable in telegraf configs +1. [11700](https://github.com/influxdata/influxdb/pull/11700): Update Tasks tab on Org page to look like Tasks Page ## Bug Fixes 1. [11678](https://github.com/influxdata/influxdb/pull/11678): Update the System Telegraf Plugin bundle to include the swap plugin diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 4b74caa2c6a..f5e9d220ff7 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -25,6 +25,8 @@ import TaskPage from 'src/tasks/containers/TaskPage' import TasksPage from 'src/tasks/containers/TasksPage' import OrganizationsIndex from 'src/organizations/containers/OrganizationsIndex' import OrganizationView from 'src/organizations/containers/OrganizationView' +import OrgTaskPage from 'src/organizations/components/OrgTaskPage' +import OrgTaskEditPage from 'src/organizations/components/OrgTaskEditPage' import TaskEditPage from 'src/tasks/containers/TaskEditPage' import DashboardPage from 'src/dashboards/components/DashboardPage' import DashboardsIndex from 'src/dashboards/components/dashboard_index/DashboardsIndex' @@ -101,10 +103,20 @@ class Root extends PureComponent { - + + + + + diff --git a/ui/src/organizations/components/OrgTaskEditPage.tsx b/ui/src/organizations/components/OrgTaskEditPage.tsx new file mode 100644 index 00000000000..484842b2db4 --- /dev/null +++ b/ui/src/organizations/components/OrgTaskEditPage.tsx @@ -0,0 +1,189 @@ +// Libraries +import _ from 'lodash' +import React, {PureComponent, ChangeEvent} from 'react' +import {InjectedRouter} from 'react-router' +import {connect} from 'react-redux' + +// Components +import TaskForm from 'src/tasks/components/TaskForm' +import TaskHeader from 'src/tasks/components/TaskHeader' +import {Page} from 'src/pageLayout' +import FluxEditor from 'src/shared/components/FluxEditor' + +// Actions +import { + updateScript, + selectTaskByID, + setCurrentScript, + cancel, + setTaskOption, + clearTask, + setAllTaskOptions, +} from 'src/tasks/actions/v2' + +// Types +import {Organization} from '@influxdata/influx' +import {Links} from 'src/types/v2/links' +import {State as TasksState} from 'src/tasks/reducers/v2' +import { + TaskOptions, + TaskOptionKeys, + TaskSchedule, +} from 'src/utils/taskOptionsToFluxScript' +import {Task} from 'src/tasks/containers/TasksPage' + +interface PassedInProps { + router: InjectedRouter + params: {id: string; orgID: string} +} + +interface ConnectStateProps { + orgs: Organization[] + taskOptions: TaskOptions + currentTask: Task + currentScript: string + tasksLink: string +} + +interface ConnectDispatchProps { + setTaskOption: typeof setTaskOption + setCurrentScript: typeof setCurrentScript + updateScript: typeof updateScript + cancel: typeof cancel + selectTaskByID: typeof selectTaskByID + clearTask: typeof clearTask + setAllTaskOptions: typeof setAllTaskOptions +} + +class OrgTaskEditPage extends PureComponent< + PassedInProps & ConnectStateProps & ConnectDispatchProps +> { + constructor(props) { + super(props) + } + + public async componentDidMount() { + const { + params: {id, orgID}, + } = this.props + await this.props.selectTaskByID(id, `organizations/${orgID}/tasks_tab/`) + + const {currentTask} = this.props + + this.props.setAllTaskOptions(currentTask) + } + + public componentWillUnmount() { + this.props.clearTask() + } + + public render(): JSX.Element { + const {currentScript, taskOptions, orgs} = this.props + + return ( + + + +
+
+ +
+
+ +
+
+
+
+ ) + } + + private get isFormValid(): boolean { + const { + taskOptions: {name, cron, interval}, + currentScript, + } = this.props + + const hasSchedule = !!cron || !!interval + return hasSchedule && !!name && !!currentScript + } + + private handleChangeScript = (script: string) => { + this.props.setCurrentScript(script) + } + + private handleChangeScheduleType = (schedule: TaskSchedule) => { + this.props.setTaskOption({key: 'taskScheduleType', value: schedule}) + } + + private handleSave = () => { + const {params} = this.props + + this.props.updateScript(`organizations/${params.orgID}/tasks_tab/`) + } + + private handleCancel = () => { + this.props.cancel() + } + + private handleChangeInput = (e: ChangeEvent) => { + const {name, value} = e.target + const key = name as TaskOptionKeys + + this.props.setTaskOption({key, value}) + } + + private handleChangeTaskOrgID = (orgID: string) => { + this.props.setTaskOption({key: 'orgID', value: orgID}) + } +} + +const mstp = ({ + tasks, + links, + orgs, +}: { + tasks: TasksState + links: Links + orgs: Organization[] +}): ConnectStateProps => { + return { + orgs, + taskOptions: tasks.taskOptions, + currentScript: tasks.currentScript, + tasksLink: links.tasks, + currentTask: tasks.currentTask, + } +} + +const mdtp: ConnectDispatchProps = { + setTaskOption, + setCurrentScript, + updateScript, + cancel, + selectTaskByID, + setAllTaskOptions, + clearTask, +} + +export default connect( + mstp, + mdtp +)(OrgTaskEditPage) diff --git a/ui/src/organizations/components/OrgTaskPage.tsx b/ui/src/organizations/components/OrgTaskPage.tsx new file mode 100644 index 00000000000..40d589b1312 --- /dev/null +++ b/ui/src/organizations/components/OrgTaskPage.tsx @@ -0,0 +1,181 @@ +import _ from 'lodash' +import React, {PureComponent, ChangeEvent} from 'react' +import {InjectedRouter} from 'react-router' +import {connect} from 'react-redux' + +// components +import TaskForm from 'src/tasks/components/TaskForm' +import TaskHeader from 'src/tasks/components/TaskHeader' +import FluxEditor from 'src/shared/components/FluxEditor' +import {Page} from 'src/pageLayout' + +// actions +import {State as TasksState} from 'src/tasks/reducers/v2' +import { + setNewScript, + saveNewScript, + setTaskOption, + clearTask, + cancel, +} from 'src/tasks/actions/v2' +// types +import {Links} from 'src/types/v2/links' +import {Organization} from 'src/types/v2' +import { + TaskOptions, + TaskOptionKeys, + TaskSchedule, +} from 'src/utils/taskOptionsToFluxScript' + +// Styles +import 'src/tasks/components/TaskForm.scss' + +interface PassedInProps { + router: InjectedRouter + params: {orgID: string} +} + +interface ConnectStateProps { + orgs: Organization[] + taskOptions: TaskOptions + newScript: string + tasksLink: string +} + +interface ConnectDispatchProps { + setNewScript: typeof setNewScript + saveNewScript: typeof saveNewScript + setTaskOption: typeof setTaskOption + clearTask: typeof clearTask + cancel: typeof cancel +} + +class OrgTaskPage extends PureComponent< + PassedInProps & ConnectStateProps & ConnectDispatchProps +> { + constructor(props) { + super(props) + } + + public componentDidMount() { + this.props.setTaskOption({ + key: 'taskScheduleType', + value: TaskSchedule.interval, + }) + } + + public componentWillUnmount() { + this.props.clearTask() + } + + public render(): JSX.Element { + const {newScript, taskOptions, orgs} = this.props + + return ( + + + +
+
+ +
+
+ +
+
+
+
+ ) + } + + private get isFormValid(): boolean { + const { + taskOptions: {name, cron, interval}, + newScript, + } = this.props + + const hasSchedule = !!cron || !!interval + return hasSchedule && !!name && !!newScript + } + + private handleChangeScript = (script: string) => { + this.props.setNewScript(script) + } + + private handleChangeScheduleType = (schedule: TaskSchedule) => { + this.props.setTaskOption({key: 'taskScheduleType', value: schedule}) + } + + private handleSave = () => { + const {params, newScript, taskOptions} = this.props + + this.props.saveNewScript( + newScript, + taskOptions, + `organizations/${params.orgID}/tasks_tab/` + ) + } + + private handleCancel = () => { + this.props.cancel() + } + + private handleChangeInput = (e: ChangeEvent) => { + const {name, value} = e.target + const key = name as TaskOptionKeys + + this.props.setTaskOption({key, value}) + } + + private handleChangeTaskOrgID = (orgID: string) => { + this.props.setTaskOption({key: 'orgID', value: orgID}) + } +} + +const mstp = ({ + tasks, + links, + orgs, +}: { + tasks: TasksState + links: Links + orgs: Organization[] +}): ConnectStateProps => { + return { + orgs, + taskOptions: tasks.taskOptions, + newScript: tasks.newScript, + tasksLink: links.tasks, + } +} + +const mdtp: ConnectDispatchProps = { + setNewScript, + saveNewScript, + setTaskOption, + clearTask, + cancel, +} + +export default connect( + mstp, + mdtp +)(OrgTaskPage) diff --git a/ui/src/organizations/components/OrgTasksPage.tsx b/ui/src/organizations/components/OrgTasksPage.tsx index 66c95274de6..04009447520 100644 --- a/ui/src/organizations/components/OrgTasksPage.tsx +++ b/ui/src/organizations/components/OrgTasksPage.tsx @@ -35,6 +35,7 @@ import {AppState} from 'src/types/v2' interface PassedInProps { tasks: Task[] orgName: string + orgID: string onChange: () => void router: InjectedRouter } @@ -123,7 +124,7 @@ class OrgTasksPage extends PureComponent { onDelete={this.handleDelete} onCreate={this.handleCreateTask} onClone={this.handleClone} - onSelect={selectTask} + onSelect={this.handleSelectTask} onAddTaskLabels={onAddTaskLabels} onRemoveTaskLabels={onRemoveTaskLabels} /> @@ -135,6 +136,12 @@ class OrgTasksPage extends PureComponent { ) } + private handleSelectTask = (task: Task) => { + const {selectTask, orgID} = this.props + + selectTask(task, `organizations/${orgID}/tasks_tab/${task.id}`) + } + private get filteredTasks() { const {tasks, showInactive} = this.props if (showInactive) { @@ -175,9 +182,9 @@ class OrgTasksPage extends PureComponent { } private handleCreateTask = () => { - const {router} = this.props + const {router, orgID} = this.props - router.push('/tasks/new') + router.push(`/organizations/${orgID}/tasks_tab/new`) } private handleToggleOverlay = () => { @@ -209,6 +216,7 @@ class OrgTasksPage extends PureComponent { this.props.setSearchTerm(e.target.value) } } + const mstp = ({ tasks: {searchTerm, showInactive}, orgs, @@ -219,6 +227,7 @@ const mstp = ({ orgs, } } + const mdtp: ConnectedDispatchProps = { updateTaskStatus, deleteTask, @@ -230,6 +239,7 @@ const mdtp: ConnectedDispatchProps = { onRemoveTaskLabels: removeTaskLabelsAsync, onAddTaskLabels: addTaskLabelsAsync, } + export default connect< ConnectedStateProps, ConnectedDispatchProps, diff --git a/ui/src/organizations/containers/OrganizationView.tsx b/ui/src/organizations/containers/OrganizationView.tsx index e8792ed3ae6..db1f322d199 100644 --- a/ui/src/organizations/containers/OrganizationView.tsx +++ b/ui/src/organizations/containers/OrganizationView.tsx @@ -181,6 +181,7 @@ class OrganizationView extends PureComponent { diff --git a/ui/src/tasks/actions/v2/index.ts b/ui/src/tasks/actions/v2/index.ts index 8a33d907f34..889c3855fdc 100644 --- a/ui/src/tasks/actions/v2/index.ts +++ b/ui/src/tasks/actions/v2/index.ts @@ -1,5 +1,5 @@ // Libraries -import {push} from 'react-router-redux' +import {push, goBack} from 'react-router-redux' import _ from 'lodash' // APIs @@ -288,7 +288,7 @@ export const populateTasks = () => async ( } } -export const selectTaskByID = (id: string) => async ( +export const selectTaskByID = (id: string, route?: string) => async ( dispatch, getState: GetStateFunc ): Promise => { @@ -301,26 +301,37 @@ export const selectTaskByID = (id: string) => async ( return dispatch(setCurrentTask({...task, organization: org})) } catch (e) { console.error(e) - dispatch(goToTasks()) + dispatch(goToTasks(route)) const message = getErrorMessage(e) dispatch(notify(taskNotFound(message))) } } -export const selectTask = (task: Task) => async dispatch => { +export const selectTask = (task: Task, route?: string) => async dispatch => { + if (route) { + dispatch(push(route)) + return + } dispatch(push(`/tasks/${task.id}`)) } -export const goToTasks = () => async dispatch => { +export const goToTasks = (route?: string) => async dispatch => { + if (route) { + dispatch(push(route)) + return + } dispatch(push('/tasks')) } -export const cancelUpdateTask = () => async dispatch => { +export const cancel = () => async dispatch => { dispatch(setCurrentTask(null)) - dispatch(goToTasks()) + dispatch(goBack()) } -export const updateScript = () => async (dispatch, getState: GetStateFunc) => { +export const updateScript = (route?: string) => async ( + dispatch, + getState: GetStateFunc +) => { try { const { tasks: {currentScript: script, currentTask: task, taskOptions}, @@ -341,7 +352,7 @@ export const updateScript = () => async (dispatch, getState: GetStateFunc) => { await client.tasks.update(task.id, updatedTask) dispatch(setCurrentTask(null)) - dispatch(goToTasks()) + dispatch(goToTasks(route)) dispatch(notify(taskUpdateSuccess())) } catch (e) { console.error(e) @@ -352,7 +363,8 @@ export const updateScript = () => async (dispatch, getState: GetStateFunc) => { export const saveNewScript = ( script: string, - taskOptions: TaskOptions + taskOptions: TaskOptions, + route?: string ) => async (dispatch, getState: GetStateFunc): Promise => { try { const {orgs} = await getState() @@ -375,8 +387,8 @@ export const saveNewScript = ( dispatch(setNewScript('')) dispatch(clearTask()) - dispatch(goToTasks()) dispatch(populateTasks()) + dispatch(goToTasks(route)) dispatch(notify(taskCreatedSuccess())) } catch (e) { console.error(e) diff --git a/ui/src/tasks/containers/TaskEditPage.tsx b/ui/src/tasks/containers/TaskEditPage.tsx index bbe9080ca2f..1850863b1ea 100644 --- a/ui/src/tasks/containers/TaskEditPage.tsx +++ b/ui/src/tasks/containers/TaskEditPage.tsx @@ -15,14 +15,14 @@ import { updateScript, selectTaskByID, setCurrentScript, - cancelUpdateTask, + cancel, setTaskOption, clearTask, setAllTaskOptions, } from 'src/tasks/actions/v2' // Types -import {Task as TaskAPI, User, Organization} from '@influxdata/influx' +import {Organization} from '@influxdata/influx' import {Links} from 'src/types/v2/links' import {State as TasksState} from 'src/tasks/reducers/v2' import { @@ -30,12 +30,7 @@ import { TaskOptionKeys, TaskSchedule, } from 'src/utils/taskOptionsToFluxScript' - -interface Task extends TaskAPI { - organization: Organization - owner?: User - offset?: string -} +import {Task} from 'src/tasks/containers/TasksPage' interface PassedInProps { router: InjectedRouter @@ -54,13 +49,13 @@ interface ConnectDispatchProps { setTaskOption: typeof setTaskOption setCurrentScript: typeof setCurrentScript updateScript: typeof updateScript - cancelUpdateTask: typeof cancelUpdateTask + cancel: typeof cancel selectTaskByID: typeof selectTaskByID clearTask: typeof clearTask setAllTaskOptions: typeof setAllTaskOptions } -class TaskPage extends PureComponent< +class TaskEditPage extends PureComponent< PassedInProps & ConnectStateProps & ConnectDispatchProps > { constructor(props) { @@ -143,7 +138,7 @@ class TaskPage extends PureComponent< } private handleCancel = () => { - this.props.cancelUpdateTask() + this.props.cancel() } private handleChangeInput = (e: ChangeEvent) => { @@ -180,7 +175,7 @@ const mdtp: ConnectDispatchProps = { setTaskOption, setCurrentScript, updateScript, - cancelUpdateTask, + cancel, selectTaskByID, setAllTaskOptions, clearTask, @@ -189,4 +184,4 @@ const mdtp: ConnectDispatchProps = { export default connect( mstp, mdtp -)(TaskPage) +)(TaskEditPage) diff --git a/ui/src/tasks/containers/TaskPage.tsx b/ui/src/tasks/containers/TaskPage.tsx index 74d2e08bec6..a0ccae902b7 100644 --- a/ui/src/tasks/containers/TaskPage.tsx +++ b/ui/src/tasks/containers/TaskPage.tsx @@ -14,9 +14,9 @@ import {State as TasksState} from 'src/tasks/reducers/v2' import { setNewScript, saveNewScript, - goToTasks, setTaskOption, clearTask, + cancel, } from 'src/tasks/actions/v2' // types import {Links} from 'src/types/v2/links' @@ -44,9 +44,9 @@ interface ConnectStateProps { interface ConnectDispatchProps { setNewScript: typeof setNewScript saveNewScript: typeof saveNewScript - goToTasks: typeof goToTasks setTaskOption: typeof setTaskOption clearTask: typeof clearTask + cancel: typeof cancel } class TaskPage extends PureComponent< @@ -130,7 +130,7 @@ class TaskPage extends PureComponent< } private handleCancel = () => { - this.props.goToTasks() + this.props.cancel() } private handleChangeInput = (e: ChangeEvent) => { @@ -165,9 +165,9 @@ const mstp = ({ const mdtp: ConnectDispatchProps = { setNewScript, saveNewScript, - goToTasks, setTaskOption, clearTask, + cancel, } export default connect(