mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 00:37:04 -06:00
dashfolders: POC - Use separate component for "Add permission" #10676
This commit is contained in:
parent
ee57ed10f9
commit
666d29fafa
@ -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>
|
||||
|
144
public/app/core/components/Permissions/AddPermissions.tsx
Normal file
144
public/app/core/components/Permissions/AddPermissions.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
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;
|
@ -1,9 +1,6 @@
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user