Added User Setting Page and User Log Page

Adjusted some other files and made css adjustments

Change-Id: Ib497e148f6775d625179adef0f5515a5a34b9e24
This commit is contained in:
Shuai Zhu 2014-08-28 11:23:53 -07:00
parent 0a8b074ac4
commit c8105bb29a
25 changed files with 3488 additions and 104 deletions

View File

@ -130,6 +130,9 @@ select {
.margin-bottom-10 { .margin-bottom-10 {
margin-bottom: 10px; margin-bottom: 10px;
} }
.margin-bottom-15 {
margin-bottom: 15px;
}
.margin-right-5 { .margin-right-5 {
margin-right: 5px !important; margin-right: 5px !important;
} }
@ -268,6 +271,12 @@ button[disabled] {
.padding-left-15 { .padding-left-15 {
padding-left: 15px; padding-left: 15px;
} }
.padding-left-1 {
padding-left: 1px;
}
.padding-left-2 {
padding-left: 2px;
}
.infobox { .infobox {
width: 260px !important; width: 260px !important;
height: 160px !important; height: 160px !important;
@ -321,14 +330,16 @@ button[disabled] {
.btn-white { .btn-white {
border-width: 1px !important; border-width: 1px !important;
} }
.btn-black-white { .btn-black-white, .btn-black-white:focus {
border: none !important; border: none !important;
background-color: inherit!important; background-color: inherit!important;
color: #000000 !important color: #000000 !important
} }
.btn-trash-hover { .btn-trash-hover {
/* Fixes padding on icon*/ padding: 4px 7px 2px 7px !important;
padding: 1px 3px 1px 4px !important; }
.btn-export-hover {
padding: 4px 8px 2px 8px !important;
} }
button.btn-trash-hover:hover, button.btn-trash-hover:active { button.btn-trash-hover:hover, button.btn-trash-hover:active {
background-color: #d15b47 !important; background-color: #d15b47 !important;
@ -529,8 +540,8 @@ td.center-align {
.top-margin-14 { .top-margin-14 {
margin-top: 14px; margin-top: 14px;
} }
.top-margin-26 { .top-margin-17 {
margin-top: 26px; margin-top: 17px;
} }
table { table {
margin-bottom: 0px !important; margin-bottom: 0px !important;
@ -784,3 +795,128 @@ circle[depth='4'] {
.table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th { .table > thead > tr > td.success, .table > tbody > tr > td.success, .table > tfoot > tr > td.success, .table > thead > tr > th.success, .table > tbody > tr > th.success, .table > tfoot > tr > th.success, .table > thead > tr.success > td, .table > tbody > tr.success > td, .table > tfoot > tr.success > td, .table > thead > tr.success > th, .table > tbody > tr.success > th, .table > tfoot > tr.success > th {
background-color: rgba(239, 243, 252, 0.98); background-color: rgba(239, 243, 252, 0.98);
} }
.search-badge-adjustment {
float: left !important;
display: inline-block !important;
min-width: 400px !important;
}
.badge-adjustment {
padding-top: 23px !important;
padding-left: 0px !important;
}
.skinny-badge {
padding: 1px 7px 1px 7px !important;
}
.server-margin-adjustment {
padding-top: 17px !important;
padding-left: 0px !important;
}
.margin-right-7 {
margin-right: 7px !important;
}
.search-small {
font-size: 65% !important;
}
.top-padding-5 {
padding-top: 5px !important;
}
.margin-top-10 {
margin-top: 10px !important;
}
.margin-top-29 {
margin-top: 29px !important;
}
.margin-left-15 {
margin-left: 15px !important;
}
.margin-left-neg30 {
margin-left: -30px !important;
}
.margin-left-17 {
margin-left: 17px !important;
}
.margin-left-18 {
margin-left: 18px !important;
}
.margin-left-14 {
margin-left: 14px !important;
}
.new-user-form {
margin-left: 160px !important;
}
.input-width-200 {
width: 220px !important;
}
.date-picker-width {
width: 337px !important;
}
.opacity-50 {
opacity: 0.50 !important;
}
.cluster-margin-bottom-20 {
margin-bottom: 20px !important;
}
.padding-bottom-0 {
padding-bottom: 0px !important;
}
.user-form-note {
padding-left: 120px !important;
}
.date-picker-margin {
margin-bottom: 15px !important;
}
.user-row {
height: 50px;
line-height: 55px;
display: inline-block;
}
.row-action {
width: 27%;
}
.row-timestamp {
width: 40%;
}
.row-user-id {
margin-left: 1%;
width: 30%;
}
.log-row {
border-top: 1px solid #ddd;
}
.log-row:nth-child(2n+1) {
background-color: #f9f9f9;
}
.reload-servers-border {
padding: 4px 8px 4px 9px;
}
.cluster-overview-delete, .cluster-overview-delete:focus {
border: none !important;
background-color: inherit !important;
color: black !important;
font-size: 130%;
}
.cluster-overview-menu {
vertical-align: inherit !important;
}
.compass-menu-icon {
display: inline-block;
font-size: 18px;
font-weight: 400;
margin-right: 2px;
min-width: 30px;
text-align: center;
vertical-align: auto !important;
}
.iframe-fail {
display: none;
}
/* iPads (portrait and landscape) ----------- */
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
iframe {
display: none;
}
.iframe-fail {
display: block !important;
}
}

View File

@ -42,6 +42,11 @@
<script type="text/javascript" src="vendor/angular-spinner/spin.min.js"></script> <script type="text/javascript" src="vendor/angular-spinner/spin.min.js"></script>
<script type="text/javascript" src="vendor/angular-spinner/angular-spinner.min.js"></script> <script type="text/javascript" src="vendor/angular-spinner/angular-spinner.min.js"></script>
<script type="text/javascript" src="vendor/angular-daterangepicker/moment.min.js"></script>
<script type="text/javascript" src="vendor/angular-daterangepicker/daterangepicker.js"></script>
<link rel="stylesheet" type="text/css" href="vendor/angular-daterangepicker/daterangepicker-bs3.css">
<script type="text/javascript" src="vendor/angular-daterangepicker/ng-bs-daterangepicker.js"></script>
<script type="text/javascript" src="src/app/app.js"></script> <script type="text/javascript" src="src/app/app.js"></script>
<script type="text/javascript" src="src/app/appDev.js"></script> <script type="text/javascript" src="src/app/appDev.js"></script>
<script type="text/javascript" src="src/app/login/login.js"></script> <script type="text/javascript" src="src/app/login/login.js"></script>
@ -55,6 +60,8 @@
<script type="text/javascript" src="src/common/charts.js"></script> <script type="text/javascript" src="src/common/charts.js"></script>
<script type="text/javascript" src="src/common/findservers/findservers.js"></script> <script type="text/javascript" src="src/common/findservers/findservers.js"></script>
<script type="text/javascript" src="src/common/gantt-chart-d3v2.js"></script> <script type="text/javascript" src="src/common/gantt-chart-d3v2.js"></script>
<script type="text/javascript" src="src/app/user/usersetting.js"></script>
<script type="text/javascript" src="src/app/user/userprofile.js"></script>
</head> </head>
<body ng-app="compass" ng-controller="appController"> <body ng-app="compass" ng-controller="appController">

View File

@ -7,9 +7,11 @@ var app = angular.module('compass', [
'compass.clusterlist', 'compass.clusterlist',
'compass.monitoring', 'compass.monitoring',
'compass.server', 'compass.server',
'compass.userSetting',
'compass.userProfile',
'ui.router', 'ui.router',
'ui.bootstrap', 'ui.bootstrap',
// 'compassAppDev', 'compassAppDev',
'ngAnimate' 'ngAnimate'
]); ]);

View File

@ -425,6 +425,67 @@ compassAppDev.run(function($httpBackend, settings, $http) {
return [201, mockResponse, {}]; return [201, mockResponse, {}];
}); });
$httpBackend.whenPOST(/\.*\/users$/).respond(function(method, url, data) {
console.log(method, url, data);
var postData = JSON.parse(data)
return [201, {}];
});
$httpBackend.whenGET(/\.*\/users$/).respond(function(method, url, data) {
var userSetting = [{
"id": 1,
"email": "admin@compass.org",
"username": "admin",
"first_name": "",
"last_name": "",
"active": false,
"is_admin": true,
"created_at": "2014-4-14",
"last_login_at": "2014-4-14"
}, {
"id": 2,
"email": "tsinghua@compass.org",
"username": "tsinghua",
"first_name": "",
"last_name": "",
"active": true,
"is_admin": true,
"created_at": "2014-4-14",
"last_login": "2014-3-14"
}, {
"id": 3,
"email": "ann@compass.org",
"username": "ann",
"first_name": "Ann",
"last_name": "",
"active": true,
"is_admin": false,
"created_at": "2014-4-14",
"last_login": "2014-3-14"
}, {
"id": 4,
"email": "john@compass.org",
"username": "jsmith",
"first_name": "John",
"last_name": "Smitch",
"active": true,
"is_admin": true,
"created_at": "2014-4-14",
"last_login": "2014-3-14"
}, {
"id": 5,
"email": "tom@compass.org",
"username": "tom",
"first_name": "Tom",
"last_name": "Jones",
"active": true,
"is_admin": false,
"created_at": "2014-4-14",
"last_login": "2014-3-14"
}];
return [201, userSetting, {}];
});
$httpBackend.whenGET(/\.*\/clusters\/[1-9][0-9]*\/state/).respond(function(method, url, data) { $httpBackend.whenGET(/\.*\/clusters\/[1-9][0-9]*\/state/).respond(function(method, url, data) {
console.log(method, url, data); console.log(method, url, data);
var states = ["UNINITIALIZED", "INITIALIZED", "INSTALLING", "SUCCESSFUL", "ERROR"]; var states = ["UNINITIALIZED", "INITIALIZED", "INSTALLING", "SUCCESSFUL", "ERROR"];
@ -443,6 +504,35 @@ compassAppDev.run(function($httpBackend, settings, $http) {
return [200, progressData, {}]; return [200, progressData, {}];
}); });
$httpBackend.whenGET(/\.*\/users\/logs$/).respond(function(method, url, data) {
var userLog = [{
"user_id": 1,
"action": "Created New User",
"timestamp": "2012-12-30 12:22:00"
}, {
"user_id": 1,
"action": "Modified Admin Priviledges",
"timestamp": "2013-03-25 09:10:00"
}, {
"user_id": 3,
"action": "Test Cluster",
"timestamp": "2014-06-19 23:32:00"
}, {
"user_id": 3,
"action": "Deleted User",
"timestamp": "2014-06-20 09:10:00"
}, {
"user_id": 2,
"action": "Deleted Cluster",
"timestamp": "2014-07-26 16:22:00"
}, {
"user_id": 2,
"action": "Created New Cluster",
"timestamp": "2014-08-22 14:22:00"
}];
return [200, userLog, {}];
});
$httpBackend.whenGET(/\.*\/clusters$/).respond(function(method, url, data) { $httpBackend.whenGET(/\.*\/clusters$/).respond(function(method, url, data) {
console.log(method, url); console.log(method, url);
var clusters = [{ var clusters = [{

View File

@ -9,13 +9,14 @@
Cluster Name Cluster Name
</div> </div>
</th> </th>
<th class="sortable" ng-class="{ <!-- With icons doesn't make sense to have sorting
class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('state', 'asc'), 'sort-asc': tableParams.isSortBy('state', 'asc'),
'sort-desc': tableParams.isSortBy('state', 'desc') 'sort-desc': tableParams.isSortBy('state', 'desc')
}" ng-click="tableParams.sorting({'state' : tableParams.isSortBy('state', 'asc') ? 'desc' : 'asc'})"> }" ng-click="tableParams.sorting({'state' : tableParams.isSortBy('state', 'asc') ? 'desc' : 'asc'})"
<div> -->
<th>
State State
</div>
</th> </th>
<th> <th>
Deployment Details Deployment Details
@ -92,12 +93,12 @@
{{cluster.updated_at}} {{cluster.updated_at}}
</td> </td>
<td> <td>
<button ng-click="export()" class="btn btn-xs btn-black-white btn-clte btn-export-hover border-radius-4">
<i class="glyphicon glyphicon-export bigger-130"></i>
</button>
<button ng-click="alert()" class="btn btn-xs btn-black-white btn-trash-hover border-radius-4"> <button ng-click="alert()" class="btn btn-xs btn-black-white btn-trash-hover border-radius-4">
<span class="glyphicon glyphicon-trash bigger-130"></span> <span class="glyphicon glyphicon-trash bigger-130"></span>
</button> </button>
<button ng-click="export()" class="btn btn-xs btn-black-white btn-clte border-radius-4">
<i class="glyphicon glyphicon-export bigger-130"></i>
</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -1,24 +1,22 @@
<div class="main-content"> <div class="main-content">
<div class="side-padding-40 top-padding-10"> <div class="side-padding-40 top-padding-10">
<div class="row"> <div class="row margin-bottom-15">
<div class="col-md-5 no-padding-left"> <div class="col-md-5 no-padding-left search-badge-adjustment">
<h1 class="blue-header"> <h1 class="blue-header margin-right-7">
<span style="font-family:Impact;">My Clusters</span> My Clusters
</h1>
<span class="badge badge-yellow">{{clusters.length}}</span> <div class="badge-adjustment">
<small> <span class="badge badge-yellow skinny-badge">{{clusters.length}}</span>
<span class="no-border-search"> <span class="no-border-search search-small">
<span class="input-icon"> <span class="input-icon">
<input class="no-border" type="text" placeholder="Search" ng-model="search.$"> <input class="no-border" type="text" placeholder="Search" ng-model="search.$">
<i class="ace-icon fa fa-search" style="top:-2px"></i> <i class="ace-icon fa fa-search"></i>
</span> </span>
</span> </span>
</small> </div>
</h1>
</div> </div>
<div class="col-md-7 no-padding-right"> <div class="col-md-7 no-padding-right">
<div class="pull-right"> <div class="pull-right">
<span ng-controller="createClusterCtrl" class="pull-right"> <span ng-controller="createClusterCtrl" class="pull-right">
<button ng-click="open()" class="btn btn-app btn-sm btn-primary radius-4 btn-create"> <button ng-click="open()" class="btn btn-app btn-sm btn-primary radius-4 btn-create">
<i class="glyphicon glyphicon-plus"></i> <i class="glyphicon glyphicon-plus"></i>
@ -26,7 +24,7 @@
</button> </button>
</span> </span>
</div> </div>
<div class="pull-right clusters-view" ng-init="view = 'grid'"> <div class="pull-right clusters-view top-padding-5" ng-init="view = 'grid'">
<span class="action grey" ng-click="view = 'grid'"> <span class="action grey" ng-click="view = 'grid'">
<span class="glyphicon glyphicon-th-large" ng-class="{'blue': view == 'grid'}"></span> <span class="glyphicon glyphicon-th-large" ng-class="{'blue': view == 'grid'}"></span>
</span> </span>
@ -36,7 +34,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="space-16"></div>
<div class="row" ng-controller="createClusterCtrl"> <div class="row" ng-controller="createClusterCtrl">
<div ng-if="clusters.length == 0" class="dashed-panel no-cluster-panel" ng-click="open()"> <div ng-if="clusters.length == 0" class="dashed-panel no-cluster-panel" ng-click="open()">

View File

@ -1 +1,2 @@
log <iframe id="kibanaFrame" width="1050" height="850" frameborder="0" src="http://192.168.255.7/index.html#/dashboard/file/logstash.json">Browser not compatible with this feature.</iframe>
<p class="iframe-fail">Log functionality not compatible on this device.</p

View File

@ -1,20 +1,20 @@
<ul class="nav nav-list"> <ul class="nav nav-list">
<li ng-class="{active:state.includes('cluster.overview')}"> <li ng-class="{active:state.includes('cluster.overview')}">
<a ui-sref="cluster.overview"> <a ui-sref="cluster.overview">
<i class="menu-icon fa fa-tachometer"></i> <i class="compass-menu-icon fa fa-tachometer"></i>
<span class="menu-text">Overview</span> <span class="menu-text">Overview</span>
</a> </a>
</li> </li>
<li ng-class="{'active open':state.includes('cluster.monitoring')}"> <li ng-class="{'active open':state.includes('cluster.monitoring')}">
<a href ng-click="isMonitoringNavOpen = !isMonitoringNavOpen"> <a href ng-click="isMonitoringNavOpen = !isMonitoringNavOpen">
<i class="menu-icon fa fa-bar-chart-o"></i> <i class="compass-menu-icon fa fa-bar-chart-o"></i>
<span class="menu-text">Monitoring</span> <span class="menu-text">Monitoring</span>
<b class="arrow fa fa-angle-down"></b> <b class="arrow fa fa-angle-down"></b>
</a> </a>
<ul class="submenu" ng-show="isMonitoringNavOpen" style="display: block"> <ul class="submenu" ng-show="isMonitoringNavOpen" style="display: block">
<li ng-repeat="nav in monitoringNav" ng-class="{active:state.includes('{{nav.name}}')}"> <li ng-repeat="nav in monitoringNav" ng-class="{active:state.includes('{{nav.name}}')}">
<a ui-sref="{{nav.name}}"> <a ui-sref="{{nav.name}}">
<i class="menu-icon fa fa-caret-right"></i> <i class="compass-menu-icon fa fa-caret-right"></i>
{{nav.display}} {{nav.display}}
</a> </a>
</li> </li>
@ -22,32 +22,32 @@
</li> </li>
<li ng-class="{'active open':state.includes('cluster.config')}"> <li ng-class="{'active open':state.includes('cluster.config')}">
<a href ng-click="isConfigNavOpen = !isConfigNavOpen"> <a href ng-click="isConfigNavOpen = !isConfigNavOpen">
<i class="menu-icon fa fa-cog"></i> <i class="compass-menu-icon fa fa-cog"></i>
<span class="menu-text">Configurations</span> <span class="menu-text">Configurations</span>
<b class="arrow fa fa-angle-down"></b> <b class="arrow fa fa-angle-down"></b>
</a> </a>
<ul class="submenu" ng-show="isConfigNavOpen" style="display: block"> <ul class="submenu" ng-show="isConfigNavOpen" style="display: block">
<li ng-class="{active:state.includes('cluster.config.security')}"> <li ng-class="{active:state.includes('cluster.config.security')}">
<a ui-sref="cluster.config.security"> <a ui-sref="cluster.config.security">
<i class="menu-icon fa fa-caret-right"></i> <i class="compass-menu-icon fa fa-caret-right"></i>
Security Security
</a> </a>
</li> </li>
<li ng-class="{active:state.includes('cluster.config.network')}"> <li ng-class="{active:state.includes('cluster.config.network')}">
<a ui-sref="cluster.config.network"> <a ui-sref="cluster.config.network">
<i class="menu-icon fa fa-caret-right"></i> <i class="compass-menu-icon fa fa-caret-right"></i>
Network Network
</a> </a>
</li> </li>
<li ng-class="{active:state.includes('cluster.config.partition')}"> <li ng-class="{active:state.includes('cluster.config.partition')}">
<a ui-sref="cluster.config.partition"> <a ui-sref="cluster.config.partition">
<i class="menu-icon fa fa-caret-right"></i> <i class="compass-menu-icon fa fa-caret-right"></i>
Partition Partition
</a> </a>
</li> </li>
<li ng-class="{active:state.includes('cluster.config.roles')}"> <li ng-class="{active:state.includes('cluster.config.roles')}">
<a ui-sref="cluster.config.roles"> <a ui-sref="cluster.config.roles">
<i class="menu-icon fa fa-caret-right"></i> <i class="compass-menu-icon fa fa-caret-right"></i>
Roles Roles
</a> </a>
</li> </li>
@ -55,7 +55,7 @@
</li> </li>
<li ng-class="{active:state.includes('cluster.log')}"> <li ng-class="{active:state.includes('cluster.log')}">
<a ui-sref="cluster.log"> <a ui-sref="cluster.log">
<i class="menu-icon fa fa-list-alt"></i> <i class="compass-menu-icon fa fa-list-alt"></i>
<span class="menu-text">Log</span> <span class="menu-text">Log</span>
</a> </a>
</li> </li>

View File

@ -8,10 +8,10 @@
'badge-success':clusterProgress.state==='SUCCESSFUL'}"> 'badge-success':clusterProgress.state==='SUCCESSFUL'}">
{{clusterProgress.state}} {{clusterProgress.state}}
</span> </span>
<!--<i class="ace-icon fa fa-angle-double-right"></i>&nbsp;
<a href="">OpenStack Dashboard Link</a>-->
</small> </small>
</h1> </h1>
<!--<i class="ace-icon fa fa-angle-double-right"></i>&nbsp;
<a href="">OpenStack Dashboard Link</a>-->
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
@ -162,7 +162,7 @@
</hostprogressbar> </hostprogressbar>
</td> </td>
<td class="align-right"> <td class="align-right">
<button class="btn btn-xs" ng-click="openDeleteHostModal($index)" ng-disabled="clusterProgress.state == 'INSTALLING'"> <button class="btn btn-trash-hover cluster-overview-delete border-radius-4 btn-xs" ng-click="openDeleteHostModal($index)" ng-disabled="clusterProgress.state == 'INSTALLING'">
<i class="ace-icon fa fa-trash-o bigger-120"></i> <i class="ace-icon fa fa-trash-o bigger-120"></i>
</button> </button>
</td> </td>

View File

@ -59,6 +59,7 @@ angular.module('compass.cluster', [
}) })
.state('cluster.log', { .state('cluster.log', {
url: '/log', url: '/log',
controller: "clusterLogCtrl",
templateUrl: 'src/app/cluster/cluster-log.tpl.html', templateUrl: 'src/app/cluster/cluster-log.tpl.html',
authenticate: true authenticate: true
}); });
@ -97,6 +98,10 @@ angular.module('compass.cluster', [
} }
}) })
.controller('clusterLogCtrl', function() {
})
.controller('clusterProgressCtrl', function($scope, dataService, $stateParams, $filter, ngTableParams, $timeout, $modal, clusterhostsData) { .controller('clusterProgressCtrl', function($scope, dataService, $stateParams, $filter, ngTableParams, $timeout, $modal, clusterhostsData) {
var clusterId = $stateParams.id; var clusterId = $stateParams.id;
var progressTimer; var progressTimer;
@ -187,7 +192,7 @@ angular.module('compass.cluster', [
$scope.open = function(size) { $scope.open = function(size) {
var modalInstance = $modal.open({ var modalInstance = $modal.open({
templateUrl: 'createClusterModal.html', templateUrl: 'createClusterModal.html',
controller: ModalInstanceCtrl, controller: ClusterModalCtrl,
size: size, size: size,
resolve: { resolve: {
allAdapters: function() { allAdapters: function() {
@ -198,7 +203,6 @@ angular.module('compass.cluster', [
} }
} }
}); });
modalInstance.result.then(function(cluster) { modalInstance.result.then(function(cluster) {
$scope.cluster = cluster; $scope.cluster = cluster;
var postClusterData = { var postClusterData = {
@ -266,7 +270,7 @@ angular.module('compass.cluster', [
}); });
}) })
var ModalInstanceCtrl = function($scope, $modalInstance, allAdapters, cluster) { var ClusterModalCtrl = function($scope, $modalInstance, allAdapters, cluster) {
$scope.allAdapters = allAdapters; $scope.allAdapters = allAdapters;
$scope.cluster = cluster; $scope.cluster = cluster;

View File

@ -13,23 +13,22 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-5 no-padding-left"> <div class="col-md-5 no-padding-left search-badge-adjustment">
<h1 class="blue-header"> <h1 class="blue-header margin-right-7">
Servers&nbsp; Servers
<span class="badge badge-yellow">{{allservers.length}}</span> </h1>
<div class="badge-adjustment">
<small> <span class="badge badge-yellow skinny-badge">{{allservers.length}}</span>
<span class="no-border-search"> <span class="no-border-search search-small">
<span class="input-icon"> <span class="input-icon">
<input class="no-border" type="text" placeholder="Search" ng-model="search.$"> <input class="no-border" type="text" placeholder="Search" ng-model="search.$">
<i class="ace-icon fa fa-search" style="top:-2px"></i> <i class="ace-icon fa fa-search"></i>
</span> </span>
</span> </span>
</small> </div>
</h1>
</div> </div>
<div class="col-md-7 no-padding-right"> <div class="col-md-7 no-padding-right">
<div class="pull-right top-margin-26 center-align"> <div class="pull-right top-margin-17 center-align">
<!-- Column Show / Hide button --> <!-- Column Show / Hide button -->
<div class="btn-group" dropdown> <div class="btn-group" dropdown>
<button type="button" class="btn btn-default dropdown-toggle"> <button type="button" class="btn btn-default dropdown-toggle">
@ -47,7 +46,7 @@
</li> </li>
</ul> </ul>
</div> </div>
<button class="btn btn-success" ng-click="reloadServers()"> <button class="btn btn-success border-radius-4 reload-servers-border" ng-click="reloadServers()">
<i class="ace-icon fa fa-refresh"></i> <i class="ace-icon fa fa-refresh"></i>
</button> </button>
<!--div class="btn-group" dropdown> <!--div class="btn-group" dropdown>
@ -66,12 +65,11 @@
</li> </li>
</ul> </ul>
</div--> </div-->
<button class="btn btn-info" ng-click="findNewServersPanel.isCollapsed = !findNewServersPanel.isCollapsed" ng-init="findNewServersPanel.isCollapsed = true;"> <button class="btn btn-info border-radius-4" ng-click="findNewServersPanel.isCollapsed = !findNewServersPanel.isCollapsed" ng-init="findNewServersPanel.isCollapsed = true;">
Discover Servers&nbsp;&nbsp; Discover Servers&nbsp;&nbsp;
<i class="ace-icon fa fa-plus" ng-class="{'fa-minus': !findNewServersPanel.isCollapsed}"></i> <i class="ace-icon fa fa-plus" ng-class="{'fa-minus': !findNewServersPanel.isCollapsed}"></i>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -102,6 +102,17 @@ angular.module('compass.services', [])
return $http.post(settings.apiUrlBase + '/clusters', angular.toJson(cluster)); return $http.post(settings.apiUrlBase + '/clusters', angular.toJson(cluster));
}; };
this.createUser = function(newUser) {
return $http.post(settings.apiUrlBase + '/users', angular.toJson(newUser));
};
this.getUserSetting = function() {
return $http.get(settings.apiUrlBase + '/users');
};
this.getUserLog = function() {
return $http.get(settings.apiUrlBase + '/users/logs');
}
this.getClusters = function() { this.getClusters = function() {
return $http.get(settings.apiUrlBase + '/clusters'); return $http.get(settings.apiUrlBase + '/clusters');
}; };

View File

@ -4,7 +4,7 @@
<a style="height: 50px; width: 190px; padding-left:5px;padding-top:8px;" class="navbar-brand" ui-sref="clusterList"> <a style="height: 50px; width: 190px; padding-left:5px;padding-top:8px;" class="navbar-brand" ui-sref="clusterList">
<div class="pull-left"> <div class="pull-left">
<img src="assets/img/logo-pure.png" height="45px"> <img src="assets/img/logo-pure.png" height="45px">
<img src="assets/img/logo-text.png" height="28px" width="115px"> <img src="assets/img/logo-text.png" height="28px" width="117px">
</div> </div>
</a> </a>
<!-- /.brand --> <!-- /.brand -->
@ -109,13 +109,13 @@
<ul class="user-menu pull-right dropdown-menu dropdown-menu-right dropdown-yellow dropdown-caret dropdown-close"> <ul class="user-menu pull-right dropdown-menu dropdown-menu-right dropdown-yellow dropdown-caret dropdown-close">
<li> <li>
<a href="#"> <a ui-sref="userSetting">
<i class="ace-icon fa fa-cog blue"></i> <i class="ace-icon fa fa-cog blue"></i>
Settings Settings
</a> </a>
</li> </li>
<li> <li>
<a href="#"> <a ui-sref="userProfile">
<i class="ace-icon fa fa-user blue"></i> <i class="ace-icon fa fa-user blue"></i>
Profile Profile
</a> </a>

View File

@ -0,0 +1 @@
User profile goes here

View File

@ -0,0 +1,64 @@
<script type="text/ng-template" id="userSettingModal.html">
<div class="modal-header">
<h3 class="modal-title">Create New User</h3>
</div>
<div class="modal-body padding-bottom-0">
<form name="newUserForm" class="form-horizontal">
<div class="form-group new-user-form" ng-class="{'has-error': newUserForm.useremail.$invalid && newUserForm.useremail.$dirty}">
<input type="email" name="useremail" placeholder="Email" class="input-width-200" ng-model="newUser.email" required>
<i class="fa fa-envelope opacity-50 margin-left-neg30"></i>
<div class="margin-left-15 inline">
<span class="red">*</span>
<span class="red" ng-show="newUserForm.useremail.$error.email"> Invalid Email</span>
</div>
</div>
<div class="form-group new-user-form" ng-class="{'has-error': newUserForm.userName.$invalid && newUserForm.userName.$dirty}">
<input type="text" ng-model="newUser.username" name="userName" placeholder="Username" class="input-width-200" required>
<i class="fa fa-user opacity-50 margin-left-neg30 padding-left-1"></i>
<div class="margin-left-17 inline">
<span class="red">*</span>
</div>
</div>
<div class="form-group new-user-form">
<input type="text" ng-model="newUser.first_name" name="userfirstname" placeholder="First Name" class="input-width-200">
</div>
<div class="form-group new-user-form">
<input type="text" ng-model="newUser.last_name" name="userlastname" placeholder="Last Name" class="input-width-200">
</div>
<div class="form-group new-user-form" ng-class="{'has-error': newUserForm.userPassword.$invalid && newUserForm.userPassword.$dirty}">
<input type="password" ng-model="newUser.password" name="userPassword" placeholder="Password" class="input-width-200" required>
<i class="fa fa-lock opacity-50 margin-left-neg30 padding-left-2"></i>
<div class="margin-left-18 inline">
<span class="red">*</span>
</div>
</div>
<div class="form-group new-user-form" ng-class="{'has-error': newUserForm.confirmPassword.$invalid && newUserForm.confirmPassword.$dirty}">
<input type="password" ng-model="newUser.confirmPassword" data-match="newUser.password" name="confirmPassword" placeholder="Repeat Password" class="input-width-200" required>
<i class="fa fa-retweet opacity-50 margin-left-neg30"></i>
<div class="margin-left-14 inline">
<span class="red">*</span>
<span data-ng-show="newUserForm.confirmPassword.$error.match"> Passwords do not match</span>
</div>
</div>
<div class="form-group new-user-form">
<label>
<input class="ace" type="checkbox" name="active" ng-model="newUser.active"><span class="lbl"> active</span></div>
</label>
<div class="form-group new-user-form">
<label>
<input class="ace" type="checkbox" name="admin" ng-model="newUser.is_admin"><span class="lbl"> admin</span>
</label>
</div>
<p class="user-form-note">Note: Items marked with <span class="red">*</span> are required fields.</p>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
<button class="btn btn-primary" ng-click="ok()" ng-disabled="newUserForm.$invalid">Create</button>
</div>
</script>

View File

@ -0,0 +1,182 @@
<div class="side-padding-40 top-padding-10">
<tabset>
<tab>
<tab-heading>
<i class="fa fa-user"></i> User
</tab-heading>
<div class="row padding-left-15">
<h1 class="blue-header">Settings</h1>
<div class="badge-adjustment">
<span class="no-border-search search-small">
<span class="input-icon">
<input class="no-border" type="text" placeholder="Search" ng-model="searchUser.$">
<i class="ace-icon fa fa-search"></i>
</span>
</span>
</div>
</div>
<div ng-include="'src/app/user/user-setting-create.html'"></div>
<div class="cluster-margin-bottom-20">
<a href>
<button ng-click="open()" class="btn btn-info"><i class="glyphicon glyphicon-plus"></i>Add New User</button>
</a>
</div>
<div class="table-responsive">
<table ng-table="tableParams" class="ng-table table table-striped table-hover nowrap">
<thead>
<tr>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('email', 'asc'),
'sort-desc': tableParams.isSortBy('email', 'desc')}" ng-click="tableParams.sorting({'email' : tableParams.isSortBy('email', 'asc') ? 'desc' : 'asc'})">
<div>
E-mail
</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('username', 'asc'),
'sort-desc': tableParams.isSortBy('uesrname', 'desc')
}" ng-click="tableParams.sorting({'username' : tableParams.isSortBy('username', 'asc') ? 'desc' : 'asc'})">
<div>
Username
</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('first_name', 'asc'),
'sort-desc': tableParams.isSortBy('first_name', 'desc')
}" ng-click="tableParams.sorting({'first_name' : tableParams.isSortBy('first_name', 'asc') ? 'desc' : 'asc'})">
<div>
First Name
</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('last_name', 'asc'),
'sort-desc': tableParams.isSortBy('last_name', 'desc')
}" ng-click="tableParams.sorting({'last_name' : tableParams.isSortBy('last_name', 'asc') ? 'desc' : 'asc'})">
<div>
Last Name
</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('active', 'asc'),
'sort-desc': tableParams.isSortBy('active', 'desc')
}" ng-click="tableParams.sorting({'active' : tableParams.isSortBy('active', 'asc') ? 'desc' : 'asc'})">
<div>Active</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('is_admin', 'asc'),
'sort-desc': tableParams.isSortBy('is_admin', 'desc')
}" ng-click="tableParams.sorting({'is_admin' : tableParams.isSortBy('is_admin', 'asc') ? 'desc' : 'asc'})">
<div>Admin</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': tableParams.isSortBy('last_login', 'asc'),
'sort-desc': tableParams.isSortBy('last_login', 'desc')
}" ng-click="tableParams.sorting({'last_login' : tableParams.isSortBy('last_login', 'asc') ? 'desc' : 'asc'})">
<div>Last Login</div>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="userSetting in $data | filter:searchUser">
<td sortable="'email'">
{{userSetting.email}}
</td>
<td sortable="'username'">
{{userSetting.username}}
</td>
<td sortable="'first_name'">
{{userSetting.first_name}}
</td>
<td sortable="'last_name'">
{{userSetting.last_name}}
</td>
<td sortable="'active'">
<span ng-if="userSetting.active == true">
<i class="ace-icon fa fa-check green"></i>
</span>
<span ng-if="userSetting.active == false">
</span>
</td>
<td sortable="'is_admin'">
<span ng-if="userSetting.is_admin == true">
<i class="ace-icon fa fa-check green"></i>
</span>
<span ng-if="userSetting.is_admin == false">
</span>
</td>
<td sortable="'last_login'">
{{userSetting.last_login}}
</td>
<td>
<button ng-click="edit()" class="btn btn-xs btn-black-white btn-clte btn-export-hover border-radius-4">
<i class="glyphicon glyphicon-export bigger-130"></i>
</button>
<button ng-click="delete()" class="btn btn-xs btn-black-white btn-trash-hover border-radius-4">
<i class="glyphicon glyphicon-trash bigger-130"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</tab>
<tab heading="Activity">
<div class="row padding-left-15">
<h1 class="blue-header">Activity</h1>
<div class="badge-adjustment">
<span class="no-border-search search-small">
<span class="input-icon">
<input class="no-border" type="text" placeholder="Search" ng-model="searchActivity.$">
<i class="ace-icon fa fa-search"></i>
</span>
</span>
</div>
</div>
<div class="input-prepend input-group date-picker-margin">
<span class="add-on input-group-addon">
<i class="fa fa-calendar"></i>
</span>
<input type="daterange" class="form-control date-picker-width" ng-model="dateRange" enableTimePicker="true" format="M/DD/YYYY h:mm A">
</div>
<div class="table-responsive">
<table ng-table="userParams" class="ng-table table table-striped table-hover nowrap">
<thead>
<tr>
<th class="sortable" ng-class="{
'sort-asc': userParams.isSortBy('user_id', 'asc'),
'sort-desc': userParams.isSortBy('user_id', 'desc')
}" ng-click="userParams.sorting({'user_id' : userParams.isSortBy('user_id', 'asc') ? 'desc' : 'asc'})">
<div>User ID</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': userParams.isSortBy('action', 'asc'),
'sort-desc': userParams.isSortBy('action', 'desc')
}" ng-click="userParams.sorting({'action' : userParams.isSortBy('action', 'asc') ? 'desc' : 'asc'})">
<div>Action</div>
</th>
<th class="sortable" ng-class="{
'sort-asc': userParams.isSortBy('timestamp', 'asc'),
'sort-desc': userParams.isSortBy('timestamp', 'desc')
}" ng-click="userParams.sorting({'timestamp' : userParams.isSortBy('timestamp', 'asc') ? 'desc' : 'asc'})">
<div>Timestamp</div>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="userLog in $data | filter:searchActivity | timeStampFilter:dateRange">
<td sortable="'user_id'">
{{userLog.user_id}}
</td>
<td sortable="'action'">
{{userLog.action}}
</td>
<td sortable="'timestamp'">
{{userLog.timestamp | date:'M/d/yy h:mm a'}}
</td>
</tr>
</tbody>
</table>
</div>
</tab>
</div>

View File

@ -0,0 +1,17 @@
var app = angular.module('compass.userProfile', [
'ui.router',
'ui.bootstrap'
])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('userProfile', {
url: '/userProfile',
//controller: 'userProfileCtrl',
templateUrl: 'src/app/user/user-profile.html',
authenticate: true,
//resolve: {}
});
})
//combine usersetting and userprofile into a single file?

View File

@ -0,0 +1,145 @@
var app = angular.module('compass.userSetting', [
'ui.router',
'ui.bootstrap',
'ngTable',
'ngBootstrap'
])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('userSetting', {
url: '/userSetting',
controller: 'userSettingCtrl',
templateUrl: 'src/app/user/user-setting.html',
authenticate: true,
resolve: {
userSettingData: function($q, dataService) {
var deferred = $q.defer();
dataService.getUserSetting().success(function(data) {
deferred.resolve(data);
});
return deferred.promise;
},
userLogData: function($q, dataService) {
var deferred = $q.defer();
dataService.getUserLog().success(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
}
});
})
.controller('userSettingCtrl', function($scope, $state, ngTableParams, $filter, dataService, userSettingData, $modal) {
$scope.userSetting = userSettingData;
var data = userSettingData;
$scope.tableParams = new ngTableParams({
page: 1,
count: 10,
}, {
total: data.length,
getData: function($defer, params) {
var orderedData = params.sorting() ?
$filter('orderBy')(data, params.orderBy()) :
data;
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
$scope.edit = function() {
alert("Edit User?")
};
$scope.delete = function() {
alert("Delete User?")
};
$scope.newUser = {};
$scope.open = function(size) {
var modalInstance = $modal.open({
templateUrl: 'userSettingModal.html',
controller: UserModalCtrl,
resolve: {
newUser: function() {
return $scope.newUser;
}
}
});
modalInstance.result.then(function(newUser) {
$scope.newUser = newUser;
dataService.createUser(newUser).success(function(data, status) {
$scope.userSetting.push(newUser);
$scope.tableParams.reload();
});
$scope.newUser = {};
}, function() {
// modal cancelled
});
};
dataService.getUserLog().success(function(data) {
$scope.userParams = new ngTableParams({
page: 1,
count: 10,
}, {
total: data.length,
getData: function($defer, params) {
var orderedData = params.sorting() ?
$filter('orderBy')(data, params.orderBy()) :
data;
$defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
});
})
.directive('match', function() {
return {
require: 'ngModel',
restrict: 'A',
scope: {
match: '='
},
link: function(scope, elem, attrs, ctrl) {
scope.$watch(function() {
return (ctrl.$pristine && angular.isUndefined(ctrl.$modelValue)) || scope.match === ctrl.$modelValue;
}, function(currentValue) {
ctrl.$setValidity('match', currentValue);
});
}
};
});
app.filter('timeStampFilter', function () {
return function(items, dateRange) {
if (items !== undefined) {
var filtered = [];
var startDate = dateRange.startDate;
var endDate = dateRange.endDate;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var standardTime = moment(item.timestamp);
if (moment(standardTime).isAfter(startDate) && moment(standardTime).isBefore(endDate)) {
filtered.push(item);
}
}
return filtered;
}
};
})
var UserModalCtrl = function($scope, $modalInstance, newUser) {
$scope.newUser = newUser;
$scope.ok = function() {
$scope.result = 'ok';
$modalInstance.close($scope.newUser);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
$scope.result = 'cancel';
};
};

View File

@ -6,7 +6,8 @@ angular.module('compass.wizard', [
'compass.findservers', 'compass.findservers',
'ngDragDrop', 'ngDragDrop',
'ngTouch', 'ngTouch',
'angularSpinner' 'angularSpinner',
'ngAnimate'
]) ])
.config(function config($stateProvider) { .config(function config($stateProvider) {
@ -122,13 +123,10 @@ angular.module('compass.wizard', [
'id': $scope.cluster.id 'id': $scope.cluster.id
}); });
} }
$scope.stepControl(); $scope.stepControl();
if ($scope.currentStep > $scope.maxStep) { if ($scope.currentStep > $scope.maxStep) {
$scope.maxStep = $scope.currentStep; $scope.maxStep = $scope.currentStep;
} }
} else if (newCommitState.state == "error") { } else if (newCommitState.state == "error") {
console.warn("### catch error in wizardCtrl ###", newCommitState, oldCommitState); console.warn("### catch error in wizardCtrl ###", newCommitState, oldCommitState);
$scope.openErrMessageModal(newCommitState.message); $scope.openErrMessageModal(newCommitState.message);

View File

@ -1,6 +1,6 @@
<div class="main-content"> <div class="main-content">
<!--div class="page-content" ng-swipe-right="stepForward()" ng-swipe-left="stepBackward()"--> <!--div class="page-content"-->
<div class="page-content"> <div class="page-content" ng-swipe-disable-mouse ng-swipe-right="stepForward()" ng-swipe-left="stepBackward()" >
<div class="page-header"> <div class="page-header">
<h1>Create Cluster Wizard</h1> <h1>Create Cluster Wizard</h1>
<span us-spinner="{radius:30, length: 20, lines:13, width:10}" spinner-key="spinner-1"></span> <span us-spinner="{radius:30, length: 20, lines:13, width:10}" spinner-key="spinner-1"></span>
@ -19,7 +19,6 @@
<div class="widget-header widget-header-flat widget-header-small"> <div class="widget-header widget-header-flat widget-header-small">
<h5 class="widget-title"> <h5 class="widget-title">
{{steps[currentStep-1].title}} {{steps[currentStep-1].title}}
<small ng-if="steps[currentStep-1].description"><i class="ace-icon fa fa-angle-double-right"></i> {{steps[currentStep-1].description}}</small> <small ng-if="steps[currentStep-1].description"><i class="ace-icon fa fa-angle-double-right"></i> {{steps[currentStep-1].description}}</small>
</h5> </h5>
</div> </div>

View File

@ -0,0 +1,284 @@
/*!
* Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
*
* Copyright 2013 Dan Grossman ( http://www.dangrossman.info )
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Built for http://www.improvely.com
*/
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
float: left;
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
float: right;
margin: 4px;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
}
.daterangepicker .ranges .range_inputs>div {
float: left;
}
.daterangepicker .ranges .range_inputs>div:nth-child(2) {
padding-left: 11px;
}
.daterangepicker .calendar {
display: none;
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
min-width: 32px;
}
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
display: block;
font-size: 11px;
font-weight: normal;
height: 20px;
line-height: 20px;
margin-bottom: 2px;
text-shadow: #fff 1px 1px 0px;
text-transform: uppercase;
width: 74px;
}
.daterangepicker .ranges input {
font-size: 11px;
}
.daterangepicker .ranges .input-mini {
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
display: block;
font-size: 11px;
height: 30px;
line-height: 30px;
vertical-align: middle;
margin: 0 0 10px 0;
padding: 0 6px;
width: 74px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}
.daterangepicker .calendar-date {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 8px auto 0 auto;
line-height: 30px;
}
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
}
.daterangepicker td.off {
color: #999;
}
.daterangepicker td.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.daterangepicker td.available + td.start-date {
-webkit-border-radius: 4px 0 0 4px;
-moz-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.daterangepicker td.in-range + td.end-date{
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date{
-webkit-border-radius: 4px !important;
-moz-border-radius: 4px !important;
border-radius: 4px !important;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: #3071a9;
color: #fff;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
width: 50px;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
/**
* @license ng-bs-daterangepicker v0.0.1
* (c) 2013 Luis Farzati http://github.com/luisfarzati/ng-bs-daterangepicker
* License: MIT
*/
(function (angular) {
'use strict';
angular.module('ngBootstrap', []).directive('input', function ($compile, $parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function ($scope, $element, $attributes, ngModel) {
if ($attributes.type !== 'daterange' || ngModel === null ) return;
var options = {};
options.format = $attributes.format || 'YYYY-MM-DD';
options.separator = $attributes.separator || ' - ';
options.minDate = $attributes.minDate && moment($attributes.minDate);
options.maxDate = $attributes.maxDate && moment($attributes.maxDate);
options.dateLimit = $attributes.limit && moment.duration.apply(this, $attributes.limit.split(' ').map(function (elem, index) { return index === 0 && parseInt(elem, 10) || elem; }) );
options.ranges = $attributes.ranges && $parse($attributes.ranges)($scope);
options.locale = $attributes.locale && $parse($attributes.locale)($scope);
options.opens = $attributes.opens && $parse($attributes.opens)($scope);
options.timePicker = $attributes.enabletimepicker && $parse($attributes.enabletimepicker)($scope);
function format(date) {
return date.format(options.format);
}
function formatted(dates) {
return [format(dates.startDate), format(dates.endDate)].join(options.separator);
}
ngModel.$formatters.unshift(function (modelValue) {
if (!modelValue) return '';
return modelValue;
});
ngModel.$parsers.unshift(function (viewValue) {
return viewValue;
});
ngModel.$render = function () {
if (!ngModel.$viewValue || !ngModel.$viewValue.startDate) return;
$element.val(formatted(ngModel.$viewValue));
};
$scope.$watch($attributes.ngModel, function (modelValue) {
if (!modelValue || (!modelValue.startDate)) {
ngModel.$setViewValue({ startDate: moment().subtract(3, 'days'), endDate: moment().startOf('day') });
return;
}
$element.data('daterangepicker').startDate = modelValue.startDate;
$element.data('daterangepicker').endDate = modelValue.endDate;
$element.data('daterangepicker').updateView();
$element.data('daterangepicker').updateCalendars();
$element.data('daterangepicker').updateInputText();
});
$element.daterangepicker(options, function(start, end) {
$scope.$apply(function () {
ngModel.$setViewValue({ startDate: start, endDate: end });
ngModel.$render();
});
});
}
};
});
})(angular);

View File

@ -1,5 +1,5 @@
/** /**
* @license AngularJS v1.2.16 * @license AngularJS v1.3.0-beta.2-build.local+sha.2935dad
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
@ -105,7 +105,11 @@ ngTouch.factory('$swipe', [function() {
// Whether a swipe is active. // Whether a swipe is active.
var active = false; var active = false;
element.on('touchstart mousedown', function(event) { var optionalMouseEventStart = '';
if(!eventHandlers.disableMouseEvents){
optionalMouseEventStart += ' mousedown';
}
element.on('touchstart' + optionalMouseEventStart, function(event) {
startCoords = getCoordinates(event); startCoords = getCoordinates(event);
active = true; active = true;
totalX = 0; totalX = 0;
@ -119,7 +123,11 @@ ngTouch.factory('$swipe', [function() {
eventHandlers['cancel'] && eventHandlers['cancel'](event); eventHandlers['cancel'] && eventHandlers['cancel'](event);
}); });
element.on('touchmove mousemove', function(event) { var optionalMouseEventMove = '';
if(!eventHandlers.disableMouseEvents){
optionalMouseEventMove += ' mousemove';
}
element.on('touchmove' + optionalMouseEventMove, function(event) {
if (!active) return; if (!active) return;
// Android will send a touchcancel if it thinks we're starting to scroll. // Android will send a touchcancel if it thinks we're starting to scroll.
@ -153,7 +161,11 @@ ngTouch.factory('$swipe', [function() {
} }
}); });
element.on('touchend mouseup', function(event) { var optionalMouseEventEnd = '';
if(!eventHandlers.disableMouseEvents){
optionalMouseEventEnd += ' mouseup';
}
element.on('touchend' + optionalMouseEventEnd, function(event) {
if (!active) return; if (!active) return;
active = false; active = false;
eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event); eventHandlers['end'] && eventHandlers['end'](getCoordinates(event), event);
@ -215,7 +227,6 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var ACTIVE_CLASS_NAME = 'ng-click-active'; var ACTIVE_CLASS_NAME = 'ng-click-active';
var lastPreventedTime; var lastPreventedTime;
var touchCoordinates; var touchCoordinates;
var lastLabelClickCoordinates;
// TAP EVENTS AND GHOST CLICKS // TAP EVENTS AND GHOST CLICKS
@ -287,23 +298,10 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var y = touches[0].clientY; var y = touches[0].clientY;
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label // Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
// and on the input element). Depending on the exact browser, this second click we don't want // and on the input element). Depending on the exact browser, this second click we don't want
// to bust has either (0,0), negative coordinates, or coordinates equal to triggering label // to bust has either (0,0) or negative coordinates.
// click event
if (x < 1 && y < 1) { if (x < 1 && y < 1) {
return; // offscreen return; // offscreen
} }
if (lastLabelClickCoordinates &&
lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
return; // input click triggered by label click
}
// reset label click coordinates on first subsequent click
if (lastLabelClickCoordinates) {
lastLabelClickCoordinates = null;
}
// remember label click coordinates to prevent click busting of trigger click event on input
if (event.target.tagName.toLowerCase() === 'label') {
lastLabelClickCoordinates = [x, y];
}
// Look for an allowable region containing this click. // Look for an allowable region containing this click.
// If we find one, that means it was created by touchstart and not removed by // If we find one, that means it was created by touchstart and not removed by
@ -518,8 +516,8 @@ function makeSwipeDirective(directiveName, direction, eventName) {
var MAX_VERTICAL_DISTANCE = 75; var MAX_VERTICAL_DISTANCE = 75;
// Vertical distance should not be more than a fraction of the horizontal distance. // Vertical distance should not be more than a fraction of the horizontal distance.
var MAX_VERTICAL_RATIO = 0.3; var MAX_VERTICAL_RATIO = 0.3;
// At least a 30px lateral motion is necessary for a swipe. // At least a 160px lateral motion is necessary for a swipe.
var MIN_HORIZONTAL_DISTANCE = 30; var MIN_HORIZONTAL_DISTANCE = 160;
return function(scope, element, attr) { return function(scope, element, attr) {
var swipeHandler = $parse(attr[directiveName]); var swipeHandler = $parse(attr[directiveName]);
@ -546,6 +544,7 @@ function makeSwipeDirective(directiveName, direction, eventName) {
} }
$swipe.bind(element, { $swipe.bind(element, {
'disableMouseEvents': angular.isDefined(attr['ngSwipeDisableMouse']),
'start': function(coords, event) { 'start': function(coords, event) {
startCoords = coords; startCoords = coords;
valid = true; valid = true;