Commit 3edd2097 by Torkel Ödegaard

dashboard acl modal

parent cb8b5c0d
......@@ -8,9 +8,9 @@
<i class="fa fa-chevron-left"></i>
</a>
<!-- <a class="navbar&#45;page&#45;btn navbar&#45;page&#45;btn&#45;&#45;search" ng&#45;click="ctrl.showSearch()"> -->
<!-- <i class="fa fa&#45;search"></i> -->
<!-- </a> -->
<a class="navbar-page-btn navbar-page-btn--search" ng-click="ctrl.showSearch()">
<i class="fa fa-search"></i>
</a>
<div ng-if="::!ctrl.hasMenu">
<a href="{{::ctrl.section.url}}" class="navbar-page-btn">
......
......@@ -2,8 +2,9 @@ define([
'jquery',
'angular',
'../core_module',
'lodash',
],
function ($, angular, coreModule) {
function ($, angular, coreModule, _) {
'use strict';
var editViewMap = {
......@@ -12,7 +13,8 @@ function ($, angular, coreModule) {
'templating': { src: 'public/app/features/templating/partials/editor.html'},
'history': { html: '<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>'},
'timepicker': { src: 'public/app/features/dashboard/timepicker/dropdown.html' },
'import': { html: '<dash-import></dash-import>' }
'import': { html: '<dash-import dismiss="dismiss()"></dash-import>', isModal: true },
'permissions': { html: '<dash-acl-modal dismiss="dismiss()"></dash-acl-modal>', isModal: true }
};
coreModule.default.directive('dashEditorView', function($compile, $location, $rootScope) {
......@@ -31,8 +33,7 @@ function ($, angular, coreModule) {
function showEditorPane(evt, options) {
if (options.editview) {
options.src = editViewMap[options.editview].src;
options.html = editViewMap[options.editview].html;
_.defaults(options, editViewMap[options.editview]);
}
if (lastEditView && lastEditView === options.editview) {
......@@ -61,19 +62,24 @@ function ($, angular, coreModule) {
var urlParams = $location.search();
if (options.editview === urlParams.editview) {
delete urlParams.editview;
$location.search(urlParams);
// hack for consistently updating url
setTimeout(function() {
$rootScope.$apply(function() {
$location.search(urlParams);
});
});
}
}
};
if (options.editview === 'import') {
if (options.isModal) {
var modalScope = $rootScope.$new();
modalScope.$on("$destroy", function() {
editorScope.dismiss();
});
$rootScope.appEvent('show-modal', {
templateHtml: '<dash-import></dash-import>',
templateHtml: options.html,
scope: modalScope,
backdrop: 'static'
});
......
......@@ -168,6 +168,12 @@ export class NavModelSrv {
clickHandler: () => dashNavCtrl.openEditView('annotations')
});
menu.push({
title: 'Permissions...',
icon: 'fa fa-fw fa-lock',
clickHandler: () => dashNavCtrl.openEditView('permissions')
});
if (!dashboard.meta.isHome) {
menu.push({
title: 'Version history',
......@@ -199,7 +205,7 @@ export class NavModelSrv {
if (this.contextSrv.isEditor && !dashboard.meta.isFolder) {
menu.push({
title: 'Save As ...',
title: 'Save As...',
icon: 'fa fa-fw fa-save',
clickHandler: () => dashNavCtrl.saveDashboardAs()
});
......
<div class="editor-row">
<h5 class="section-heading">Add New Permission</h5>
<form name="addPermission" class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label">Type</span>
<select class="gf-form-input gf-size-auto" ng-model="ctrl.type" ng-options="r for r in ['User Group', 'User']"></select>
</div>
<div class="gf-form" ng-show="ctrl.type === 'User'">
<span class="gf-form-label">User</span>
<user-picker user-id="ctrl.userId"></user-picker>
<div class="modal-body modal-body--with-overflow">
<div class="modal-header">
<h2 class="modal-header-title">
<i class="fa fa-lock"></i>
<span class="p-l-1">Permissions</span>
</h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i>
</a>
</div>
<form ng-submit="ctrl.save()" class="modal-content" novalidate>
<!-- <h5 class="section&#45;heading">Add New Permission</h5> -->
<!-- <form name="addPermission" class="gf&#45;form&#45;group"> -->
<!-- <div class="gf&#45;form&#45;inline"> -->
<!-- <div class="gf&#45;form"> -->
<!-- <span class="gf&#45;form&#45;label">Type</span> -->
<!-- <select class="gf&#45;form&#45;input gf&#45;size&#45;auto" ng&#45;model="ctrl.type" ng&#45;options="r for r in ['User Group', 'User']"></select> -->
<!-- </div> -->
<!-- <div class="gf&#45;form" ng&#45;show="ctrl.type === 'User'"> -->
<!-- <span class="gf&#45;form&#45;label">User</span> -->
<!-- <user&#45;picker user&#45;id="ctrl.userId"></user&#45;picker> -->
<!-- </div> -->
<!-- <div class="gf&#45;form" ng&#45;show="ctrl.type === 'User Group'"> -->
<!-- <span class="gf&#45;form&#45;label">User Group</span> -->
<!-- <user&#45;group&#45;picker user&#45;group&#45;id="ctrl.userGroupId"></user&#45;group&#45;picker> -->
<!-- </div> -->
<!-- <div class="gf&#45;form"> -->
<!-- <span class="gf&#45;form&#45;label">Permission</span> -->
<!-- <select class="gf&#45;form&#45;input gf&#45;size&#45;auto" ng&#45;model="ctrl.permission" ng&#45;options="p.value as p.text for p in ctrl.permissionTypeOptions"></select> -->
<!-- </div> -->
<!-- <div class="gf&#45;form"> -->
<!-- <button class="btn gf&#45;form&#45;btn btn&#45;success" ng&#45;click="ctrl.addPermission()">Add</button> -->
<!-- </div> -->
<!-- </div> -->
<!-- </form> -->
<div>
<div class="section">
<h5 class="section-heading">Groups & Users</h5>
<div class="gf-form" ng-repeat="acl in ctrl.userAcl">
<span class="gf-form-label width-15">
<i class="fa fa-fw fa-user"></i>
{{acl.userLogin}}
</span>
<div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="acl.permissions" ng-options="p.value as p.text for p in ctrl.permissionTypeOptions" ng-change="ctrl.updatePermission(permission)"></select>
</div>
<button class="btn btn-inverse gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.current.meta.mixed">
<i class="fa fa-remove"></i>
</button>
</div>
<div class="gf-form">
<button class="btn btn-inverse gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.current.meta.mixed">
<i class="fa fa-fw fa-plus"></i>
Add Permission
</button>
</div>
</div>
<div class="gf-form" ng-show="ctrl.type === 'User Group'">
<span class="gf-form-label">User Group</span>
<user-group-picker user-group-id="ctrl.userGroupId"></user-group-picker>
<div class="section pull-right">
<h5 class="section-heading">Built-in roles</h5>
<div class="gf-form" ng-repeat="roleAcl in ctrl.roles">
<span class="gf-form-label width-5">
{{roleAcl.name}}
</span>
<div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="roleAcl.permissions" ng-options="p.value as p.text for p in ctrl.roleOptions" ng-change="ctrl.updatePermission(permission)"></select>
</div>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label">Permission</span>
<select class="gf-form-input gf-size-auto" ng-model="ctrl.permission" ng-options="p.value as p.text for p in ctrl.permissionTypeOptions"></select>
</div>
<div class="gf-form">
<button class="btn gf-form-btn btn-success" ng-click="ctrl.addPermission()">Add</button>
</div>
</div>
</form>
</div>
<div class="permissionlist">
<div class="permissionlist__section">
<div class="permissionlist__section-header">
<h6>Permissions</h6>
</div>
<table class="filter-table form-inline">
<thead>
<tr>
<th style="width: 50px;"></th>
<th>Name</th>
<th style="width: 220px;">Permission</th>
<th style="width: 120px"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="permission in ctrl.userPermissions" class="permissionlist__item">
<td><i class="fa fa-fw fa-user"></i></td>
<td>{{permission.userLogin}}</td>
<td><select class="gf-form-input gf-size-auto" ng-model="permission.permissions" ng-options="p.value as p.text for p in ctrl.permissionTypeOptions" ng-change="ctrl.updatePermission(permission)"></select></td>
<td class="text-right">
<a ng-click="ctrl.removePermission(permission)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i>
</a>
</td>
</tr>
<tr ng-repeat="permission in ctrl.userGroupPermissions" class="permissionlist__item">
<td><i class="fa fa-fw fa-users"></i></td>
<td>{{permission.userGroup}}</td>
<td><select class="gf-form-input gf-size-auto" ng-model="permission.permissions" ng-options="p.value as p.text for p in ctrl.permissionTypeOptions" ng-change="ctrl.updatePermission(permission)"></select></td>
<td class="text-right">
<a ng-click="ctrl.removePermission(permission)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i>
</a>
</td>
</tr>
<tr ng-repeat="role in ctrl.roles" class="permissionlist__item">
<td></td>
<td>{{role.name}}</td>
<td><select class="gf-form-input gf-size-auto" ng-model="role.permissions" ng-options="p.value as p.text for p in ctrl.roleOptions" ng-change="ctrl.updatePermission(role)"></select></td>
<td class="text-right">
<div class="clearfix"></div>
</td>
</tr>
</tbody>
</table>
<div class="gf-form-button-row text-center">
<button type="submit" class="btn btn-danger" ng-disabled="!ctrl.canUpdate">Update Permissions</button>
<a class="btn-text" ng-click="ctrl.dismiss();">Close</a>
</div>
</div>
</form>
</div>
<!-- <br> -->
<!-- <br> -->
<!-- <br> -->
<!-- -->
<!-- <div class="permissionlist"> -->
<!-- <div class="permissionlist__section"> -->
<!-- <div class="permissionlist__section&#45;header"> -->
<!-- <h6>Permissions</h6> -->
<!-- </div> -->
<!-- <table class="filter&#45;table form&#45;inline"> -->
<!-- <thead> -->
<!-- <tr> -->
<!-- <th style="width: 50px;"></th> -->
<!-- <th>Name</th> -->
<!-- <th style="width: 220px;">Permission</th> -->
<!-- <th style="width: 120px"></th> -->
<!-- </tr> -->
<!-- </thead> -->
<!-- <tbody> -->
<!-- <tr ng&#45;repeat="permission in ctrl.userPermissions" class="permissionlist__item"> -->
<!-- <td><i class="fa fa&#45;fw fa&#45;user"></i></td> -->
<!-- <td>{{permission.userLogin}}</td> -->
<!-- <td><select class="gf&#45;form&#45;input gf&#45;size&#45;auto" ng&#45;model="permission.permissions" ng&#45;options="p.value as p.text for p in ctrl.permissionTypeOptions" ng&#45;change="ctrl.updatePermission(permission)"></select></td> -->
<!-- <td class="text&#45;right"> -->
<!-- <a ng&#45;click="ctrl.removePermission(permission)" class="btn btn&#45;danger btn&#45;small"> -->
<!-- <i class="fa fa&#45;remove"></i> -->
<!-- </a> -->
<!-- </td> -->
<!-- </tr> -->
<!-- <tr ng&#45;repeat="permission in ctrl.userGroupPermissions" class="permissionlist__item"> -->
<!-- <td><i class="fa fa&#45;fw fa&#45;users"></i></td> -->
<!-- <td>{{permission.userGroup}}</td> -->
<!-- <td><select class="gf&#45;form&#45;input gf&#45;size&#45;auto" ng&#45;model="permission.permissions" ng&#45;options="p.value as p.text for p in ctrl.permissionTypeOptions" ng&#45;change="ctrl.updatePermission(permission)"></select></td> -->
<!-- <td class="text&#45;right"> -->
<!-- <a ng&#45;click="ctrl.removePermission(permission)" class="btn btn&#45;danger btn&#45;small"> -->
<!-- <i class="fa fa&#45;remove"></i> -->
<!-- </a> -->
<!-- </td> -->
<!-- </tr> -->
<!-- <tr ng&#45;repeat="role in ctrl.roles" class="permissionlist__item"> -->
<!-- <td></td> -->
<!-- <td>{{role.name}}</td> -->
<!-- <td><select class="gf&#45;form&#45;input gf&#45;size&#45;auto" ng&#45;model="role.permissions" ng&#45;options="p.value as p.text for p in ctrl.roleOptions" ng&#45;change="ctrl.updatePermission(role)"></select></td> -->
<!-- <td class="text&#45;right"> -->
<!-- -->
<!-- </td> -->
<!-- </tr> -->
<!-- </tbody> -->
<!-- </table> -->
<!-- </div> -->
<!-- </div> -->
<!-- </div> -->
......@@ -5,21 +5,20 @@ import appEvents from 'app/core/app_events';
import _ from 'lodash';
export class AclCtrl {
tabIndex: any;
dashboard: any;
userPermissions: Permission[];
userGroupPermissions: Permission[];
userAcl: DashboardAcl[];
groupAcl: DashboardAcl[];
permissionTypeOptions = [
{value: 1, text: 'View'},
{value: 2, text: 'Read-only Edit'},
{value: 4, text: 'Edit'}
{value: 2, text: 'Edit'},
{value: 4, text: 'Admin'}
];
roleOptions = [
{value: 0, text: 'None'},
{value: 0, text: 'No Access'},
{value: 1, text: 'View'},
{value: 2, text: 'Read-only Edit'},
{value: 4, text: 'Edit'}
{value: 2, text: 'Edit'},
{value: 4, text: 'Admin'}
];
roles = [];
......@@ -30,28 +29,27 @@ export class AclCtrl {
userGroupId: number;
/** @ngInject */
constructor(private backendSrv, private $scope) {
this.tabIndex = 0;
this.userPermissions = [];
this.userGroupPermissions = [];
constructor(private backendSrv, private dashboardSrv) {
this.userAcl = [];
this.groupAcl = [];
this.dashboard = dashboardSrv.getCurrent();
this.get(this.dashboard.id);
}
get(dashboardId: number) {
return this.backendSrv.get(`/api/dashboards/id/${dashboardId}/acl`)
.then(result => {
this.userPermissions = _.filter(result, p => { return p.userId > 0;});
this.userGroupPermissions = _.filter(result, p => { return p.userGroupId > 0;});
this.userAcl = _.filter(result, p => { return p.userId > 0;});
this.groupAcl = _.filter(result, p => { return p.userGroupId > 0;});
this.roles = this.setRoles(result);
});
}
setRoles(result: any) {
return [
{name: 'Org Viewer', permissions: 1},
{name: 'Org Read Only Editor', permissions: 2},
{name: 'Org Editor', permissions: 4},
{name: 'Org Admin', permissions: 4}
{name: 'Viewer', permissions: 1},
{name: 'Editor', permissions: 2},
{name: 'Admin', permissions: 4}
];
}
......@@ -76,21 +74,21 @@ export class AclCtrl {
}
}
addOrUpdateUserPermission(userId: number, permissionType: number) {
addOrUpdateUserPermission(userId: number, permissions: number) {
return this.backendSrv.post(`/api/dashboards/id/${this.dashboard.id}/acl`, {
userId: userId,
permissions: permissionType
permissions: permissions
});
}
addOrUpdateUserGroupPermission(userGroupId: number, permissionType: number) {
addOrUpdateUserGroupPermission(userGroupId: number, permissions: number) {
return this.backendSrv.post(`/api/dashboards/id/${this.dashboard.id}/acl`, {
userGroupId: userGroupId,
permissions: permissionType
permissions: permissions
});
}
updatePermission(permission: any) {
updatePermission(permission: DashboardAcl) {
if (permission.userId > 0) {
return this.addOrUpdateUserPermission(permission.userId, permission.permissions);
} else {
......@@ -101,21 +99,23 @@ export class AclCtrl {
}
}
removePermission(permission: Permission) {
removePermission(permission: DashboardAcl) {
return this.backendSrv.delete(`/api/dashboards/id/${permission.dashboardId}/acl/${permission.id}`).then(() => {
return this.get(permission.dashboardId);
});
}
}
export function aclSettings() {
export function dashAclModal() {
return {
restrict: 'E',
templateUrl: 'public/app/features/dashboard/acl/acl.html',
controller: AclCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: { dashboard: "=" }
scope: {
dismiss: "&"
}
};
}
......@@ -126,7 +126,7 @@ export interface FormModel {
PermissionType: number;
}
export interface Permission {
export interface DashboardAcl {
id: number;
orgId: number;
dashboardId: number;
......@@ -137,8 +137,8 @@ export interface Permission {
userEmail: string;
userGroupId: number;
userGroup: string;
permissions: string[];
permissionType: number[];
permissions: number;
permissionName: string;
}
coreModule.directive('aclSettings', aclSettings);
coreModule.directive('dashAclModal', dashAclModal);
<div class="navbar">
<div class="navbar-inner">
<a class="navbar-brand-btn pointer" ng-click="ctrl.toggleSideMenu()">
<span class="navbar-brand-btn-background">
<img src="public/img/grafana_icon.svg"></img>
</span>
<i class="icon-gf icon-gf-grafana_wordmark"></i>
<i class="fa fa-caret-down"></i>
<i class="fa fa-chevron-left"></i>
</a>
<navbar model="ctrl.navModel">
<div class="navbar-section-wrapper">
<a class="navbar-page-btn" ng-click="ctrl.showSearch()">
<i class="icon-gf icon-gf-dashboard"></i>
{{ctrl.dashboard.title}}
<i class="fa fa-caret-down"></i>
</a>
</div>
<ul class="nav dash-playlist-actions" ng-if="ctrl.playlistSrv.isPlaying">
<li>
<a ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
</li>
</ul>
<ul class="nav dash-playlist-actions" ng-if="ctrl.playlistSrv.isPlaying">
<li>
<a ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
</li>
<ul class="nav pull-left dashnav-action-icons">
<li ng-show="::ctrl.dashboard.meta.canStar">
<a class="pointer" ng-click="ctrl.starDashboard()">
<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}" style="color: orange;"></i>
</a>
</li>
<li ng-show="::ctrl.dashboard.meta.canShare" class="dropdown">
<a class="pointer" ng-click="ctrl.hideTooltip($event)" bs-tooltip="'Share dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-share-square-o"></i></a>
<ul class="dropdown-menu">
<li>
<a ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
<a class="pointer" ng-click="ctrl.shareDashboard(0)">
<i class="fa fa-link"></i> Link to Dashboard
<div class="dropdown-desc">Share an internal link to the current dashboard. Some configuration options available.</div>
</a>
</li>
<li>
<a ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
</li>
</ul>
<ul class="nav pull-left dashnav-action-icons">
<li ng-show="::ctrl.dashboard.meta.canStar">
<a class="pointer" ng-click="ctrl.starDashboard()">
<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}" style="color: orange;"></i>
<a class="pointer" ng-click="ctrl.shareDashboard(1)">
<i class="icon-gf icon-gf-snapshot"></i>Snapshot
<div class="dropdown-desc">Interactive, publically accessible dashboard. Sensitive data is stripped out.</div>
</a>
</li>
<li ng-show="::ctrl.dashboard.meta.canShare" class="dropdown">
<a class="pointer" ng-click="ctrl.hideTooltip($event)" bs-tooltip="'Share dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-share-square-o"></i></a>
<ul class="dropdown-menu">
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(0)">
<i class="fa fa-link"></i> Link to Dashboard
<div class="dropdown-desc">Share an internal link to the current dashboard. Some configuration options available.</div>
</a>
</li>
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(1)">
<i class="icon-gf icon-gf-snapshot"></i>Snapshot
<div class="dropdown-desc">Interactive, publically accessible dashboard. Sensitive data is stripped out.</div>
</a>
</li>
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(2)">
<i class="fa fa-cloud-upload"></i>Export
<div class="dropdown-desc">Export the dashboard to a JSON file for others and to share on Grafana.com</div>
</a>
</li>
</ul>
</li>
<li ng-show="::ctrl.dashboard.meta.canSave">
<a ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom"><i class="fa fa-save"></i></a>
</li>
<li ng-if="::ctrl.dashboard.snapshot.originalUrl">
<a ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom"><i class="fa fa-link"></i></a>
</li>
<li class="dropdown">
<a class="pointer" data-toggle="dropdown">
<i class="fa fa-cog"></i>
<li>
<a class="pointer" ng-click="ctrl.shareDashboard(2)">
<i class="fa fa-cloud-upload"></i>Export
<div class="dropdown-desc">Export the dashboard to a JSON file for others and to share on Grafana.com</div>
</a>
<ul class="dropdown-menu dropdown-menu--navbar">
<li ng-repeat="navItem in ::ctrl.navModel.menu" ng-class="{active: navItem.active}">
<a class="pointer" ng-href="{{::navItem.url}}" ng-click="ctrl.navItemClicked(navItem, $event)">
<i class="{{::navItem.icon}}" ng-show="::navItem.icon"></i>
{{::navItem.title}}
</a>
</li>
</ul>
</li>
</ul>
</li>
<li ng-show="::ctrl.dashboard.meta.canSave">
<a ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom"><i class="fa fa-save"></i></a>
</li>
<li ng-if="::ctrl.dashboard.snapshot.originalUrl">
<a ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom"><i class="fa fa-link"></i></a>
</li>
</ul>
<ul class="nav pull-right">
<li ng-show="ctrl.dashboard.meta.fullscreen" class="dashnav-back-to-dashboard">
<a ng-click="ctrl.exitFullscreen()">
Back to dashboard
</a>
</li>
<li>
<gf-time-picker dashboard="ctrl.dashboard"></gf-time-picker>
</li>
</ul>
</div>
</div>
<ul class="nav pull-right">
<li ng-show="ctrl.dashboard.meta.fullscreen" class="dashnav-back-to-dashboard">
<a ng-click="ctrl.exitFullscreen()">
Back to dashboard
</a>
</li>
<li>
<gf-time-picker dashboard="ctrl.dashboard"></gf-time-picker>
</li>
</ul>
</navbar>
<dashboard-search></dashboard-search>
......@@ -143,17 +143,6 @@ export class DashNavCtrl {
onFolderChange(parentId) {
this.dashboard.parentId = parentId;
}
showSearch() {
this.$rootScope.appEvent('show-dash-search');
}
navItemClicked(navItem, evt) {
if (navItem.clickHandler) {
navItem.clickHandler();
evt.preventDefault();
}
}
}
export function dashNavDirective() {
......
<div class="modal-body">
<div class="modal-header">
<h2 class="modal-header-title">
......
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