Create org task page which uses tasks component
parent
8b991e5dc5
commit
c30d49cc57
|
@ -0,0 +1,240 @@
|
|||
// Libraries
|
||||
import React, {PureComponent, ChangeEvent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {InjectedRouter} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import {Input, IconFont} from 'src/clockface'
|
||||
import FilterList from 'src/shared/components/Filter'
|
||||
import TasksHeader from 'src/tasks/components/TasksHeader'
|
||||
import TasksList from 'src/tasks/components/TasksList'
|
||||
import {Page} from 'src/pageLayout'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {OverlayTechnology} from 'src/clockface'
|
||||
import ImportTaskOverlay from 'src/tasks/components/ImportTaskOverlay'
|
||||
|
||||
// Actions
|
||||
import {
|
||||
updateTaskStatus,
|
||||
deleteTask,
|
||||
selectTask,
|
||||
cloneTask,
|
||||
setSearchTerm as setSearchTermAction,
|
||||
setShowInactive as setShowInactiveAction,
|
||||
importScript,
|
||||
addTaskLabelsAsync,
|
||||
removeTaskLabelsAsync,
|
||||
} from 'src/tasks/actions/v2'
|
||||
|
||||
// Types
|
||||
import {Task as TaskAPI, Organization} from '@influxdata/influx'
|
||||
import {Task} from 'src/tasks/containers/TasksPage'
|
||||
import {AppState} from 'src/types/v2'
|
||||
|
||||
interface PassedInProps {
|
||||
tasks: Task[]
|
||||
orgName: string
|
||||
onChange: () => void
|
||||
router: InjectedRouter
|
||||
}
|
||||
|
||||
interface ConnectedDispatchProps {
|
||||
updateTaskStatus: typeof updateTaskStatus
|
||||
deleteTask: typeof deleteTask
|
||||
cloneTask: typeof cloneTask
|
||||
selectTask: typeof selectTask
|
||||
setSearchTerm: typeof setSearchTermAction
|
||||
setShowInactive: typeof setShowInactiveAction
|
||||
importScript: typeof importScript
|
||||
onAddTaskLabels: typeof addTaskLabelsAsync
|
||||
onRemoveTaskLabels: typeof removeTaskLabelsAsync
|
||||
}
|
||||
|
||||
interface ConnectedStateProps {
|
||||
searchTerm: string
|
||||
showInactive: boolean
|
||||
orgs: Organization[]
|
||||
}
|
||||
|
||||
type Props = ConnectedDispatchProps & PassedInProps & ConnectedStateProps
|
||||
|
||||
interface State {
|
||||
isImportOverlayVisible: boolean
|
||||
taskLabelsEdit: Task
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class OrgTasksPage extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
props.setSearchTerm('')
|
||||
if (!props.showInactive) {
|
||||
props.setShowInactive()
|
||||
}
|
||||
|
||||
this.state = {
|
||||
isImportOverlayVisible: false,
|
||||
taskLabelsEdit: null,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
setSearchTerm,
|
||||
showInactive,
|
||||
searchTerm,
|
||||
onAddTaskLabels,
|
||||
onRemoveTaskLabels,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Page titleTag="Tasks">
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter tasks..."
|
||||
widthPixels={290}
|
||||
value={searchTerm}
|
||||
onChange={this.handleFilterChange}
|
||||
onBlur={this.handleFilterBlur}
|
||||
/>
|
||||
<TasksHeader
|
||||
onCreateTask={this.handleCreateTask}
|
||||
setSearchTerm={setSearchTerm}
|
||||
setShowInactive={this.handleToggle}
|
||||
showInactive={showInactive}
|
||||
toggleOverlay={this.handleToggleOverlay}
|
||||
showOrgDropdown={false}
|
||||
showFilter={false}
|
||||
/>
|
||||
<FilterList<Task>
|
||||
searchTerm={searchTerm}
|
||||
searchKeys={['name']}
|
||||
list={this.filteredTasks}
|
||||
>
|
||||
{ts => (
|
||||
<TasksList
|
||||
searchTerm={searchTerm}
|
||||
tasks={ts}
|
||||
totalCount={this.totalTaskCount}
|
||||
onActivate={this.handleActivate}
|
||||
onDelete={this.handleDelete}
|
||||
onCreate={this.handleCreateTask}
|
||||
onClone={this.handleClone}
|
||||
onSelect={selectTask}
|
||||
onAddTaskLabels={onAddTaskLabels}
|
||||
onRemoveTaskLabels={onRemoveTaskLabels}
|
||||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
</Page>
|
||||
{this.renderImportOverlay}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private get filteredTasks() {
|
||||
const {tasks, showInactive} = this.props
|
||||
if (showInactive) {
|
||||
return tasks
|
||||
}
|
||||
const mappedTasks = tasks.filter(t => {
|
||||
if (!showInactive) {
|
||||
return t.status === TaskAPI.StatusEnum.Active
|
||||
}
|
||||
})
|
||||
|
||||
return mappedTasks
|
||||
}
|
||||
|
||||
private handleToggle = async () => {
|
||||
await this.props.setShowInactive()
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
private get totalTaskCount(): number {
|
||||
return this.props.tasks.length
|
||||
}
|
||||
|
||||
private handleActivate = async (task: Task) => {
|
||||
await this.props.updateTaskStatus(task)
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
private handleDelete = async (task: Task) => {
|
||||
await this.props.deleteTask(task)
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
private handleClone = async (task: Task) => {
|
||||
const {tasks} = this.props
|
||||
await this.props.cloneTask(task, tasks)
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
private handleCreateTask = () => {
|
||||
const {router} = this.props
|
||||
|
||||
router.push('/tasks/new')
|
||||
}
|
||||
|
||||
private handleToggleOverlay = () => {
|
||||
this.setState({isImportOverlayVisible: !this.state.isImportOverlayVisible})
|
||||
}
|
||||
|
||||
private handleSave = (script: string, fileName: string) => {
|
||||
this.props.importScript(script, fileName)
|
||||
}
|
||||
|
||||
private get renderImportOverlay(): JSX.Element {
|
||||
const {isImportOverlayVisible} = this.state
|
||||
|
||||
return (
|
||||
<OverlayTechnology visible={isImportOverlayVisible}>
|
||||
<ImportTaskOverlay
|
||||
onDismissOverlay={this.handleToggleOverlay}
|
||||
onSave={this.handleSave}
|
||||
/>
|
||||
</OverlayTechnology>
|
||||
)
|
||||
}
|
||||
|
||||
private handleFilterBlur = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.props.setSearchTerm(e.target.value)
|
||||
}
|
||||
|
||||
private handleFilterChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.props.setSearchTerm(e.target.value)
|
||||
}
|
||||
}
|
||||
const mstp = ({
|
||||
tasks: {searchTerm, showInactive},
|
||||
orgs,
|
||||
}: AppState): ConnectedStateProps => {
|
||||
return {
|
||||
searchTerm,
|
||||
showInactive,
|
||||
orgs,
|
||||
}
|
||||
}
|
||||
const mdtp: ConnectedDispatchProps = {
|
||||
updateTaskStatus,
|
||||
deleteTask,
|
||||
selectTask,
|
||||
cloneTask,
|
||||
setSearchTerm: setSearchTermAction,
|
||||
setShowInactive: setShowInactiveAction,
|
||||
importScript,
|
||||
onRemoveTaskLabels: removeTaskLabelsAsync,
|
||||
onAddTaskLabels: addTaskLabelsAsync,
|
||||
}
|
||||
export default connect<
|
||||
ConnectedStateProps,
|
||||
ConnectedDispatchProps,
|
||||
PassedInProps
|
||||
>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(OrgTasksPage)
|
|
@ -1,47 +0,0 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
// Components
|
||||
import {IndexList} from 'src/clockface'
|
||||
|
||||
// Types
|
||||
import {Task} from '@influxdata/influx'
|
||||
import TaskRow from 'src/organizations/components/TaskRow'
|
||||
|
||||
interface Props {
|
||||
tasks: Task[]
|
||||
emptyState: JSX.Element
|
||||
onDelete: (taskID: string) => void
|
||||
onUpdate: (task: Task) => void
|
||||
onClone: (task: Task) => void
|
||||
}
|
||||
|
||||
export default class TaskList extends PureComponent<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<IndexList>
|
||||
<IndexList.Header>
|
||||
<IndexList.HeaderCell columnName="Name" width="40%" />
|
||||
<IndexList.HeaderCell columnName="Owner" width="40%" />
|
||||
<IndexList.HeaderCell width="20%" />
|
||||
</IndexList.Header>
|
||||
<IndexList.Body columnCount={3} emptyState={this.props.emptyState}>
|
||||
{this.rows}
|
||||
</IndexList.Body>
|
||||
</IndexList>
|
||||
)
|
||||
}
|
||||
|
||||
private get rows(): JSX.Element[] {
|
||||
const {tasks, onDelete, onClone, onUpdate} = this.props
|
||||
return tasks.map(task => (
|
||||
<TaskRow
|
||||
key={task.id}
|
||||
task={task}
|
||||
onDelete={onDelete}
|
||||
onUpdate={onUpdate}
|
||||
onClone={onClone}
|
||||
/>
|
||||
))
|
||||
}
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
// Components
|
||||
import {
|
||||
ComponentSize,
|
||||
IndexList,
|
||||
ConfirmationButton,
|
||||
Alignment,
|
||||
Button,
|
||||
IconFont,
|
||||
ComponentColor,
|
||||
} from 'src/clockface'
|
||||
|
||||
// Api
|
||||
import {Task} from '@influxdata/influx'
|
||||
import EditableName from 'src/shared/components/EditableName'
|
||||
|
||||
interface Props {
|
||||
task: Task
|
||||
onDelete: (taskID: string) => void
|
||||
onUpdate: (task: Task) => void
|
||||
onClone: (task: Task) => void
|
||||
}
|
||||
|
||||
export default class TaskRow extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {task} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<IndexList.Row key={task.id}>
|
||||
<IndexList.Cell>
|
||||
<EditableName
|
||||
onUpdate={this.handleUpdateTask}
|
||||
name={task.name}
|
||||
hrefValue={`/tasks/${task.id}`}
|
||||
/>
|
||||
</IndexList.Cell>
|
||||
<IndexList.Cell>{task.name}</IndexList.Cell>
|
||||
<IndexList.Cell revealOnHover={true} alignment={Alignment.Right}>
|
||||
<Button
|
||||
size={ComponentSize.ExtraSmall}
|
||||
color={ComponentColor.Secondary}
|
||||
text="Clone"
|
||||
icon={IconFont.Duplicate}
|
||||
onClick={this.handleClone}
|
||||
/>
|
||||
<ConfirmationButton
|
||||
size={ComponentSize.ExtraSmall}
|
||||
text="Delete"
|
||||
confirmText="Confirm"
|
||||
onConfirm={this.handleDeleteTask}
|
||||
/>
|
||||
</IndexList.Cell>
|
||||
</IndexList.Row>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private handleUpdateTask = (name: string) => {
|
||||
const {onUpdate, task} = this.props
|
||||
onUpdate({...task, name})
|
||||
}
|
||||
|
||||
private handleClone = (): void => {
|
||||
this.props.onClone(this.props.task)
|
||||
}
|
||||
|
||||
private handleDeleteTask = (): void => {
|
||||
this.props.onDelete(this.props.task.id)
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
// Libraries
|
||||
import React, {PureComponent, ChangeEvent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader'
|
||||
import {Input, IconFont, ComponentSize, EmptyState} from 'src/clockface'
|
||||
import TaskList from 'src/organizations/components/TaskList'
|
||||
import FilterList from 'src/shared/components/Filter'
|
||||
|
||||
import {client} from 'src/utils/api'
|
||||
|
||||
// Types
|
||||
import {Task} from '@influxdata/influx'
|
||||
|
||||
interface Props {
|
||||
tasks: Task[]
|
||||
orgName: string
|
||||
onChange: () => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
searchTerm: string
|
||||
}
|
||||
|
||||
export default class Tasks extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
searchTerm: '',
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {searchTerm} = this.state
|
||||
const {tasks} = this.props
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabbedPageHeader>
|
||||
<Input
|
||||
icon={IconFont.Search}
|
||||
placeholder="Filter tasks..."
|
||||
widthPixels={290}
|
||||
value={searchTerm}
|
||||
onChange={this.handleFilterChange}
|
||||
onBlur={this.handleFilterBlur}
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
<FilterList<Task>
|
||||
searchTerm={searchTerm}
|
||||
searchKeys={['name']}
|
||||
list={tasks}
|
||||
>
|
||||
{ts => (
|
||||
<TaskList
|
||||
tasks={ts}
|
||||
emptyState={this.emptyState}
|
||||
onDelete={this.handleDeleteTask}
|
||||
onUpdate={this.handleUpdateTask}
|
||||
onClone={this.handleCloneTask}
|
||||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
private handleFilterBlur = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({searchTerm: e.target.value})
|
||||
}
|
||||
|
||||
private handleFilterChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({searchTerm: e.target.value})
|
||||
}
|
||||
|
||||
private get emptyState(): JSX.Element {
|
||||
const {orgName} = this.props
|
||||
const {searchTerm} = this.state
|
||||
|
||||
if (_.isEmpty(searchTerm)) {
|
||||
return (
|
||||
<EmptyState size={ComponentSize.Medium}>
|
||||
<EmptyState.Text
|
||||
text={`${orgName} does not own any Tasks , why not create one?`}
|
||||
highlightWords={'Tasks'}
|
||||
/>
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<EmptyState size={ComponentSize.Medium}>
|
||||
<EmptyState.Text text="No Tasks match your query" />
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
private handleUpdateTask = async (task: Task) => {
|
||||
await client.tasks.update(task.id, task)
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
private handleDeleteTask = async (taskID: string) => {
|
||||
await client.tasks.delete(taskID)
|
||||
this.props.onChange()
|
||||
}
|
||||
|
||||
private handleCloneTask = async (task: Task) => {
|
||||
await client.tasks.create(task.orgID, task.flux)
|
||||
this.props.onChange()
|
||||
}
|
||||
}
|
|
@ -20,10 +20,6 @@ const getBuckets = async (org: Organization) => {
|
|||
return client.buckets.getAllByOrg(org)
|
||||
}
|
||||
|
||||
const getTasks = async (org: Organization) => {
|
||||
return client.tasks.getAllByOrg(org)
|
||||
}
|
||||
|
||||
// Actions
|
||||
import {updateOrg} from 'src/organizations/actions'
|
||||
import * as notifyActions from 'src/shared/actions/notifications'
|
||||
|
@ -36,7 +32,7 @@ import TabbedPageSection from 'src/shared/components/tabbed_page/TabbedPageSecti
|
|||
import Members from 'src/organizations/components/Members'
|
||||
import Buckets from 'src/organizations/components/Buckets'
|
||||
import Dashboards from 'src/organizations/components/Dashboards'
|
||||
import Tasks from 'src/organizations/components/Tasks'
|
||||
import OrgTasksPage from 'src/organizations/components/OrgTasksPage'
|
||||
import Collectors from 'src/organizations/components/Collectors'
|
||||
import Scrapers from 'src/organizations/components/Scrapers'
|
||||
import GetOrgResources from 'src/organizations/components/GetOrgResources'
|
||||
|
@ -48,7 +44,6 @@ import {
|
|||
ResourceOwner,
|
||||
Bucket,
|
||||
Organization,
|
||||
Task,
|
||||
Telegraf,
|
||||
ScraperTargetResponse,
|
||||
} from '@influxdata/influx'
|
||||
|
@ -56,11 +51,23 @@ import * as NotificationsActions from 'src/types/actions/notifications'
|
|||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {Task} from 'src/tasks/containers/TasksPage'
|
||||
|
||||
interface StateProps {
|
||||
org: Organization
|
||||
}
|
||||
|
||||
const getTasks = async (org: Organization): Promise<Task[]> => {
|
||||
const tasks = await client.tasks.getAllByOrg(org)
|
||||
const mappedTasks = tasks.map(task => {
|
||||
return {
|
||||
...task,
|
||||
organization: org,
|
||||
}
|
||||
})
|
||||
return mappedTasks
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
onUpdateOrg: typeof updateOrg
|
||||
notify: NotificationsActions.PublishNotificationActionCreator
|
||||
|
@ -71,7 +78,7 @@ type Props = StateProps & WithRouterProps & DispatchProps
|
|||
@ErrorHandling
|
||||
class OrganizationView extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {org, params, notify} = this.props
|
||||
const {org, params, notify, router} = this.props
|
||||
|
||||
return (
|
||||
<Page titleTag={org.name}>
|
||||
|
@ -171,10 +178,11 @@ class OrganizationView extends PureComponent<Props> {
|
|||
loading={loading}
|
||||
spinnerComponent={<TechnoSpinner />}
|
||||
>
|
||||
<Tasks
|
||||
<OrgTasksPage
|
||||
tasks={tasks}
|
||||
orgName={org.name}
|
||||
onChange={fetch}
|
||||
router={router}
|
||||
/>
|
||||
</SpinnerContainer>
|
||||
)}
|
||||
|
|
|
@ -21,13 +21,22 @@ interface Props {
|
|||
setShowInactive: () => void
|
||||
showInactive: boolean
|
||||
toggleOverlay: () => void
|
||||
showOrgDropdown?: boolean
|
||||
showFilter?: boolean
|
||||
}
|
||||
|
||||
export default class TasksHeader extends PureComponent<Props> {
|
||||
public static defaultProps: {
|
||||
showOrgDropdown: boolean
|
||||
showFilter: boolean
|
||||
} = {
|
||||
showOrgDropdown: true,
|
||||
showFilter: true,
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
onCreateTask,
|
||||
setSearchTerm,
|
||||
setShowInactive,
|
||||
showInactive,
|
||||
toggleOverlay,
|
||||
|
@ -36,7 +45,7 @@ export default class TasksHeader extends PureComponent<Props> {
|
|||
return (
|
||||
<Page.Header fullWidth={false}>
|
||||
<Page.Header.Left>
|
||||
<Page.Title title="Tasks" />
|
||||
<Page.Title title={this.pageTitle} />
|
||||
</Page.Header.Left>
|
||||
<Page.Header.Right>
|
||||
<SlideToggle.Label text="Show Inactive" />
|
||||
|
@ -45,11 +54,8 @@ export default class TasksHeader extends PureComponent<Props> {
|
|||
size={ComponentSize.ExtraSmall}
|
||||
onChange={setShowInactive}
|
||||
/>
|
||||
<SearchWidget
|
||||
placeholderText="Filter tasks by name..."
|
||||
onSearch={setSearchTerm}
|
||||
/>
|
||||
<TaskOrgDropdown />
|
||||
{this.filterSearch}
|
||||
{this.orgDropDown}
|
||||
<Button
|
||||
text="Import"
|
||||
icon={IconFont.Import}
|
||||
|
@ -66,4 +72,36 @@ export default class TasksHeader extends PureComponent<Props> {
|
|||
</Page.Header>
|
||||
)
|
||||
}
|
||||
|
||||
private get pageTitle() {
|
||||
const {showOrgDropdown} = this.props
|
||||
|
||||
if (showOrgDropdown) {
|
||||
return 'Tasks'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
private get filterSearch(): JSX.Element {
|
||||
const {setSearchTerm, showFilter} = this.props
|
||||
|
||||
if (showFilter) {
|
||||
return (
|
||||
<SearchWidget
|
||||
placeholderText="Filter tasks by name..."
|
||||
onSearch={setSearchTerm}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
|
||||
private get orgDropDown(): JSX.Element {
|
||||
const {showOrgDropdown} = this.props
|
||||
|
||||
if (showOrgDropdown) {
|
||||
return <TaskOrgDropdown />
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import {allOrganizationsID} from 'src/tasks/constants'
|
|||
import {Task as TaskAPI, User, Organization} from '@influxdata/influx'
|
||||
import {AppState} from 'src/types/v2'
|
||||
|
||||
interface Task extends TaskAPI {
|
||||
export interface Task extends TaskAPI {
|
||||
organization: Organization
|
||||
owner?: User
|
||||
offset?: string
|
||||
|
|
Loading…
Reference in New Issue