Merge pull request #12975 from influxdata/fix/org-dashboards-disappearing

fix(ui): Ensure dashboard cards generates sort order from correct dashboards
pull/11814/head
Iris Scholten 2019-03-28 14:15:50 -07:00 committed by GitHub
commit b8272155e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 274 additions and 425 deletions

View File

@ -9,6 +9,7 @@ import {getBuckets} from 'src/buckets/actions'
import {getTelegrafs} from 'src/telegrafs/actions'
import {getVariables} from 'src/variables/actions'
import {getScrapers} from 'src/scrapers/actions'
import {getDashboardsAsync} from 'src/dashboards/actions'
// Types
import {AppState} from 'src/types'
@ -24,6 +25,7 @@ import {TechnoSpinner, SpinnerContainer} from '@influxdata/clockface'
import {getAuthorizations} from 'src/authorizations/actions'
import {AuthorizationsState} from 'src/authorizations/reducers'
import {VariablesState} from 'src/variables/reducers'
import {DashboardsState} from 'src/dashboards/reducers/dashboards'
interface StateProps {
org: Organization
@ -33,6 +35,7 @@ interface StateProps {
variables: VariablesState
scrapers: ScrapersState
tokens: AuthorizationsState
dashboards: DashboardsState
}
interface DispatchProps {
@ -42,6 +45,7 @@ interface DispatchProps {
getVariables: typeof getVariables
getScrapers: typeof getScrapers
getAuthorizations: typeof getAuthorizations
getDashboards: typeof getDashboardsAsync
}
interface PassedProps {
@ -57,12 +61,17 @@ export enum ResourceTypes {
Variables = 'variables',
Authorizations = 'tokens',
Scrapers = 'scrapers',
Dashboards = 'dashboards',
}
@ErrorHandling
class GetResources extends PureComponent<Props, StateProps> {
public async componentDidMount() {
switch (this.props.resource) {
case ResourceTypes.Dashboards: {
return await this.props.getDashboards()
}
case ResourceTypes.Labels: {
return await this.props.getLabels()
}
@ -115,6 +124,7 @@ const mstp = ({
variables,
scrapers,
tokens,
dashboards,
}: AppState): StateProps => {
const org = orgs[0]
@ -122,6 +132,7 @@ const mstp = ({
labels,
buckets,
telegrafs,
dashboards,
variables,
scrapers,
tokens,
@ -136,6 +147,7 @@ const mdtp = {
getVariables: getVariables,
getScrapers: getScrapers,
getAuthorizations: getAuthorizations,
getDashboards: getDashboardsAsync,
}
export default connect<StateProps, DispatchProps, {}>(

View File

@ -66,22 +66,22 @@ import {
} from 'src/types'
export enum ActionTypes {
LoadDashboards = 'LOAD_DASHBOARDS',
LoadDashboard = 'LOAD_DASHBOARD',
DeleteDashboard = 'DELETE_DASHBOARD',
SetDashboards = 'SET_DASHBOARDS',
SetDashboard = 'SET_DASHBOARD',
RemoveDashboard = 'REMOVE_DASHBOARD',
DeleteDashboardFailed = 'DELETE_DASHBOARD_FAILED',
UpdateDashboard = 'UPDATE_DASHBOARD',
DeleteCell = 'DELETE_CELL',
EditDashboard = 'EDIT_DASHBOARD',
RemoveCell = 'REMOVE_CELL',
AddDashboardLabels = 'ADD_DASHBOARD_LABELS',
RemoveDashboardLabels = 'REMOVE_DASHBOARD_LABELS',
}
export type Action =
| LoadDashboardsAction
| DeleteDashboardAction
| LoadDashboardAction
| UpdateDashboardAction
| DeleteCellAction
| SetDashboardsAction
| RemoveDashboardAction
| SetDashboardAction
| EditDashboardAction
| RemoveCellAction
| PublishNotificationAction
| SetViewAction
| DeleteTimeRangeAction
@ -89,32 +89,33 @@ export type Action =
| AddDashboardLabelsAction
| RemoveDashboardLabelsAction
interface DeleteCellAction {
type: ActionTypes.DeleteCell
interface RemoveCellAction {
type: ActionTypes.RemoveCell
payload: {
dashboard: Dashboard
cell: Cell
}
}
interface UpdateDashboardAction {
type: ActionTypes.UpdateDashboard
interface EditDashboardAction {
type: ActionTypes.EditDashboard
payload: {
dashboard: Dashboard
}
}
interface LoadDashboardsAction {
type: ActionTypes.LoadDashboards
interface SetDashboardsAction {
type: ActionTypes.SetDashboards
payload: {
dashboards: Dashboard[]
status: RemoteDataState
list: Dashboard[]
}
}
interface DeleteDashboardAction {
type: ActionTypes.DeleteDashboard
interface RemoveDashboardAction {
type: ActionTypes.RemoveDashboard
payload: {
dashboardID: string
id: string
}
}
@ -125,8 +126,8 @@ interface DeleteDashboardFailedAction {
}
}
interface LoadDashboardAction {
type: ActionTypes.LoadDashboard
interface SetDashboardAction {
type: ActionTypes.SetDashboard
payload: {
dashboard: Dashboard
}
@ -150,32 +151,30 @@ interface RemoveDashboardLabelsAction {
// Action Creators
export const updateDashboard = (
dashboard: Dashboard
): UpdateDashboardAction => ({
type: ActionTypes.UpdateDashboard,
export const editDashboard = (dashboard: Dashboard): EditDashboardAction => ({
type: ActionTypes.EditDashboard,
payload: {dashboard},
})
export const loadDashboards = (
dashboards: Dashboard[]
): LoadDashboardsAction => ({
type: ActionTypes.LoadDashboards,
export const setDashboards = (
status: RemoteDataState,
list?: Dashboard[]
): SetDashboardsAction => ({
type: ActionTypes.SetDashboards,
payload: {
dashboards,
status,
list,
},
})
export const loadDashboard = (dashboard: Dashboard): LoadDashboardAction => ({
type: ActionTypes.LoadDashboard,
export const setDashboard = (dashboard: Dashboard): SetDashboardAction => ({
type: ActionTypes.SetDashboard,
payload: {dashboard},
})
export const deleteDashboard = (
dashboardID: string
): DeleteDashboardAction => ({
type: ActionTypes.DeleteDashboard,
payload: {dashboardID},
export const removeDashboard = (id: string): RemoveDashboardAction => ({
type: ActionTypes.RemoveDashboard,
payload: {id},
})
export const deleteDashboardFailed = (
@ -185,11 +184,11 @@ export const deleteDashboardFailed = (
payload: {dashboard},
})
export const deleteCell = (
export const removeCell = (
dashboard: Dashboard,
cell: Cell
): DeleteCellAction => ({
type: ActionTypes.DeleteCell,
): RemoveCellAction => ({
type: ActionTypes.RemoveCell,
payload: {dashboard, cell},
})
@ -215,10 +214,12 @@ export const getDashboardsAsync = () => async (
dispatch: Dispatch<Action>
): Promise<Dashboard[]> => {
try {
dispatch(setDashboards(RemoteDataState.Loading))
const dashboards = await getDashboardsAJAX()
dispatch(loadDashboards(dashboards))
dispatch(setDashboards(RemoteDataState.Done, dashboards))
return dashboards
} catch (error) {
dispatch(setDashboards(RemoteDataState.Error))
console.error(error)
throw error
}
@ -244,9 +245,10 @@ export const importDashboardAsync = (dashboard: Dashboard) => async (
await createDashboardAJAX(dashboard)
const dashboards = await getDashboardsAJAX()
dispatch(loadDashboards(dashboards))
dispatch(setDashboards(RemoteDataState.Done, dashboards))
dispatch(notify(copy.dashboardImported()))
} catch (error) {
dispatch(setDashboards(RemoteDataState.Error))
dispatch(notify(copy.dashboardImportFailed('Could not upload dashboard')))
console.error(error)
}
@ -255,7 +257,7 @@ export const importDashboardAsync = (dashboard: Dashboard) => async (
export const deleteDashboardAsync = (dashboard: Dashboard) => async (
dispatch: Dispatch<Action>
): Promise<void> => {
dispatch(deleteDashboard(dashboard.id))
dispatch(removeDashboard(dashboard.id))
dispatch(deleteTimeRange(dashboard.id))
try {
@ -303,7 +305,7 @@ export const getDashboardAsync = (dashboardID: string) => async (
await dispatch(refreshDashboardVariableValues(dashboard, views))
// Now that all the necessary state has been loaded, set the dashboard
dispatch(loadDashboard(dashboard))
dispatch(setDashboard(dashboard))
} catch {
dispatch(replace(`/dashboards`))
dispatch(notify(copy.dashboardGetFailed(dashboardID)))
@ -319,7 +321,7 @@ export const updateDashboardAsync = (dashboard: Dashboard) => async (
): Promise<void> => {
try {
const updatedDashboard = await updateDashboardAJAX(dashboard)
dispatch(updateDashboard(updatedDashboard))
dispatch(editDashboard(updatedDashboard))
} catch (error) {
console.error(error)
dispatch(notify(copy.dashboardUpdateFailed()))
@ -354,7 +356,7 @@ export const createCellWithView = (
await dispatch(refreshDashboardVariableValues(dashboard, views))
dispatch(setView(createdCell.id, newView, RemoteDataState.Done))
dispatch(updateDashboard(updatedDashboard))
dispatch(editDashboard(updatedDashboard))
} catch {
notify(copy.cellAddFailed())
}
@ -393,7 +395,7 @@ export const updateCellsAsync = (dashboard: Dashboard, cells: Cell[]) => async (
cells: updatedCells,
}
dispatch(loadDashboard(updatedDashboard))
dispatch(setDashboard(updatedDashboard))
} catch (error) {
console.error(error)
}
@ -413,7 +415,7 @@ export const deleteCellAsync = (dashboard: Dashboard, cell: Cell) => async (
dispatch(refreshDashboardVariableValues(dashboard, views)),
])
dispatch(deleteCell(dashboard, cell))
dispatch(removeCell(dashboard, cell))
dispatch(notify(copy.cellDeleted()))
} catch (error) {
console.error(error)
@ -431,7 +433,7 @@ export const copyDashboardCellAsync = (
cells: [...dashboard.cells, clonedCell],
}
dispatch(loadDashboard(updatedDashboard))
dispatch(setDashboard(updatedDashboard))
dispatch(notify(copy.cellAdded()))
} catch (error) {
console.error(error)
@ -471,7 +473,7 @@ export const selectVariableValue = (
value: string
) => async (dispatch, getState: GetState): Promise<void> => {
const variables = getHydratedVariables(getState(), dashboardID)
const dashboard = getState().dashboards.find(d => d.id === dashboardID)
const dashboard = getState().dashboards.list.find(d => d.id === dashboardID)
dispatch(selectValue(dashboardID, variableID, value))

View File

@ -65,7 +65,7 @@ export const createNoteCell = (dashboardID: string) => async (
dispatch: Dispatch<Action>,
getState: GetState
) => {
const dashboard = getState().dashboards.find(d => d.id === dashboardID)
const dashboard = getState().dashboards.list.find(d => d.id === dashboardID)
if (!dashboard) {
throw new Error(`could not find dashboard with id "${dashboardID}"`)

View File

@ -312,7 +312,7 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
const timeRange =
ranges.find(r => r.dashboardID === dashboardID) || DEFAULT_TIME_RANGE
const dashboard = dashboards.find(d => d.id === dashboardID)
const dashboard = dashboards.list.find(d => d.id === dashboardID)
return {
links,

View File

@ -105,7 +105,7 @@ class VEOContents extends PureComponent<Props, {}> {
const mstp = (state: AppState, {dashboardID}): StateProps => {
const {dashboards} = state
const dashboard = dashboards.find(d => d.id === dashboardID)
const dashboard = dashboards.list.find(d => d.id === dashboardID)
const {view, draftQueries} = getActiveTimeMachine(state)

View File

@ -84,7 +84,7 @@ class DashboardCards extends PureComponent<Props> {
const mstp = (state: AppState, props: OwnProps) => {
return {
sortedIDs: getSortedResource(state.dashboards, props),
sortedIDs: getSortedResource(state.dashboards.list, props),
}
}

View File

@ -22,6 +22,9 @@ import {
} from 'src/dashboards/actions'
import {retainRangesDashTimeV1 as retainRangesDashTimeV1Action} from 'src/dashboards/actions/ranges'
import {notify as notifyAction} from 'src/shared/actions/notifications'
import GetResources, {
ResourceTypes,
} from 'src/configuration/components/GetResources'
// Constants
import {DEFAULT_DASHBOARD_NAME} from 'src/dashboards/constants/index'
@ -71,8 +74,8 @@ class DashboardIndex extends PureComponent<Props, State> {
}
public async componentDidMount() {
const {handleGetDashboards, dashboards} = this.props
await handleGetDashboards()
const {dashboards} = this.props
const dashboardIDs = dashboards.map(d => d.id)
this.props.retainRangesDashTimeV1(dashboardIDs)
}
@ -98,25 +101,27 @@ class DashboardIndex extends PureComponent<Props, State> {
</Page.Header>
<Page.Contents fullWidth={false} scrollable={true}>
<div className="col-md-12">
<DashboardsIndexContents
filterComponent={() => (
<SearchWidget
placeholderText="Filter dashboards..."
onSearch={this.handleFilterDashboards}
searchTerm={searchTerm}
/>
)}
dashboards={dashboards}
onDeleteDashboard={this.handleDeleteDashboard}
onCreateDashboard={this.handleCreateDashboard}
onCloneDashboard={this.handleCloneDashboard}
onUpdateDashboard={handleUpdateDashboard}
notify={notify}
searchTerm={searchTerm}
showOwnerColumn={true}
onFilterChange={this.handleFilterDashboards}
onImportDashboard={this.summonImportOverlay}
/>
<GetResources resource={ResourceTypes.Dashboards}>
<DashboardsIndexContents
filterComponent={() => (
<SearchWidget
placeholderText="Filter dashboards..."
onSearch={this.handleFilterDashboards}
searchTerm={searchTerm}
/>
)}
dashboards={dashboards}
onDeleteDashboard={this.handleDeleteDashboard}
onCreateDashboard={this.handleCreateDashboard}
onCloneDashboard={this.handleCloneDashboard}
onUpdateDashboard={handleUpdateDashboard}
notify={notify}
searchTerm={searchTerm}
showOwnerColumn={true}
onFilterChange={this.handleFilterDashboards}
onImportDashboard={this.summonImportOverlay}
/>
</GetResources>
</div>
</Page.Contents>
</Page>
@ -174,7 +179,11 @@ class DashboardIndex extends PureComponent<Props, State> {
}
const mstp = (state: AppState): StateProps => {
const {dashboards, links, orgs} = state
const {
dashboards: {list: dashboards},
links,
orgs,
} = state
return {
orgs,

View File

@ -1,13 +1,13 @@
// Reducer
import reducer from 'src/dashboards/reducers/dashboards'
import {dashboardsReducer as reducer} from 'src/dashboards/reducers/dashboards'
// Actions
import {
loadDashboard,
loadDashboards,
deleteDashboard,
updateDashboard,
deleteCell,
setDashboard,
setDashboards,
removeDashboard,
editDashboard,
removeCell,
addDashboardLabels,
removeDashboardLabels,
} from 'src/dashboards/actions/'
@ -15,48 +15,53 @@ import {
// Resources
import {dashboard} from 'src/dashboards/resources'
import {labels} from 'mocks/dummyData'
import {RemoteDataState} from '@influxdata/clockface'
const status = RemoteDataState.Done
describe('dashboards reducer', () => {
it('can load the dashboards', () => {
const expected = [dashboard]
const actual = reducer([], loadDashboards(expected))
it('can set the dashboards', () => {
const list = [dashboard]
const expected = {status, list}
const actual = reducer(undefined, setDashboards(status, list))
expect(actual).toEqual(expected)
})
it('can delete a dashboard', () => {
it('can remove a dashboard', () => {
const d2 = {...dashboard, id: '2'}
const state = [dashboard, d2]
const expected = [dashboard]
const actual = reducer(state, deleteDashboard(d2.id))
const list = [dashboard, d2]
const expected = {list: [dashboard], status}
const actual = reducer({list, status}, removeDashboard(d2.id))
expect(actual).toEqual(expected)
})
it('can load a dashboard', () => {
it('can set a dashboard', () => {
const loadedDashboard = {...dashboard, name: 'updated'}
const d2 = {...dashboard, id: '2'}
const state = [dashboard, d2]
const state = {status, list: [dashboard, d2]}
const expected = [loadedDashboard, d2]
const actual = reducer(state, loadDashboard(loadedDashboard))
const expected = {status, list: [loadedDashboard, d2]}
const actual = reducer(state, setDashboard(loadedDashboard))
expect(actual).toEqual(expected)
})
it('can update a dashboard', () => {
it('can edit a dashboard', () => {
const updates = {...dashboard, name: 'updated dash'}
const expected = [updates]
const actual = reducer([dashboard], updateDashboard(updates))
const expected = {status, list: [updates]}
const actual = reducer({status, list: [dashboard]}, editDashboard(updates))
expect(actual).toEqual(expected)
})
it('can delete a cell from a dashboard', () => {
const expected = [{...dashboard, cells: []}]
it('can remove a cell from a dashboard', () => {
const expected = {status, list: [{...dashboard, cells: []}]}
const actual = reducer(
[dashboard],
deleteCell(dashboard, dashboard.cells[0])
{status, list: [dashboard]},
removeCell(dashboard, dashboard.cells[0])
)
expect(actual).toEqual(expected)
@ -64,20 +69,24 @@ describe('dashboards reducer', () => {
it('can add labels to a dashboard', () => {
const dashboardWithoutLabels = {...dashboard, labels: []}
const expected = [{...dashboard, labels}]
const expected = {status, list: [{...dashboard, labels}]}
const actual = reducer(
[dashboardWithoutLabels],
{status, list: [dashboardWithoutLabels]},
addDashboardLabels(dashboardWithoutLabels.id, labels)
)
expect(actual).toEqual(expected)
})
it('can delete labels from a dashboard', () => {
const dashboardWithLabels = {...dashboard, labels}
const expected = [{...dashboard, labels: []}]
it('can remove labels from a dashboard', () => {
const leftOverLabel = {...labels[0], name: 'wowowowo', id: '3'}
const dashboardWithLabels = {
...dashboard,
labels: [...labels, leftOverLabel],
}
const expected = {status, list: [{...dashboard, labels: [leftOverLabel]}]}
const actual = reducer(
[dashboardWithLabels],
{status, list: [dashboardWithLabels]},
removeDashboardLabels(dashboardWithLabels.id, labels)
)

View File

@ -1,83 +1,111 @@
import {Action, ActionTypes} from 'src/dashboards/actions'
import {Dashboard} from 'src/types'
// Libraries
import {produce} from 'immer'
import _ from 'lodash'
type State = Dashboard[]
// Types
import {Action, ActionTypes} from 'src/dashboards/actions'
import {Dashboard, RemoteDataState} from 'src/types'
export default (state: State = [], action: Action): State => {
switch (action.type) {
case ActionTypes.LoadDashboards: {
const {dashboards} = action.payload
return [...dashboards]
}
case ActionTypes.DeleteDashboard: {
const {dashboardID} = action.payload
return [...state.filter(d => d.id !== dashboardID)]
}
case ActionTypes.LoadDashboard: {
const {dashboard} = action.payload
const newDashboards = _.unionBy([dashboard], state, 'id')
return newDashboards
}
case ActionTypes.UpdateDashboard: {
const {dashboard} = action.payload
const newState = state.map(d =>
d.id === dashboard.id ? {...dashboard} : d
)
return [...newState]
}
case ActionTypes.DeleteCell: {
const {dashboard, cell} = action.payload
const newState = state.map(d => {
if (d.id !== dashboard.id) {
return {...d}
}
const cells = d.cells.filter(c => c.id !== cell.id)
return {...d, cells}
})
return [...newState]
}
case ActionTypes.AddDashboardLabels: {
const {dashboardID, labels} = action.payload
const newState = state.map(d => {
if (d.id === dashboardID) {
return {...d, labels: [...d.labels, ...labels]}
}
return d
})
return [...newState]
}
case ActionTypes.RemoveDashboardLabels: {
const {dashboardID, labels} = action.payload
const newState = state.map(d => {
if (d.id === dashboardID) {
const updatedLabels = d.labels.filter(l => {
return !labels.includes(l)
})
return {...d, labels: updatedLabels}
}
return d
})
return [...newState]
}
}
return state
export interface DashboardsState {
list: Dashboard[]
status: RemoteDataState
}
const initialState = () => ({
list: [],
status: RemoteDataState.NotStarted,
})
export const dashboardsReducer = (
state: DashboardsState = initialState(),
action: Action
): DashboardsState => {
return produce(state, draftState => {
switch (action.type) {
case ActionTypes.SetDashboards: {
const {list, status} = action.payload
draftState.status = status
if (list) {
draftState.list = list
}
return
}
case ActionTypes.RemoveDashboard: {
const {id} = action.payload
draftState.list = draftState.list.filter(l => l.id !== id)
return
}
case ActionTypes.SetDashboard: {
const {dashboard} = action.payload
draftState.list = _.unionBy([dashboard], state.list, 'id')
return
}
case ActionTypes.EditDashboard: {
const {dashboard} = action.payload
draftState.list = draftState.list.map(d => {
if (d.id === dashboard.id) {
return dashboard
}
return d
})
return
}
case ActionTypes.RemoveCell: {
const {dashboard, cell} = action.payload
draftState.list = draftState.list.map(d => {
if (d.id === dashboard.id) {
const cells = d.cells.filter(c => c.id !== cell.id)
d.cells = cells
}
return d
})
return
}
case ActionTypes.AddDashboardLabels: {
const {dashboardID, labels} = action.payload
draftState.list = draftState.list.map(d => {
if (d.id === dashboardID) {
d.labels = [...d.labels, ...labels]
}
return d
})
return
}
case ActionTypes.RemoveDashboardLabels: {
const {dashboardID, labels} = action.payload
draftState.list = draftState.list.map(d => {
if (d.id === dashboardID) {
const updatedLabels = d.labels.filter(el => {
const labelToRemove = labels.find(l => l.id === el.id)
return !labelToRemove
})
d.labels = updatedLabels
}
return d
})
return
}
}
})
}

View File

@ -10,7 +10,7 @@ export const getViewsForDashboard = (
state: AppState,
dashboardID: string
): View[] => {
const dashboard = state.dashboards.find(
const dashboard = state.dashboards.list.find(
dashboard => dashboard.id === dashboardID
)

View File

@ -234,7 +234,7 @@ class SaveAsCellForm extends PureComponent<Props, State> {
const mstp = (state: AppState): StateProps => {
const {
orgs,
dashboards,
dashboards: {list: dashboards},
timeMachines: {
timeMachines: {de},
},

View File

@ -21,7 +21,6 @@ import {
incrementCurrentStepIndex,
decrementCurrentStepIndex,
} from 'src/dataLoaders/actions/steps'
import {createDashboardsForPlugins as createDashboardsForPluginsAction} from 'src/protos/actions'
import {notify as notifyAction} from 'src/shared/actions/notifications'
// APIs
@ -52,7 +51,6 @@ interface DispatchProps {
onDecrementStep: typeof decrementCurrentStepIndex
notify: typeof notifyAction
onSaveTelegrafConfig: typeof createOrUpdateTelegrafConfigAsync
createDashboardsForPlugins: typeof createDashboardsForPluginsAction
}
interface StateProps {
@ -244,7 +242,6 @@ const mdtp: DispatchProps = {
onSetActiveTelegrafPlugin: setActiveTelegrafPlugin,
onSetPluginConfiguration: setPluginConfiguration,
onSaveTelegrafConfig: createOrUpdateTelegrafConfigAsync,
createDashboardsForPlugins: createDashboardsForPluginsAction,
notify: notifyAction,
}

View File

@ -5,21 +5,17 @@ import {
ITask as Task,
ITaskTemplate,
} from '@influxdata/influx'
import {Dashboard} from 'src/types'
// API
import {client} from 'src/utils/api'
import {getDashboardsByOrgID} from 'src/dashboards/apis/v2/index'
import {createTaskFromTemplate as createTaskFromTemplateAJAX} from 'src/templates/api'
export enum ActionTypes {
GetTasks = 'GET_TASKS',
PopulateTasks = 'POPULATE_TASKS',
getDashboards = 'GET_DASHBOARDS',
PopulateDashboards = 'POPULATE_DASHBOARDS',
}
export type Actions = PopulateTasks | PopulateDashboards
export type Actions = PopulateTasks
export interface PopulateTasks {
type: ActionTypes.PopulateTasks
@ -36,23 +32,6 @@ export const getTasks = (orgID: string) => async dispatch => {
dispatch(populateTasks(tasks))
}
export interface PopulateDashboards {
type: ActionTypes.PopulateDashboards
payload: {dashboards: Dashboard[]}
}
export const populateDashboards = (
dashboards: Dashboard[]
): PopulateDashboards => ({
type: ActionTypes.PopulateDashboards,
payload: {dashboards},
})
export const getDashboards = (orgID: string) => async dispatch => {
const dashboards = await getDashboardsByOrgID(orgID)
dispatch(populateDashboards(dashboards))
}
export const createScraper = (scraper: ScraperTargetRequest) => async () => {
await client.scrapers.create(scraper)
}

View File

@ -26,8 +26,7 @@ import {DEFAULT_DASHBOARD_NAME} from 'src/dashboards/constants/index'
// Types
import {IconFont} from '@influxdata/clockface'
import {Notification} from 'src/types/notifications'
import {Dashboard} from 'src/types'
import {Dashboard, AppState, Notification} from 'src/types'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -39,12 +38,14 @@ interface DispatchProps {
}
interface OwnProps {
dashboards: Dashboard[]
onChange: () => void
orgID: string
}
type Props = DispatchProps & OwnProps & WithRouterProps
interface StateProps {
dashboards: Dashboard[]
}
type Props = DispatchProps & StateProps & OwnProps & WithRouterProps
interface State {
searchTerm: string
@ -149,13 +150,19 @@ class Dashboards extends PureComponent<Props, State> {
}
}
const mstp = (state: AppState, props: OwnProps): StateProps => {
const dashboards = state.dashboards.list.filter(d => d.orgID === props.orgID)
return {dashboards}
}
const mdtp: DispatchProps = {
notify: notifyAction,
handleDeleteDashboard: deleteDashboardAsync,
handleUpdateDashboard: updateDashboardAsync,
}
export default connect<{}, DispatchProps, OwnProps>(
null,
export default connect<StateProps, DispatchProps, OwnProps>(
mstp,
mdtp
)(withRouter(Dashboards))

View File

@ -9,21 +9,19 @@ import OrganizationNavigation from 'src/organizations/components/OrganizationNav
import OrgHeader from 'src/organizations/containers/OrgHeader'
import {Tabs} from 'src/clockface'
import {Page} from 'src/pageLayout'
import {SpinnerContainer, TechnoSpinner} from '@influxdata/clockface'
import TabbedPageSection from 'src/shared/components/tabbed_page/TabbedPageSection'
import Dashboards from 'src/organizations/components/Dashboards'
import GetResources, {
ResourceTypes,
} from 'src/configuration/components/GetResources'
//Actions
import * as NotificationsActions from 'src/types/actions/notifications'
import * as notifyActions from 'src/shared/actions/notifications'
import {
getDashboards as getDashboardsAction,
populateDashboards as populateDashboardsAction,
} from 'src/organizations/actions/orgView'
// Types
import {Organization} from '@influxdata/influx'
import {AppState, Dashboard} from 'src/types'
import {AppState} from 'src/types'
import {RemoteDataState} from 'src/types'
interface RouterProps {
@ -34,13 +32,10 @@ interface RouterProps {
interface DispatchProps {
notify: NotificationsActions.PublishNotificationActionCreator
getDashboards: typeof getDashboardsAction
populateDashboards: typeof populateDashboardsAction
}
interface StateProps {
org: Organization
dashboards: Dashboard[]
}
type Props = WithRouterProps & RouterProps & DispatchProps & StateProps
@ -51,25 +46,6 @@ interface State {
@ErrorHandling
class OrgDashboardsIndex extends Component<Props, State> {
public state = {
loadingState: RemoteDataState.NotStarted,
}
public componentDidMount = async () => {
this.setState({loadingState: RemoteDataState.Loading})
const {getDashboards, org} = this.props
await getDashboards(org.id)
this.setState({loadingState: RemoteDataState.Done})
}
public componentWillUnmount = async () => {
const {populateDashboards} = this.props
populateDashboards([])
}
public render() {
const {org} = this.props
@ -87,7 +63,9 @@ class OrgDashboardsIndex extends Component<Props, State> {
url="dashboards"
title="Dashboards"
>
{this.orgsDashboardsPage}
<GetResources resource={ResourceTypes.Dashboards}>
<Dashboards orgID={org.id} />
</GetResources>
</TabbedPageSection>
</Tabs.TabContents>
</Tabs>
@ -98,49 +76,18 @@ class OrgDashboardsIndex extends Component<Props, State> {
</>
)
}
private get orgsDashboardsPage() {
const {org, dashboards} = this.props
const {loadingState} = this.state
return (
<SpinnerContainer
loading={loadingState}
spinnerComponent={<TechnoSpinner />}
>
<Dashboards
dashboards={dashboards}
onChange={this.getDashboards}
orgID={org.id}
/>
</SpinnerContainer>
)
}
private getDashboards = async () => {
const {getDashboards, org} = this.props
await getDashboards(org.id)
}
}
const mstp = (state: AppState, props: Props): StateProps => {
const {
orgs,
orgView: {dashboards},
} = state
const org = orgs.find(o => o.id === props.params.orgID)
const org = state.orgs.find(o => o.id === props.params.orgID)
return {
org,
dashboards,
}
}
const mdtp: DispatchProps = {
notify: notifyActions.notify,
getDashboards: getDashboardsAction,
populateDashboards: populateDashboardsAction,
}
export default connect<StateProps, DispatchProps, {}>(

View File

@ -1,16 +1,13 @@
import {ITask as Task, Telegraf} from '@influxdata/influx'
import {Dashboard} from 'src/types'
import {Actions, ActionTypes} from 'src/organizations/actions/orgView'
export interface OrgViewState {
tasks: Task[]
dashboards: Dashboard[]
telegrafs: Telegraf[]
}
const defaultState: OrgViewState = {
tasks: [],
dashboards: [],
telegrafs: [],
}
@ -18,8 +15,6 @@ export default (state = defaultState, action: Actions): OrgViewState => {
switch (action.type) {
case ActionTypes.PopulateTasks:
return {...state, tasks: action.payload.tasks}
case ActionTypes.PopulateDashboards:
return {...state, dashboards: action.payload.dashboards}
default:
return state
}

View File

@ -1,105 +0,0 @@
// Libraries
import {Dispatch} from 'redux'
import {client} from 'src/utils/api'
// Utils
import {addDashboardIDToCells} from 'src/dashboards/apis/'
// Actions
import {loadDashboard} from 'src/dashboards/actions/'
import {notify} from 'src/shared/actions/notifications'
// Types
import {Proto} from '@influxdata/influx'
import {GetState, Dashboard} from 'src/types'
import {ConfigurationState} from 'src/types/dataLoaders'
// Const
import {
TelegrafDashboardFailed,
TelegrafDashboardCreated,
} from 'src/shared/copy/notifications'
export enum ActionTypes {
LoadProto = 'LOAD_PROTO',
}
export type Action = LoadProtoAction
interface LoadProtoAction {
type: ActionTypes.LoadProto
payload: {
proto: Proto
}
}
export const loadProto = (proto: Proto): LoadProtoAction => ({
type: ActionTypes.LoadProto,
payload: {proto},
})
export const getProtos = () => async (dispatch: Dispatch<Action>) => {
try {
const protos = await client.protos.getAll()
protos.forEach(p => {
dispatch(loadProto(p))
})
} catch (error) {
console.error(error)
}
}
export const createDashFromProto = (
protoID: string,
orgID: string
) => async dispatch => {
try {
const dashboards = await client.dashboards.createFromProto(protoID, orgID)
dashboards.forEach((d: Dashboard) => {
const updatedDashboard = {
...d,
cells: addDashboardIDToCells(d.cells, d.id),
}
dispatch(loadDashboard(updatedDashboard))
})
} catch (error) {
console.error(error)
}
}
export const createDashboardsForPlugins = () => async (
dispatch,
getState: GetState
) => {
await dispatch(getProtos())
const {
dataLoading: {
dataLoaders: {telegrafPlugins},
steps: {orgID},
},
protos,
} = getState()
const plugins = []
try {
telegrafPlugins.forEach(tp => {
if (tp.configured === ConfigurationState.Configured) {
if (protos[tp.name]) {
dispatch(createDashFromProto(protos[tp.name].id, orgID))
plugins.push(tp.name)
}
}
})
if (plugins.length) {
dispatch(notify(TelegrafDashboardCreated(plugins)))
}
} catch (err) {
console.error(err)
dispatch(notify(TelegrafDashboardFailed()))
}
}

View File

@ -1,27 +0,0 @@
// Types
import {Action} from 'src/protos/actions/'
import {Proto} from '@influxdata/influx'
export interface ProtosState {
[protoName: string]: Proto
}
const protosReducer = (state: ProtosState = {}, action: Action) => {
switch (action.type) {
case 'LOAD_PROTO': {
const {
proto,
proto: {name},
} = action.payload
return {
...state,
[name]: {...proto},
}
}
}
return state
}
export default protosReducer

View File

@ -13,7 +13,7 @@ import persistStateEnhancer from './persistStateEnhancer'
import meReducer from 'src/shared/reducers/v2/me'
import tasksReducer from 'src/tasks/reducers'
import rangesReducer from 'src/dashboards/reducers/ranges'
import dashboardsReducer from 'src/dashboards/reducers/dashboards'
import {dashboardsReducer} from 'src/dashboards/reducers/dashboards'
import viewsReducer from 'src/dashboards/reducers/views'
import {timeMachinesReducer} from 'src/timeMachine/reducers'
import orgsReducer from 'src/organizations/reducers/orgs'
@ -21,7 +21,6 @@ import orgViewReducer from 'src/organizations/reducers/orgView'
import onboardingReducer from 'src/onboarding/reducers'
import noteEditorReducer from 'src/dashboards/reducers/notes'
import dataLoadingReducer from 'src/dataLoaders/reducers'
import protosReducer from 'src/protos/reducers'
import {variablesReducer} from 'src/variables/reducers'
import {labelsReducer} from 'src/labels/reducers'
import {bucketsReducer} from 'src/buckets/reducers'
@ -51,7 +50,6 @@ export const rootReducer = combineReducers<ReducerState>({
onboarding: onboardingReducer,
noteEditor: noteEditorReducer,
dataLoading: dataLoadingReducer,
protos: protosReducer,
variables: variablesReducer,
labels: labelsReducer,
buckets: bucketsReducer,

View File

@ -1,4 +1,3 @@
import {Dashboard} from 'src/types/dashboards'
import {Organization} from 'src/types/orgs'
import {Links} from 'src/types/links'
import {Notification} from 'src/types'
@ -11,7 +10,6 @@ import {MeState} from 'src/shared/reducers/v2/me'
import {NoteEditorState} from 'src/dashboards/reducers/notes'
import {DataLoadingState} from 'src/dataLoaders/reducers'
import {OnboardingState} from 'src/onboarding/reducers'
import {ProtosState} from 'src/protos/reducers'
import {VariablesState} from 'src/variables/reducers'
import {OrgViewState} from 'src/organizations/reducers/orgView'
import {LabelsState} from 'src/labels/reducers'
@ -23,6 +21,7 @@ import {RangeState} from 'src/dashboards/reducers/ranges'
import {ViewsState} from 'src/dashboards/reducers/views'
import {ScrapersState} from 'src/scrapers/reducers'
import {UserSettingsState} from 'src/userSettings/reducers'
import {DashboardsState} from 'src/dashboards/reducers/dashboards'
export interface AppState {
VERSION: string
@ -33,7 +32,7 @@ export interface AppState {
app: AppPresentationState
ranges: RangeState
views: ViewsState
dashboards: Dashboard[]
dashboards: DashboardsState
notifications: Notification[]
timeMachines: TimeMachinesState
routing: RouterState
@ -45,7 +44,6 @@ export interface AppState {
onboarding: OnboardingState
noteEditor: NoteEditorState
dataLoading: DataLoadingState
protos: ProtosState
variables: VariablesState
tokens: AuthorizationsState
templates: TemplatesState