User can update a task
parent
58b9adbb52
commit
8b8733d171
|
@ -23,6 +23,7 @@ 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 TaskEditPage from 'src/tasks/containers/TaskEditPage'
|
||||
import {DashboardsPage, DashboardPage} from 'src/dashboards'
|
||||
import DataExplorerPage from 'src/dataExplorer/components/DataExplorerPage'
|
||||
import {SourcePage, ManageSources} from 'src/sources'
|
||||
|
@ -96,6 +97,7 @@ class Root extends PureComponent {
|
|||
component={OrganizationView}
|
||||
/>
|
||||
<Route path="tasks/new" component={TaskPage} />
|
||||
<Route path="tasks/:id" component={TaskEditPage} />
|
||||
<Route path="sources/new" component={SourcePage} />
|
||||
<Route
|
||||
path="data-explorer"
|
||||
|
|
|
@ -17,6 +17,11 @@ export const taskNotCreated = (): Notification => ({
|
|||
message: 'Failed to create new task',
|
||||
})
|
||||
|
||||
export const taskNotFound = (): Notification => ({
|
||||
...defaultErrorNotification,
|
||||
message: 'Failed to find task',
|
||||
})
|
||||
|
||||
export const tasksFetchFailed = (): Notification => ({
|
||||
...defaultErrorNotification,
|
||||
message: 'Failed to get tasks from server',
|
||||
|
@ -26,3 +31,8 @@ export const taskDeleteFailed = (): Notification => ({
|
|||
...defaultErrorNotification,
|
||||
message: 'Failed to delete task',
|
||||
})
|
||||
|
||||
export const taskUpdateFailed = (): Notification => ({
|
||||
...defaultErrorNotification,
|
||||
message: 'Failed to update task',
|
||||
})
|
||||
|
|
|
@ -4,7 +4,9 @@ import {push} from 'react-router-redux'
|
|||
import {Task} from 'src/types/v2/tasks'
|
||||
import {
|
||||
submitNewTask,
|
||||
updateTaskFlux,
|
||||
getUserTasks,
|
||||
getTask,
|
||||
deleteTask as deleteTaskAPI,
|
||||
} from 'src/tasks/api/v2'
|
||||
import {getMe} from 'src/shared/apis/v2/user'
|
||||
|
@ -13,15 +15,25 @@ import {
|
|||
taskNotCreated,
|
||||
tasksFetchFailed,
|
||||
taskDeleteFailed,
|
||||
taskNotFound,
|
||||
taskUpdateFailed,
|
||||
} from 'src/shared/copy/v2/notifications'
|
||||
|
||||
export type Action = SetNewScript | SetTasks | SetSearchTerm
|
||||
export type Action =
|
||||
| SetNewScript
|
||||
| SetTasks
|
||||
| SetSearchTerm
|
||||
| SetCurrentScript
|
||||
| SetCurrentTask
|
||||
|
||||
type GetStateFunc = () => Promise<AppState>
|
||||
|
||||
export enum ActionTypes {
|
||||
SetNewScript = 'SET_NEW_SCRIPT',
|
||||
SetTasks = 'SET_TASKS',
|
||||
SetSearchTerm = 'SET_TASKS_SEARCH_TERM',
|
||||
SetCurrentScript = 'SET_CURRENT_SCRIPT',
|
||||
SetCurrentTask = 'SET_CURRENT_TASK',
|
||||
}
|
||||
|
||||
export interface SetNewScript {
|
||||
|
@ -30,6 +42,18 @@ export interface SetNewScript {
|
|||
script: string
|
||||
}
|
||||
}
|
||||
export interface SetCurrentScript {
|
||||
type: ActionTypes.SetCurrentScript
|
||||
payload: {
|
||||
script: string
|
||||
}
|
||||
}
|
||||
export interface SetCurrentTask {
|
||||
type: ActionTypes.SetCurrentTask
|
||||
payload: {
|
||||
task: Task
|
||||
}
|
||||
}
|
||||
|
||||
export interface SetTasks {
|
||||
type: ActionTypes.SetTasks
|
||||
|
@ -50,6 +74,16 @@ export const setNewScript = (script: string): SetNewScript => ({
|
|||
payload: {script},
|
||||
})
|
||||
|
||||
export const setCurrentScript = (script: string): SetCurrentScript => ({
|
||||
type: ActionTypes.SetCurrentScript,
|
||||
payload: {script},
|
||||
})
|
||||
|
||||
export const setCurrentTask = (task: Task): SetCurrentTask => ({
|
||||
type: ActionTypes.SetCurrentTask,
|
||||
payload: {task},
|
||||
})
|
||||
|
||||
export const setTasks = (tasks: Task[]): SetTasks => ({
|
||||
type: ActionTypes.SetTasks,
|
||||
payload: {tasks},
|
||||
|
@ -105,6 +139,55 @@ export const populateTasks = () => async (
|
|||
}
|
||||
}
|
||||
|
||||
export const selectTaskByID = (id: string) => async (
|
||||
dispatch,
|
||||
getState: GetStateFunc
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const {
|
||||
links: {tasks: url},
|
||||
} = await getState()
|
||||
|
||||
const task = await getTask(url, id)
|
||||
|
||||
return dispatch(setCurrentTask(task))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
dispatch(goToTasks())
|
||||
dispatch(notify(taskNotFound()))
|
||||
}
|
||||
}
|
||||
|
||||
export const selectTask = (task: Task) => async dispatch => {
|
||||
dispatch(push(`/tasks/${task.id}`))
|
||||
}
|
||||
|
||||
export const goToTasks = () => async dispatch => {
|
||||
dispatch(push('/tasks'))
|
||||
}
|
||||
|
||||
export const cancelUpdateTask = () => async dispatch => {
|
||||
dispatch(setCurrentTask(null))
|
||||
dispatch(goToTasks())
|
||||
}
|
||||
|
||||
export const updateScript = () => async (dispatch, getState: GetStateFunc) => {
|
||||
try {
|
||||
const {
|
||||
links: {tasks: url},
|
||||
tasks: {currentScript: script, currentTask: task},
|
||||
} = await getState()
|
||||
|
||||
await updateTaskFlux(url, task.id, script)
|
||||
|
||||
dispatch(setCurrentTask(null))
|
||||
dispatch(goToTasks())
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
dispatch(notify(taskUpdateFailed()))
|
||||
}
|
||||
}
|
||||
|
||||
export const saveNewScript = () => async (
|
||||
dispatch,
|
||||
getState: GetStateFunc
|
||||
|
@ -121,7 +204,7 @@ export const saveNewScript = () => async (
|
|||
await submitNewTask(url, user, orgs[0], script)
|
||||
|
||||
dispatch(setNewScript(''))
|
||||
dispatch(push('/tasks'))
|
||||
dispatch(goToTasks())
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
dispatch(notify(taskNotCreated()))
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import AJAX from 'src/utils/ajax'
|
||||
import {Task} from 'src/types/v2/tasks'
|
||||
|
||||
export const submitNewTask = async (url, owner, org, flux) => {
|
||||
export const submitNewTask = async (
|
||||
url,
|
||||
owner,
|
||||
org,
|
||||
flux: string
|
||||
): Promise<Task> => {
|
||||
const request = {
|
||||
flux,
|
||||
organizationId: org.id,
|
||||
|
@ -13,7 +19,18 @@ export const submitNewTask = async (url, owner, org, flux) => {
|
|||
return data
|
||||
}
|
||||
|
||||
export const getUserTasks = async (url, user) => {
|
||||
export const updateTaskFlux = async (url, id, flux: string): Promise<Task> => {
|
||||
const completeUrl = `${url}/${id}`
|
||||
const request = {
|
||||
flux,
|
||||
}
|
||||
|
||||
const {data} = await AJAX({url: completeUrl, data: request, method: 'PATCH'})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export const getUserTasks = async (url, user): Promise<Task[]> => {
|
||||
const completeUrl = `${url}?user=${user.id}`
|
||||
|
||||
const {data} = await AJAX({url: completeUrl})
|
||||
|
@ -21,6 +38,13 @@ export const getUserTasks = async (url, user) => {
|
|||
return data
|
||||
}
|
||||
|
||||
export const getTask = async (url, id): Promise<Task> => {
|
||||
const completeUrl = `${url}/${id}`
|
||||
const {data} = await AJAX({url: completeUrl})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export const deleteTask = (url: string, taskID: string) => {
|
||||
const completeUrl = `${url}/${taskID}`
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import _ from 'lodash'
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
import {Form, Columns} from 'src/clockface'
|
||||
import FluxEditor from 'src/flux/components/v2/FluxEditor'
|
||||
|
||||
interface Props {
|
||||
script: string
|
||||
onChange: (script) => void
|
||||
}
|
||||
|
||||
export default class TaskForm extends PureComponent<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<>
|
||||
<Form style={{height: '90%'}}>
|
||||
<Form.Element
|
||||
label="Script"
|
||||
colsXS={Columns.Six}
|
||||
offsetXS={Columns.Three}
|
||||
errorMessage={''}
|
||||
>
|
||||
<FluxEditor
|
||||
script={this.props.script}
|
||||
onChangeScript={this.props.onChange}
|
||||
visibility={'visible'}
|
||||
status={{text: '', type: ''}}
|
||||
onSubmitScript={_.noop}
|
||||
suggestions={[]}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import {Page} from 'src/pageLayout'
|
||||
import {ComponentColor, ComponentSize, Button} from 'src/clockface'
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
onCancel: () => void
|
||||
onSave: () => void
|
||||
}
|
||||
|
||||
export default class TaskHeader extends PureComponent<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<Page.Header fullWidth={true}>
|
||||
<Page.Header.Left>
|
||||
<Page.Title title={this.props.title} />
|
||||
</Page.Header.Left>
|
||||
<Page.Header.Right>
|
||||
<Button
|
||||
color={ComponentColor.Default}
|
||||
text="Cancel"
|
||||
onClick={this.props.onCancel}
|
||||
size={ComponentSize.Medium}
|
||||
/>
|
||||
<Button
|
||||
color={ComponentColor.Success}
|
||||
text="Save"
|
||||
onClick={this.props.onSave}
|
||||
size={ComponentSize.Medium}
|
||||
/>
|
||||
</Page.Header.Right>
|
||||
</Page.Header>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import {Task} from 'src/types/v2/tasks'
|
|||
interface Props {
|
||||
task: Task
|
||||
onDelete: (task: Task) => void
|
||||
onSelect: (task: Task) => void
|
||||
}
|
||||
|
||||
export default class TaskRow extends PureComponent<Props> {
|
||||
|
@ -23,7 +24,9 @@ export default class TaskRow extends PureComponent<Props> {
|
|||
return (
|
||||
<IndexList.Row>
|
||||
<IndexList.Cell>
|
||||
<a href="#">{task.name}</a>
|
||||
<a href="#" onClick={this.handleClick}>
|
||||
{task.name}
|
||||
</a>
|
||||
</IndexList.Cell>
|
||||
<IndexList.Cell>
|
||||
<a href="#">{task.organization.name}</a>
|
||||
|
@ -43,6 +46,12 @@ export default class TaskRow extends PureComponent<Props> {
|
|||
)
|
||||
}
|
||||
|
||||
private handleClick = e => {
|
||||
e.preventDefault()
|
||||
|
||||
this.props.onSelect(this.props.task)
|
||||
}
|
||||
|
||||
private handleDelete = () => {
|
||||
this.props.onDelete(this.props.task)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ interface Props {
|
|||
searchTerm: string
|
||||
onDelete: (task: Task) => void
|
||||
onCreate: () => void
|
||||
onSelect: (task: Task) => void
|
||||
}
|
||||
|
||||
export default class TasksList extends PureComponent<Props> {
|
||||
|
@ -41,10 +42,15 @@ export default class TasksList extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
private get rows(): JSX.Element[] {
|
||||
const {tasks, onDelete} = this.props
|
||||
const {tasks, onDelete, onSelect} = this.props
|
||||
|
||||
return tasks.map(t => (
|
||||
<TaskRow key={`task-id--${t.id}`} task={t} onDelete={onDelete} />
|
||||
<TaskRow
|
||||
key={`task-id--${t.id}`}
|
||||
task={t}
|
||||
onDelete={onDelete}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import _ from 'lodash'
|
||||
import React, {PureComponent} from 'react'
|
||||
import {InjectedRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import TaskForm from 'src/tasks/components/TaskForm'
|
||||
import TaskHeader from 'src/tasks/components/TaskHeader'
|
||||
import {Task} from 'src/types/v2/tasks'
|
||||
|
||||
import {Links} from 'src/types/v2/links'
|
||||
import {State as TasksState} from 'src/tasks/reducers/v2'
|
||||
import {
|
||||
updateScript,
|
||||
selectTaskByID,
|
||||
setCurrentScript,
|
||||
cancelUpdateTask,
|
||||
} from 'src/tasks/actions/v2'
|
||||
|
||||
interface PassedInProps {
|
||||
router: InjectedRouter
|
||||
params: {id: string}
|
||||
}
|
||||
|
||||
interface ConnectStateProps {
|
||||
currentTask: Task
|
||||
currentScript: string
|
||||
tasksLink: string
|
||||
}
|
||||
|
||||
interface ConnectDispatchProps {
|
||||
setCurrentScript: typeof setCurrentScript
|
||||
updateScript: typeof updateScript
|
||||
cancelUpdateTask: typeof cancelUpdateTask
|
||||
selectTaskByID: typeof selectTaskByID
|
||||
}
|
||||
|
||||
class TaskPage extends PureComponent<
|
||||
PassedInProps & ConnectStateProps & ConnectDispatchProps
|
||||
> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
this.props.selectTaskByID(this.props.params.id)
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {currentScript} = this.props
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<TaskHeader
|
||||
title="Update Task"
|
||||
onCancel={this.handleCancel}
|
||||
onSave={this.handleSave}
|
||||
/>
|
||||
<TaskForm script={currentScript} onChange={this.handleChange} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleChange = (script: string) => {
|
||||
this.props.setCurrentScript(script)
|
||||
}
|
||||
|
||||
private handleSave = () => {
|
||||
this.props.updateScript()
|
||||
}
|
||||
|
||||
private handleCancel = () => {
|
||||
this.props.cancelUpdateTask()
|
||||
}
|
||||
}
|
||||
|
||||
const mstp = ({
|
||||
tasks,
|
||||
links,
|
||||
}: {
|
||||
tasks: TasksState
|
||||
links: Links
|
||||
}): ConnectStateProps => {
|
||||
return {
|
||||
currentScript: tasks.currentScript,
|
||||
tasksLink: links.tasks,
|
||||
currentTask: tasks.currentTask,
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp: ConnectDispatchProps = {
|
||||
setCurrentScript,
|
||||
updateScript,
|
||||
cancelUpdateTask,
|
||||
selectTaskByID,
|
||||
}
|
||||
|
||||
export default connect<ConnectStateProps, ConnectDispatchProps, {}>(mstp, mdtp)(
|
||||
TaskPage
|
||||
)
|
|
@ -2,19 +2,13 @@ import _ from 'lodash'
|
|||
import React, {PureComponent} from 'react'
|
||||
import {InjectedRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import FluxEditor from 'src/flux/components/v2/FluxEditor'
|
||||
|
||||
import TaskForm from 'src/tasks/components/TaskForm'
|
||||
import TaskHeader from 'src/tasks/components/TaskHeader'
|
||||
|
||||
import {Links} from 'src/types/v2/links'
|
||||
import {State as TasksState} from 'src/tasks/reducers/v2'
|
||||
import {setNewScript, saveNewScript} from 'src/tasks/actions/v2'
|
||||
import {
|
||||
Form,
|
||||
Columns,
|
||||
ComponentColor,
|
||||
ComponentSize,
|
||||
Button,
|
||||
} from 'src/clockface'
|
||||
import {Page} from 'src/pageLayout'
|
||||
import {setNewScript, saveNewScript, goToTasks} from 'src/tasks/actions/v2'
|
||||
|
||||
interface PassedInProps {
|
||||
router: InjectedRouter
|
||||
|
@ -28,6 +22,7 @@ interface ConnectStateProps {
|
|||
interface ConnectDispatchProps {
|
||||
setNewScript: typeof setNewScript
|
||||
saveNewScript: typeof saveNewScript
|
||||
goToTasks: typeof goToTasks
|
||||
}
|
||||
|
||||
class TaskPage extends PureComponent<
|
||||
|
@ -42,42 +37,12 @@ class TaskPage extends PureComponent<
|
|||
|
||||
return (
|
||||
<div className="page">
|
||||
<Page.Header fullWidth={true}>
|
||||
<Page.Header.Left>
|
||||
<Page.Title title="Create Task" />
|
||||
</Page.Header.Left>
|
||||
<Page.Header.Right>
|
||||
<Button
|
||||
color={ComponentColor.Default}
|
||||
text="Cancel"
|
||||
onClick={this.handleCancel}
|
||||
size={ComponentSize.Medium}
|
||||
/>
|
||||
<Button
|
||||
color={ComponentColor.Success}
|
||||
text="Save"
|
||||
onClick={this.handleSave}
|
||||
size={ComponentSize.Medium}
|
||||
/>
|
||||
</Page.Header.Right>
|
||||
</Page.Header>
|
||||
<Form style={{height: '90%'}}>
|
||||
<Form.Element
|
||||
label="Script"
|
||||
colsXS={Columns.Six}
|
||||
offsetXS={Columns.Three}
|
||||
errorMessage={''}
|
||||
>
|
||||
<FluxEditor
|
||||
script={newScript}
|
||||
onChangeScript={this.handleChange}
|
||||
visibility={'visible'}
|
||||
status={{text: '', type: ''}}
|
||||
onSubmitScript={_.noop}
|
||||
suggestions={[]}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Form>
|
||||
<TaskHeader
|
||||
title="Create Task"
|
||||
onCancel={this.handleCancel}
|
||||
onSave={this.handleSave}
|
||||
/>
|
||||
<TaskForm script={newScript} onChange={this.handleChange} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -91,7 +56,7 @@ class TaskPage extends PureComponent<
|
|||
}
|
||||
|
||||
private handleCancel = () => {
|
||||
this.props.router.push('/tasks')
|
||||
this.props.goToTasks()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +76,7 @@ const mstp = ({
|
|||
const mdtp: ConnectDispatchProps = {
|
||||
setNewScript,
|
||||
saveNewScript,
|
||||
goToTasks,
|
||||
}
|
||||
|
||||
export default connect<ConnectStateProps, ConnectDispatchProps, {}>(mstp, mdtp)(
|
||||
|
|
|
@ -12,6 +12,7 @@ import {Page} from 'src/pageLayout'
|
|||
import {
|
||||
populateTasks,
|
||||
deleteTask,
|
||||
selectTask,
|
||||
setSearchTerm as setSearchTermAction,
|
||||
} from 'src/tasks/actions/v2'
|
||||
|
||||
|
@ -27,6 +28,7 @@ interface PassedInProps {
|
|||
interface ConnectedDispatchProps {
|
||||
populateTasks: typeof populateTasks
|
||||
deleteTask: typeof deleteTask
|
||||
selectTask: typeof selectTask
|
||||
setSearchTerm: typeof setSearchTermAction
|
||||
}
|
||||
|
||||
|
@ -61,6 +63,7 @@ class TasksPage extends PureComponent<Props> {
|
|||
tasks={this.filteredTasks}
|
||||
onDelete={this.handleDelete}
|
||||
onCreate={this.handleCreateTask}
|
||||
onSelect={this.props.selectTask}
|
||||
/>
|
||||
</div>
|
||||
</Page.Contents>
|
||||
|
@ -100,6 +103,7 @@ const mstp = ({tasks: {tasks, searchTerm}}): ConnectedStateProps => {
|
|||
const mdtp: ConnectedDispatchProps = {
|
||||
populateTasks,
|
||||
deleteTask,
|
||||
selectTask,
|
||||
setSearchTerm: setSearchTermAction,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,15 @@ import {Task} from 'src/types/v2/tasks'
|
|||
|
||||
export interface State {
|
||||
newScript: string
|
||||
currentScript: string
|
||||
currentTask?: Task
|
||||
tasks: Task[]
|
||||
searchTerm: string
|
||||
}
|
||||
|
||||
const defaultState: State = {
|
||||
newScript: '',
|
||||
currentScript: '',
|
||||
tasks: [],
|
||||
searchTerm: '',
|
||||
}
|
||||
|
@ -17,6 +20,15 @@ export default (state: State = defaultState, action: Action): State => {
|
|||
switch (action.type) {
|
||||
case ActionTypes.SetNewScript:
|
||||
return {...state, newScript: action.payload.script}
|
||||
case ActionTypes.SetCurrentScript:
|
||||
return {...state, currentScript: action.payload.script}
|
||||
case ActionTypes.SetCurrentTask:
|
||||
const {task} = action.payload
|
||||
let currentScript = ''
|
||||
if (task) {
|
||||
currentScript = task.flux
|
||||
}
|
||||
return {...state, currentScript, currentTask: task}
|
||||
case ActionTypes.SetTasks:
|
||||
return {...state, tasks: action.payload.tasks}
|
||||
case ActionTypes.SetSearchTerm:
|
||||
|
|
|
@ -10,4 +10,5 @@ export interface Task {
|
|||
id: string
|
||||
name: string
|
||||
}
|
||||
flux?: string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue