wip: folder settings page to redux progress

pull/13235/head
Torkel Ödegaard 7 years ago
parent 89ea47e7fb
commit 19cbff658b
  1. 4
      public/app/core/reducers/location.ts
  2. 10
      public/app/core/services/backend_srv.ts
  3. 2
      public/app/features/dashboard/all.ts
  4. 94
      public/app/features/dashboard/folder_settings_ctrl.ts
  5. 152
      public/app/features/manage-dashboards/FolderSettingsPage.tsx
  6. 24
      public/app/features/manage-dashboards/state/actions.ts
  7. 7
      public/app/features/manage-dashboards/state/reducers.ts
  8. 60
      public/app/stores/FolderStore/FolderStore.ts
  9. 1
      public/app/types/dashboard.ts
  10. 1
      public/app/types/index.ts

@ -9,8 +9,8 @@ export const initialState: LocationState = {
routeParams: {},
};
function renderUrl(path: string, query: UrlQueryMap): string {
if (Object.keys(query).length > 0) {
function renderUrl(path: string, query: UrlQueryMap | undefined): string {
if (query && Object.keys(query).length > 0) {
path += '?' + toUrlParams(query);
}
return path;

@ -252,16 +252,6 @@ export class BackendSrv {
return this.post('/api/folders', payload);
}
updateFolder(folder, options) {
options = options || {};
return this.put(`/api/folders/${folder.uid}`, {
title: folder.title,
version: folder.version,
overwrite: options.overwrite === true,
});
}
deleteFolder(uid: string, showSuccessAlert) {
return this.request({ method: 'DELETE', url: `/api/folders/${uid}`, showSuccessAlert: showSuccessAlert === true });
}

@ -32,11 +32,9 @@ import './dashlinks/module';
import coreModule from 'app/core/core_module';
import { FolderDashboardsCtrl } from './folder_dashboards_ctrl';
import { FolderSettingsCtrl } from './folder_settings_ctrl';
import { DashboardImportCtrl } from './dashboard_import_ctrl';
import { CreateFolderCtrl } from './create_folder_ctrl';
coreModule.controller('FolderDashboardsCtrl', FolderDashboardsCtrl);
coreModule.controller('FolderSettingsCtrl', FolderSettingsCtrl);
coreModule.controller('DashboardImportCtrl', DashboardImportCtrl);
coreModule.controller('CreateFolderCtrl', CreateFolderCtrl);

@ -1,94 +0,0 @@
import { FolderPageLoader } from './folder_page_loader';
import appEvents from 'app/core/app_events';
export class FolderSettingsCtrl {
folderPageLoader: FolderPageLoader;
navModel: any;
folderId: number;
uid: string;
canSave = false;
folder: any;
title: string;
hasChanged: boolean;
/** @ngInject */
constructor(private backendSrv, navModelSrv, private $routeParams, private $location) {
if (this.$routeParams.uid) {
this.uid = $routeParams.uid;
this.folderPageLoader = new FolderPageLoader(this.backendSrv);
this.folderPageLoader.load(this, this.uid, 'manage-folder-settings').then(folder => {
if ($location.path() !== folder.meta.url) {
$location.path(`${folder.meta.url}/settings`).replace();
}
this.folder = folder;
this.canSave = this.folder.canSave;
this.title = this.folder.title;
});
}
}
save() {
this.titleChanged();
if (!this.hasChanged) {
return;
}
this.folder.title = this.title.trim();
return this.backendSrv
.updateFolder(this.folder)
.then(result => {
if (result.url !== this.$location.path()) {
this.$location.url(result.url + '/settings');
}
appEvents.emit('dashboard-saved');
appEvents.emit('alert-success', ['Folder saved']);
})
.catch(this.handleSaveFolderError);
}
titleChanged() {
this.hasChanged = this.folder.title.toLowerCase() !== this.title.trim().toLowerCase();
}
delete(evt) {
if (evt) {
evt.stopPropagation();
evt.preventDefault();
}
appEvents.emit('confirm-modal', {
title: 'Delete',
text: `Do you want to delete this folder and all its dashboards?`,
icon: 'fa-trash',
yesText: 'Delete',
onConfirm: () => {
return this.backendSrv.deleteFolder(this.uid).then(() => {
appEvents.emit('alert-success', ['Folder Deleted', `${this.folder.title} has been deleted`]);
this.$location.url('dashboards');
});
},
});
}
handleSaveFolderError(err) {
if (err.data && err.data.status === 'version-mismatch') {
err.isHandled = true;
appEvents.emit('confirm-modal', {
title: 'Conflict',
text: 'Someone else has updated this folder.',
text2: 'Would you still like to save this folder?',
yesText: 'Save & Overwrite',
icon: 'fa-warning',
onConfirm: () => {
this.backendSrv.updateFolder(this.folder, { overwrite: true });
},
});
}
}
}

@ -4,121 +4,53 @@ import { connect } from 'react-redux';
import PageHeader from 'app/core/components/PageHeader/PageHeader';
import appEvents from 'app/core/app_events';
import { getNavModel } from 'app/core/selectors/navModel';
import { NavModel, StoreState } from 'app/types';
import { getFolderByUid } from './state/actions';
import { NavModel, StoreState, FolderState } from 'app/types';
import { getFolderByUid, setFolderTitle, saveFolder, deleteFolder } from './state/actions';
export interface Props {
navModel: NavModel;
folderUid: string;
folder: FolderState;
getFolderByUid: typeof getFolderByUid;
setFolderTitle: typeof setFolderTitle;
saveFolder: typeof saveFolder;
deleteFolder: typeof deleteFolder;
}
export class FolderSettingsPage extends PureComponent<Props> {
// formSnapshot: any;
//
componentDidMount() {
this.props.getFolderByUid(this.props.folderUid);
}
//
// loadStore() {
// const { nav, folder, view } = this.props;
//
// return folder.load(view.routeParams.get('uid') as string).then(res => {
// this.formSnapshot = getSnapshot(folder);
// view.updatePathAndQuery(`${res.url}/settings`, {}, {});
//
// return nav.initFolderNav(toJS(folder.folder), 'manage-folder-settings');
// });
// }
// onTitleChange(evt) {
// this.props.folder.setTitle(this.getFormSnapshot().folder.title, evt.target.value);
// }
//
// getFormSnapshot() {
// if (!this.formSnapshot) {
// this.formSnapshot = getSnapshot(this.props.folder);
// }
//
// return this.formSnapshot;
// }
//
// save(evt) {
// if (evt) {
// evt.stopPropagation();
// evt.preventDefault();
// }
//
// const { nav, folder, view } = this.props;
//
// folder
// .saveFolder({ overwrite: false })
// .then(newUrl => {
// view.updatePathAndQuery(newUrl, {}, {});
//
// appEvents.emit('dashboard-saved');
// appEvents.emit('alert-success', ['Folder saved']);
// })
// .then(() => {
// return nav.initFolderNav(toJS(folder.folder), 'manage-folder-settings');
// })
// .catch(this.handleSaveFolderError.bind(this));
// }
//
// delete(evt) {
// if (evt) {
// evt.stopPropagation();
// evt.preventDefault();
// }
//
// const { folder, view } = this.props;
// const title = folder.folder.title;
//
// appEvents.emit('confirm-modal', {
// title: 'Delete',
// text: `Do you want to delete this folder and all its dashboards?`,
// icon: 'fa-trash',
// yesText: 'Delete',
// onConfirm: () => {
// return folder.deleteFolder().then(() => {
// appEvents.emit('alert-success', ['Folder Deleted', `${title} has been deleted`]);
// view.updatePathAndQuery('dashboards', '', '');
// });
// },
// });
// }
//
// handleSaveFolderError(err) {
// if (err.data && err.data.status === 'version-mismatch') {
// err.isHandled = true;
//
// const { nav, folder, view } = this.props;
//
// appEvents.emit('confirm-modal', {
// title: 'Conflict',
// text: 'Someone else has updated this folder.',
// text2: 'Would you still like to save this folder?',
// yesText: 'Save & Overwrite',
// icon: 'fa-warning',
// onConfirm: () => {
// folder
// .saveFolder({ overwrite: true })
// .then(newUrl => {
// view.updatePathAndQuery(newUrl, {}, {});
//
// appEvents.emit('dashboard-saved');
// appEvents.emit('alert-success', ['Folder saved']);
// })
// .then(() => {
// return nav.initFolderNav(toJS(folder.folder), 'manage-folder-settings');
// });
// },
// });
// }
// }
onTitleChange = evt => {
this.props.setFolderTitle(evt.target.value);
};
onSave = async evt => {
evt.preventDefault();
evt.stopPropagation();
await this.props.saveFolder(this.props.folder);
appEvents.emit('alert-success', ['Folder saved']);
};
onDelete = evt => {
evt.stopPropagation();
evt.preventDefault();
appEvents.emit('confirm-modal', {
title: 'Delete',
text: `Do you want to delete this folder and all its dashboards?`,
icon: 'fa-trash',
yesText: 'Delete',
onConfirm: () => {
this.props.deleteFolder(this.props.folder.uid);
},
});
};
render() {
const { navModel } = this.props;
const { navModel, folder } = this.props;
return (
<div>
@ -127,25 +59,21 @@ export class FolderSettingsPage extends PureComponent<Props> {
<h2 className="page-sub-heading">Folder Settings</h2>
<div className="section gf-form-group">
<form name="folderSettingsForm" onSubmit={this.save.bind(this)}>
<form name="folderSettingsForm" onSubmit={this.onSave}>
<div className="gf-form">
<label className="gf-form-label width-7">Name</label>
<input
type="text"
className="gf-form-input width-30"
value={folder.folder.title}
onChange={this.onTitleChange.bind(this)}
value={folder.title}
onChange={this.onTitleChange}
/>
</div>
<div className="gf-form-button-row">
<button
type="submit"
className="btn btn-success"
disabled={!folder.folder.canSave || !folder.folder.hasChanged}
>
<button type="submit" className="btn btn-success" disabled={!folder.canSave || !folder.hasChanged}>
<i className="fa fa-save" /> Save
</button>
<button className="btn btn-danger" onClick={this.delete.bind(this)} disabled={!folder.folder.canSave}>
<button className="btn btn-danger" onClick={this.onDelete} disabled={!folder.canSave}>
<i className="fa fa-trash" /> Delete
</button>
</div>
@ -159,7 +87,6 @@ export class FolderSettingsPage extends PureComponent<Props> {
const mapStateToProps = (state: StoreState) => {
const uid = state.location.routeParams.uid;
return {
navModel: getNavModel(state.navIndex, `folder-settings-${uid}`),
folderUid: uid,
@ -169,6 +96,9 @@ const mapStateToProps = (state: StoreState) => {
const mapDispatchToProps = {
getFolderByUid,
saveFolder,
setFolderTitle,
deleteFolder,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(FolderSettingsPage));

@ -1,8 +1,8 @@
import { getBackendSrv } from 'app/core/services/backend_srv';
import { StoreState } from 'app/types';
import { ThunkAction } from 'redux-thunk';
import { FolderDTO, NavModelItem } from 'app/types';
import { updateNavIndex, UpdateNavIndexAction } from 'app/core/actions';
import { FolderDTO, FolderState, NavModelItem } from 'app/types';
import { updateNavIndex, updateLocation } from 'app/core/actions';
export enum ActionTypes {
LoadFolder = 'LOAD_FOLDER',
@ -32,7 +32,7 @@ export const setFolderTitle = (newTitle: string): SetFolderTitleAction => ({
export type Action = LoadFolderAction | SetFolderTitleAction;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action | UpdateNavIndexAction>;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, any>;
function buildNavModel(folder: FolderDTO): NavModelItem {
return {
@ -67,6 +67,7 @@ function buildNavModel(folder: FolderDTO): NavModelItem {
],
};
}
export function getFolderByUid(uid: string): ThunkResult<void> {
return async dispatch => {
const folder = await getBackendSrv().getFolderByUid(uid);
@ -74,3 +75,20 @@ export function getFolderByUid(uid: string): ThunkResult<void> {
dispatch(updateNavIndex(buildNavModel(folder)));
};
}
export function saveFolder(folder: FolderState): ThunkResult<void> {
return async dispatch => {
const res = await getBackendSrv().put(`/api/folders/${folder.uid}`, {
title: folder.title,
version: folder.version,
});
dispatch(updateLocation({ path: `${res.url}/settings` }));
};
}
export function deleteFolder(uid: string): ThunkResult<void> {
return async dispatch => {
await getBackendSrv().deleteFolder(uid, true);
dispatch(updateLocation({ path: `dashboards` }));
};
}

@ -16,9 +16,14 @@ export const folderReducer = (state = inititalState, action: Action): FolderStat
case ActionTypes.LoadFolder:
return {
...action.payload,
canSave: false,
hasChanged: false,
};
case ActionTypes.SetFolderTitle:
return {
...state,
title: action.payload,
hasChanged: true,
};
}
return state;
};

@ -1,60 +0,0 @@
import { types, getEnv, flow } from 'mobx-state-tree';
export const Folder = types.model('Folder', {
id: types.identifier(types.number),
uid: types.string,
title: types.string,
url: types.string,
canSave: types.boolean,
hasChanged: types.boolean,
version: types.number,
});
export const FolderStore = types
.model('FolderStore', {
folder: types.maybe(Folder),
})
.actions(self => ({
load: flow(function* load(uid: string) {
// clear folder state
if (self.folder && self.folder.uid !== uid) {
self.folder = null;
}
const backendSrv = getEnv(self).backendSrv;
const res = yield backendSrv.getFolderByUid(uid);
self.folder = Folder.create({
id: res.id,
uid: res.uid,
title: res.title,
url: res.url,
canSave: res.canSave,
hasChanged: false,
version: res.version,
});
return res;
}),
setTitle: (originalTitle: string, title: string) => {
self.folder.title = title;
self.folder.hasChanged = originalTitle.toLowerCase() !== title.trim().toLowerCase() && title.trim().length > 0;
},
saveFolder: flow(function* saveFolder(options: any) {
const backendSrv = getEnv(self).backendSrv;
self.folder.title = self.folder.title.trim();
const res = yield backendSrv.updateFolder(self.folder, options);
self.folder.url = res.url;
self.folder.version = res.version;
return `${self.folder.url}/settings`;
}),
deleteFolder: flow(function* deleteFolder() {
const backendSrv = getEnv(self).backendSrv;
return backendSrv.deleteFolder(self.folder.uid);
}),
}));

@ -4,6 +4,7 @@ export interface FolderDTO {
title: string;
url: string;
version: number;
canSave: boolean;
}
export interface FolderState {

@ -30,4 +30,5 @@ export interface StoreState {
alertRules: AlertRulesState;
teams: TeamsState;
team: TeamState;
folder: FolderState;
}

Loading…
Cancel
Save