Commit 666d29fa by Johannes Schill

dashfolders: POC - Use separate component for "Add permission" #10676

parent ee57ed10
......@@ -6,11 +6,14 @@ import PageHeader from 'app/core/components/PageHeader/PageHeader';
import Permissions from 'app/core/components/Permissions/Permissions';
import Tooltip from 'app/core/components/Tooltip/Tooltip';
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
import AddPermissions from 'app/core/components/Permissions/AddPermissions';
@inject('nav', 'folder', 'view', 'permissions')
@observer
export class FolderPermissions extends Component<IContainerProps, any> {
constructor(props) {
super(props);
this.handleAddPermission = this.handleAddPermission.bind(this);
this.loadStore();
}
......@@ -21,6 +24,11 @@ export class FolderPermissions extends Component<IContainerProps, any> {
});
}
handleAddPermission() {
const { permissions } = this.props;
permissions.toggleAddPermissions();
}
render() {
const { nav, folder, permissions, backendSrv } = this.props;
......@@ -34,13 +42,20 @@ export class FolderPermissions extends Component<IContainerProps, any> {
<div>
<PageHeader model={nav as any} />
<div className="page-container page-body">
<div className="page-sub-heading">
<div className="page-action-bar">
<h2 className="d-inline-block">Folder Permissions</h2>
<Tooltip className="page-sub-heading-icon" placement="auto" content={PermissionsInfo}>
<i className="gicon gicon-question gicon--has-hover" />
</Tooltip>
<div className="page-action-bar__spacer" />
<button className="btn btn-success pull-right" onClick={this.handleAddPermission}>
<i className="fa fa-plus" />
Add Permission
</button>
</div>
{permissions.isAddPermissionsVisible ? (
<AddPermissions permissions={permissions} backendSrv={backendSrv} dashboardId={dashboardId} />
) : null}
<Permissions permissions={permissions} isFolder={true} dashboardId={dashboardId} backendSrv={backendSrv} />
</div>
</div>
......
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore';
import UserPicker, { User } from 'app/core/components/Picker/UserPicker';
import TeamPicker, { Team } from 'app/core/components/Picker/TeamPicker';
import DescriptionPicker, { OptionWithDescription } from 'app/core/components/Picker/DescriptionPicker';
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
export interface IProps {
permissions: any;
backendSrv: any;
dashboardId: any;
}
@observer
class AddPermissions extends Component<IProps, any> {
constructor(props) {
super(props);
this.userPicked = this.userPicked.bind(this);
this.teamPicked = this.teamPicked.bind(this);
this.permissionPicked = this.permissionPicked.bind(this);
this.typeChanged = this.typeChanged.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentWillMount() {
const { permissions } = this.props;
permissions.resetNewType();
}
typeChanged(evt) {
const { value } = evt.target;
const { permissions } = this.props;
// if (value === 'Viewer' || value === 'Editor') {
// // permissions.addStoreItem({ permission: 1, role: value, dashboardId: dashboardId }, dashboardId);
// // this.resetNewType();
// return;
// }
permissions.setNewType(value);
}
userPicked(user: User) {
const { permissions } = this.props;
if (!user) {
permissions.newItem.setUser(null, null);
return;
}
permissions.newItem.setUser(user.id, user.login);
// return permissions.addStoreItem({ userId: user.id, userLogin: user.login, permission: 1 });
}
teamPicked(team: Team) {
const { permissions } = this.props;
if (!team) {
permissions.newItem.setTeam(null, null);
return;
}
permissions.newItem.setTeam(team.id, team.name);
}
permissionPicked(permission: OptionWithDescription) {
const { permissions } = this.props;
permissions.newItem.setPermission(permission.value);
}
resetNewType() {
const { permissions } = this.props;
permissions.resetNewType();
}
handleSubmit(evt) {
evt.preventDefault();
const { permissions } = this.props;
permissions.addStoreItem();
}
render() {
const { permissions, backendSrv } = this.props;
const newItem = permissions.newItem;
return (
<div className="gf-form-inline cta-form">
<form name="addPermission" className="gf-form-group" onSubmit={this.handleSubmit}>
<h6 className="muted">Add Permission For</h6>
<div className="gf-form-inline">
<div className="gf-form">
<div className="gf-form-select-wrapper">
<select className="gf-form-input gf-size-auto" value={newItem.type} onChange={this.typeChanged}>
{aclTypes.map((option, idx) => {
return (
<option key={idx} value={option.value}>
{option.text}
</option>
);
})}
</select>
</div>
</div>
{newItem.type === 'User' ? (
<div className="gf-form">
<UserPicker backendSrv={backendSrv} handlePicked={this.userPicked} value={newItem.userId} />
</div>
) : null}
{newItem.type === 'Group' ? (
<div className="gf-form">
<TeamPicker backendSrv={backendSrv} handlePicked={this.teamPicked} value={newItem.teamId} />
</div>
) : null}
<div className="gf-form">
<DescriptionPicker
optionsWithDesc={permissionOptions}
handlePicked={this.permissionPicked}
value={newItem.permission}
disabled={false}
className={'gf-form-input--form-dropdown-right'}
/>
</div>
<div className="gf-form">
<button className="btn btn-success" type="submit" disabled={!newItem.isValid()}>
Save
</button>
</div>
</div>
</form>
{permissions.error ? (
<div className="gf-form width-17">
<span ng-if="ctrl.error" className="text-error p-l-1">
<i className="fa fa-warning" />
{permissions.error}
</span>
</div>
) : null}
</div>
);
}
}
export default AddPermissions;
import React, { Component } from 'react';
import PermissionsList from './PermissionsList';
import { observer } from 'mobx-react';
import UserPicker, { User } from 'app/core/components/Picker/UserPicker';
import TeamPicker, { Team } from 'app/core/components/Picker/TeamPicker';
import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore';
import { FolderInfo } from './FolderInfo';
export interface DashboardAcl {
......@@ -40,8 +37,6 @@ class Permissions extends Component<IProps, any> {
this.permissionChanged = this.permissionChanged.bind(this);
this.typeChanged = this.typeChanged.bind(this);
this.removeItem = this.removeItem.bind(this);
this.userPicked = this.userPicked.bind(this);
this.teamPicked = this.teamPicked.bind(this);
this.loadStore(dashboardId, isFolder);
}
......@@ -77,18 +72,8 @@ class Permissions extends Component<IProps, any> {
permissions.setNewType(value);
}
userPicked(user: User) {
const { permissions } = this.props;
return permissions.addStoreItem({ userId: user.id, userLogin: user.login, permission: 1 });
}
teamPicked(team: Team) {
const { permissions } = this.props;
return permissions.addStoreItem({ teamId: team.id, team: team.name, permission: 1 });
}
render() {
const { permissions, folderInfo, backendSrv } = this.props;
const { permissions, folderInfo } = this.props;
return (
<div className="gf-form-group">
......@@ -99,50 +84,6 @@ class Permissions extends Component<IProps, any> {
fetching={permissions.fetching}
folderInfo={folderInfo}
/>
<div className="gf-form-inline">
<form name="addPermission" className="gf-form-group">
<h6 className="muted">Add Permission For</h6>
<div className="gf-form-inline">
<div className="gf-form">
<div className="gf-form-select-wrapper">
<select
className="gf-form-input gf-size-auto"
value={permissions.newType}
onChange={this.typeChanged}
>
{aclTypes.map((option, idx) => {
return (
<option key={idx} value={option.value}>
{option.text}
</option>
);
})}
</select>
</div>
</div>
{permissions.newType === 'User' ? (
<div className="gf-form">
<UserPicker backendSrv={backendSrv} handlePicked={this.userPicked} />
</div>
) : null}
{permissions.newType === 'Group' ? (
<div className="gf-form">
<TeamPicker backendSrv={backendSrv} handlePicked={this.teamPicked} />
</div>
) : null}
</div>
</form>
{permissions.error ? (
<div className="gf-form width-17">
<span ng-if="ctrl.error" className="text-error p-l-1">
<i className="fa fa-warning" />
{permissions.error}
</span>
</div>
) : null}
</div>
</div>
);
}
......
......@@ -9,6 +9,7 @@ export interface IProps {
isLoading: boolean;
toggleLoading: any;
handlePicked: (user) => void;
value?: string;
}
export interface Team {
......@@ -54,7 +55,7 @@ class TeamPicker extends Component<IProps, any> {
render() {
const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async;
const { isLoading, handlePicked } = this.props;
const { isLoading, handlePicked, value } = this.props;
return (
<div className="user-picker">
......@@ -70,6 +71,7 @@ class TeamPicker extends Component<IProps, any> {
className="width-8 gf-form-input gf-form-input--form-dropdown"
optionComponent={PickerOption}
placeholder="Choose"
value={value}
/>
</div>
);
......
......@@ -9,6 +9,7 @@ export interface IProps {
isLoading: boolean;
toggleLoading: any;
handlePicked: (user) => void;
value?: string;
}
export interface User {
......@@ -53,8 +54,8 @@ class UserPicker extends Component<IProps, any> {
render() {
const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async;
const { isLoading, handlePicked } = this.props;
const { isLoading, handlePicked, value } = this.props;
console.log('value', value);
return (
<div className="user-picker">
<AsyncComponent
......@@ -70,6 +71,8 @@ class UserPicker extends Component<IProps, any> {
className="width-8 gf-form-input gf-form-input--form-dropdown"
optionComponent={PickerOption}
placeholder="Choose"
value={value}
autosize={true}
/>
</div>
);
......
......@@ -3,6 +3,7 @@
export interface IProps {
backendSrv: any;
handlePicked: (data) => void;
value?: string;
}
export default function withPicker(WrappedComponent) {
......
......@@ -13,15 +13,62 @@ export const permissionOptions = [
},
];
export const aclTypes = [
{ value: 'Group', text: 'Team' },
{ value: 'User', text: 'User' },
{ value: 'Viewer', text: 'Everyone With Viewer Role' },
{ value: 'Editor', text: 'Everyone With Editor Role' },
];
export const aclTypeValues = {
GROUP: { value: 'Group', text: 'Team' },
USER: { value: 'User', text: 'User' },
VIEWER: { value: 'Viewer', text: 'Everyone With Viewer Role' },
EDITOR: { value: 'Editor', text: 'Everyone With Editor Role' },
};
export const aclTypes = Object.keys(aclTypeValues).map(item => aclTypeValues[item]);
const defaultNewType = aclTypes[0].value;
const NewPermissionsItem = types
.model('NewPermissionsItem', {
type: types.optional(
types.enumeration(Object.keys(aclTypeValues).map(item => aclTypeValues[item].value)),
defaultNewType
),
userId: types.maybe(types.number),
userLogin: types.maybe(types.string),
teamId: types.maybe(types.number),
team: types.maybe(types.string),
permission: types.optional(types.number, 1),
})
.views(self => ({
isValid: () => {
switch (self.type) {
case aclTypeValues.GROUP.value:
return self.teamId && self.team;
case aclTypeValues.USER.value:
return self.userId && self.userLogin;
case aclTypeValues.VIEWER.value:
case aclTypeValues.EDITOR.value:
return true;
default:
return false;
}
},
}))
.actions(self => ({
setUser(userId: number, userLogin: string) {
self.userId = userId;
self.userLogin = userLogin;
self.teamId = null;
self.team = null;
},
setTeam(teamId: number, team: string) {
self.userId = null;
self.userLogin = null;
self.teamId = teamId;
self.team = team;
},
setPermission(permission: number) {
self.permission = permission;
},
}));
export const PermissionsStore = types
.model('PermissionsStore', {
fetching: types.boolean,
......@@ -31,6 +78,8 @@ export const PermissionsStore = types
error: types.maybe(types.string),
originalItems: types.optional(types.array(PermissionsStoreItem), []),
newType: types.optional(types.string, defaultNewType),
newItem: types.maybe(NewPermissionsItem),
isAddPermissionsVisible: types.optional(types.boolean, false),
})
.views(self => ({
isValid: item => {
......@@ -46,48 +95,91 @@ export const PermissionsStore = types
return true;
},
}))
.actions(self => ({
load: flow(function* load(dashboardId: number, isFolder: boolean) {
const backendSrv = getEnv(self).backendSrv;
self.fetching = true;
self.isFolder = isFolder;
self.dashboardId = dashboardId;
const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`);
const items = prepareServerResponse(res, dashboardId, isFolder);
self.items = items;
self.originalItems = items;
self.fetching = false;
}),
addStoreItem: flow(function* addStoreItem(item) {
.actions(self => {
const resetNewType = () => {
self.error = null;
if (!self.isValid(item)) {
return undefined;
}
self.newItem = NewPermissionsItem.create();
};
self.items.push(prepareItem(item, self.dashboardId, self.isFolder));
return updateItems(self);
}),
removeStoreItem: flow(function* removeStoreItem(idx: number) {
self.error = null;
self.items.splice(idx, 1);
return updateItems(self);
}),
updatePermissionOnIndex: flow(function* updatePermissionOnIndex(
idx: number,
permission: number,
permissionName: string
) {
self.error = null;
self.items[idx].updatePermission(permission, permissionName);
return updateItems(self);
}),
setNewType(newType: string) {
self.newType = newType;
},
resetNewType() {
self.newType = defaultNewType;
},
}));
return {
load: flow(function* load(dashboardId: number, isFolder: boolean) {
const backendSrv = getEnv(self).backendSrv;
self.fetching = true;
self.isFolder = isFolder;
self.dashboardId = dashboardId;
const res = yield backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`);
const items = prepareServerResponse(res, dashboardId, isFolder);
self.items = items;
self.originalItems = items;
self.fetching = false;
}),
addStoreItem: flow(function* addStoreItem() {
self.error = null;
let item = {
type: self.newItem.type,
permission: self.newItem.permission,
team: undefined,
teamId: undefined,
userLogin: undefined,
userId: undefined,
role: undefined,
};
switch (self.newItem.type) {
case aclTypeValues.GROUP.value:
item.team = self.newItem.team;
item.teamId = self.newItem.teamId;
break;
case aclTypeValues.USER.value:
item.userLogin = self.newItem.userLogin;
item.userId = self.newItem.userId;
break;
case aclTypeValues.VIEWER.value:
case aclTypeValues.EDITOR.value:
item.role = self.newItem.type;
break;
default:
throw Error('Unknown type: ' + self.newItem.type);
}
if (!self.isValid(item)) {
throw Error('New item not valid');
}
self.items.push(prepareItem(item, self.dashboardId, self.isFolder));
resetNewType();
return updateItems(self);
}),
removeStoreItem: flow(function* removeStoreItem(idx: number) {
self.error = null;
self.items.splice(idx, 1);
return updateItems(self);
}),
updatePermissionOnIndex: flow(function* updatePermissionOnIndex(
idx: number,
permission: number,
permissionName: string
) {
self.error = null;
self.items[idx].updatePermission(permission, permissionName);
return updateItems(self);
}),
setNewType(newType: string) {
self.newItem = NewPermissionsItem.create({ type: newType });
},
resetNewType() {
resetNewType();
},
toggleAddPermissions() {
self.isAddPermissionsVisible = !self.isAddPermissionsVisible;
},
showAddPermissions() {
self.isAddPermissionsVisible = true;
},
hideAddPermissions() {
self.isAddPermissionsVisible = false;
},
};
});
const updateItems = self => {
self.error = null;
......
......@@ -392,3 +392,9 @@ select.gf-form-input ~ .gf-form-help-icon {
top: 10px;
color: $text-muted;
}
.cta-form {
padding: 1rem;
background-color: $dark-2;
margin-bottom: 1rem;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment