From 29c712b91ce5bc850fe3fdbd143da6725a5d288c Mon Sep 17 00:00:00 2001 From: Iris Scholten Date: Thu, 28 Mar 2019 11:06:40 -0700 Subject: [PATCH] fix(ui): Consolidate dashboards state Co-authored-by: Andrew Watkins --- .../configuration/components/GetResources.tsx | 12 ++ ui/src/dashboards/actions/index.ts | 104 +++++----- ui/src/dashboards/actions/notes.ts | 2 +- .../dashboards/components/DashboardPage.tsx | 2 +- ui/src/dashboards/components/VEOContents.tsx | 2 +- .../dashboard_index/DashboardCards.tsx | 2 +- .../dashboard_index/DashboardsIndex.tsx | 53 ++--- ui/src/dashboards/reducers/dashboards.test.ts | 69 ++++--- ui/src/dashboards/reducers/dashboards.ts | 186 ++++++++++-------- ui/src/dashboards/selectors/index.ts | 2 +- .../components/SaveAsCellForm.tsx | 2 +- .../configure/TelegrafPluginInstructions.tsx | 3 - ui/src/organizations/actions/orgView.ts | 23 +-- .../organizations/components/Dashboards.tsx | 21 +- .../containers/OrgDashboardsIndex.tsx | 69 +------ ui/src/organizations/reducers/orgView.ts | 5 - ui/src/protos/actions/index.ts | 105 ---------- ui/src/protos/reducers/index.ts | 27 --- ui/src/store/configureStore.ts | 4 +- ui/src/types/stores.ts | 6 +- 20 files changed, 274 insertions(+), 425 deletions(-) delete mode 100644 ui/src/protos/actions/index.ts delete mode 100644 ui/src/protos/reducers/index.ts diff --git a/ui/src/configuration/components/GetResources.tsx b/ui/src/configuration/components/GetResources.tsx index 9ce33e964d..3b775ae53c 100644 --- a/ui/src/configuration/components/GetResources.tsx +++ b/ui/src/configuration/components/GetResources.tsx @@ -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 { 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( diff --git a/ui/src/dashboards/actions/index.ts b/ui/src/dashboards/actions/index.ts index 91428c0b83..0bc696bc42 100644 --- a/ui/src/dashboards/actions/index.ts +++ b/ui/src/dashboards/actions/index.ts @@ -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 ): Promise => { 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 ): Promise => { - 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 => { 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 => { 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)) diff --git a/ui/src/dashboards/actions/notes.ts b/ui/src/dashboards/actions/notes.ts index 3a8d4cb0f1..ff20f04dfc 100644 --- a/ui/src/dashboards/actions/notes.ts +++ b/ui/src/dashboards/actions/notes.ts @@ -65,7 +65,7 @@ export const createNoteCell = (dashboardID: string) => async ( dispatch: Dispatch, 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}"`) diff --git a/ui/src/dashboards/components/DashboardPage.tsx b/ui/src/dashboards/components/DashboardPage.tsx index 060d9044c8..f50f79cde3 100644 --- a/ui/src/dashboards/components/DashboardPage.tsx +++ b/ui/src/dashboards/components/DashboardPage.tsx @@ -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, diff --git a/ui/src/dashboards/components/VEOContents.tsx b/ui/src/dashboards/components/VEOContents.tsx index c605fbedfe..2af566470a 100644 --- a/ui/src/dashboards/components/VEOContents.tsx +++ b/ui/src/dashboards/components/VEOContents.tsx @@ -105,7 +105,7 @@ class VEOContents extends PureComponent { 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) diff --git a/ui/src/dashboards/components/dashboard_index/DashboardCards.tsx b/ui/src/dashboards/components/dashboard_index/DashboardCards.tsx index 33378ac99d..38378aed0d 100644 --- a/ui/src/dashboards/components/dashboard_index/DashboardCards.tsx +++ b/ui/src/dashboards/components/dashboard_index/DashboardCards.tsx @@ -84,7 +84,7 @@ class DashboardCards extends PureComponent { const mstp = (state: AppState, props: OwnProps) => { return { - sortedIDs: getSortedResource(state.dashboards, props), + sortedIDs: getSortedResource(state.dashboards.list, props), } } diff --git a/ui/src/dashboards/components/dashboard_index/DashboardsIndex.tsx b/ui/src/dashboards/components/dashboard_index/DashboardsIndex.tsx index ff624f4859..97cd1681ab 100644 --- a/ui/src/dashboards/components/dashboard_index/DashboardsIndex.tsx +++ b/ui/src/dashboards/components/dashboard_index/DashboardsIndex.tsx @@ -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 { } 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 {
- ( - - )} - 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} - /> + + ( + + )} + 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} + /> +
@@ -174,7 +179,11 @@ class DashboardIndex extends PureComponent { } const mstp = (state: AppState): StateProps => { - const {dashboards, links, orgs} = state + const { + dashboards: {list: dashboards}, + links, + orgs, + } = state return { orgs, diff --git a/ui/src/dashboards/reducers/dashboards.test.ts b/ui/src/dashboards/reducers/dashboards.test.ts index 77e7604f0e..375a73fa9e 100644 --- a/ui/src/dashboards/reducers/dashboards.test.ts +++ b/ui/src/dashboards/reducers/dashboards.test.ts @@ -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) ) diff --git a/ui/src/dashboards/reducers/dashboards.ts b/ui/src/dashboards/reducers/dashboards.ts index 46a276b940..432684456c 100644 --- a/ui/src/dashboards/reducers/dashboards.ts +++ b/ui/src/dashboards/reducers/dashboards.ts @@ -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 + } + } + }) } diff --git a/ui/src/dashboards/selectors/index.ts b/ui/src/dashboards/selectors/index.ts index 8f61dec910..7f21cd0ea2 100644 --- a/ui/src/dashboards/selectors/index.ts +++ b/ui/src/dashboards/selectors/index.ts @@ -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 ) diff --git a/ui/src/dataExplorer/components/SaveAsCellForm.tsx b/ui/src/dataExplorer/components/SaveAsCellForm.tsx index d0bc9a49e4..0a30b55ccf 100644 --- a/ui/src/dataExplorer/components/SaveAsCellForm.tsx +++ b/ui/src/dataExplorer/components/SaveAsCellForm.tsx @@ -234,7 +234,7 @@ class SaveAsCellForm extends PureComponent { const mstp = (state: AppState): StateProps => { const { orgs, - dashboards, + dashboards: {list: dashboards}, timeMachines: { timeMachines: {de}, }, diff --git a/ui/src/dataLoaders/components/collectorsWizard/configure/TelegrafPluginInstructions.tsx b/ui/src/dataLoaders/components/collectorsWizard/configure/TelegrafPluginInstructions.tsx index b25672f38b..b96fbdd05d 100644 --- a/ui/src/dataLoaders/components/collectorsWizard/configure/TelegrafPluginInstructions.tsx +++ b/ui/src/dataLoaders/components/collectorsWizard/configure/TelegrafPluginInstructions.tsx @@ -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, } diff --git a/ui/src/organizations/actions/orgView.ts b/ui/src/organizations/actions/orgView.ts index 22ebb1693d..c83d001926 100644 --- a/ui/src/organizations/actions/orgView.ts +++ b/ui/src/organizations/actions/orgView.ts @@ -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) } diff --git a/ui/src/organizations/components/Dashboards.tsx b/ui/src/organizations/components/Dashboards.tsx index 79aeb51fc0..f81d9f6fb6 100644 --- a/ui/src/organizations/components/Dashboards.tsx +++ b/ui/src/organizations/components/Dashboards.tsx @@ -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 { } } +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( + mstp, mdtp )(withRouter(Dashboards)) diff --git a/ui/src/organizations/containers/OrgDashboardsIndex.tsx b/ui/src/organizations/containers/OrgDashboardsIndex.tsx index 135e353b26..f226190236 100644 --- a/ui/src/organizations/containers/OrgDashboardsIndex.tsx +++ b/ui/src/organizations/containers/OrgDashboardsIndex.tsx @@ -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 { - 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 { url="dashboards" title="Dashboards" > - {this.orgsDashboardsPage} + + + @@ -98,49 +76,18 @@ class OrgDashboardsIndex extends Component { ) } - - private get orgsDashboardsPage() { - const {org, dashboards} = this.props - const {loadingState} = this.state - return ( - } - > - - - ) - } - - 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( diff --git a/ui/src/organizations/reducers/orgView.ts b/ui/src/organizations/reducers/orgView.ts index 2bd65c0ea2..25426085b9 100644 --- a/ui/src/organizations/reducers/orgView.ts +++ b/ui/src/organizations/reducers/orgView.ts @@ -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 } diff --git a/ui/src/protos/actions/index.ts b/ui/src/protos/actions/index.ts deleted file mode 100644 index 4ab251b3c7..0000000000 --- a/ui/src/protos/actions/index.ts +++ /dev/null @@ -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) => { - 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())) - } -} diff --git a/ui/src/protos/reducers/index.ts b/ui/src/protos/reducers/index.ts deleted file mode 100644 index a9dc0b316d..0000000000 --- a/ui/src/protos/reducers/index.ts +++ /dev/null @@ -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 diff --git a/ui/src/store/configureStore.ts b/ui/src/store/configureStore.ts index a3592d754f..8954d74d21 100644 --- a/ui/src/store/configureStore.ts +++ b/ui/src/store/configureStore.ts @@ -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({ onboarding: onboardingReducer, noteEditor: noteEditorReducer, dataLoading: dataLoadingReducer, - protos: protosReducer, variables: variablesReducer, labels: labelsReducer, buckets: bucketsReducer, diff --git a/ui/src/types/stores.ts b/ui/src/types/stores.ts index 4ace82dec7..4d5cf97bcd 100644 --- a/ui/src/types/stores.ts +++ b/ui/src/types/stores.ts @@ -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