- Switch font to Source Sans Pro, like CoreUpdate

- Add support for the new cor-tabs
- Add support for title-based layouts
- Switch super user layout to the new tabs UI in prep for adding setup support
This commit is contained in:
Joseph Schorr 2014-12-22 15:13:23 -05:00
parent 914df9af1a
commit 1f9f4ef26b
14 changed files with 345 additions and 170 deletions

View file

@ -18,9 +18,9 @@ EXTERNAL_JS = [
]
EXTERNAL_CSS = [
'netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.css',
'netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css',
'netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css',
'fonts.googleapis.com/css?family=Droid+Sans:400,700',
'fonts.googleapis.com/css?family=Source+Sans+Pro:400,700',
]
EXTERNAL_FONTS = [

View file

@ -1,5 +1,5 @@
* {
font-family: 'Droid Sans', sans-serif;
font-family: 'Source Sans Pro', sans-serif;
margin: 0;
}
@ -116,6 +116,88 @@
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
}
.co-fx-text-shadow {
text-shadow: rgba(0, 0, 0, 1) 1px 1px 2px;
}
.co-nav-title {
height: 70px;
margin-top: -22px;
}
.co-nav-title .co-nav-title-content {
color: white;
text-align: center;
}
.co-tab-container {
padding: 0px;
}
.co-tabs {
margin: 0px;
padding: 0px;
width: 82px;
background-color: #e8f1f6;
border-right: 1px solid #DDE7ED;
display: table-cell;
float: none;
vertical-align: top;
}
.co-tab-content {
width: 100%;
display: table-cell;
float: none;
padding: 10px;
}
.co-tabs li {
list-style: none;
display: block;
border-bottom: 1px solid #DDE7ED;
}
.co-tabs li.active {
background-color: white;
border-right: 1px solid white;
margin-right: -1px;
}
.co-tabs li a {
display: block;
width: 82px;
height: 82px;
line-height: 82px;
text-align: center;
font-size: 36px;
color: gray;
}
.co-tabs li.active a {
color: black;
}
.co-main-content-panel {
margin-bottom: 20px;
background-color: #fff;
border: 1px solid transparent;
padding: 10px;
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
-ms-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
-o-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.4);
}
.co-tab-panel {
padding: 0px;
}
.main-panel {
margin-bottom: 20px;
background-color: #fff;

View file

@ -0,0 +1 @@
<div class="co-tab-content tab-content col-md-11" ng-transclude></div>

View file

@ -0,0 +1,3 @@
<div class="co-main-content-panel co-tab-panel co-fx-box-shadow-heavy">
<div class="container co-tab-container" ng-transclude></div>
</div>

View file

@ -0,0 +1,11 @@
<li ng-class="tabActive == 'true' ? 'active' : ''">
<a href="javascript:void(0)" data-title="{{ tabTitle }}"
data-toggle="tab"
data-target="{{ tabTarget }}"
data-placement="right"
data-container="body"
ng-click="tabInit()"
bs-tooltip>
<span ng-transclude/>
</a>
</li>

View file

@ -0,0 +1 @@
<ul class="co-tabs col-md-1" ng-transclude></ul>

View file

@ -0,0 +1,3 @@
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-12">
<h2 class="co-nav-title-content co-fx-text-shadow" ng-transclude></h2>
</div>

View file

@ -0,0 +1 @@
<div class="col-lg-3 col-md-3 col-sm-3 col-xs-6" ng-transclude></div>

View file

@ -0,0 +1,2 @@
<div class="co-nav-title" ng-transclude></div>

View file

@ -126,7 +126,7 @@ function getMarkedDown(string) {
quayDependencies = ['ngRoute', 'chieffancypants.loadingBar', 'angular-tour', 'restangular', 'angularMoment',
'mgcrea.ngStrap', 'ngCookies', 'ngSanitize', 'angular-md5', 'pasvaz.bindonce', 'ansiToHtml',
'ngAnimate'];
'ngAnimate', 'core-ui'];
if (window.__config && window.__config.MIXPANEL_KEY) {
quayDependencies.push('angulartics');
@ -2226,7 +2226,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
when('/user/', {title: 'Account Settings', description:'Account settings for ' + title, templateUrl: '/static/partials/user-admin.html',
reloadOnSearch: false, controller: UserAdminCtrl}).
when('/superuser/', {title: 'Superuser Admin Panel', description:'Admin panel for ' + title, templateUrl: '/static/partials/super-user.html',
reloadOnSearch: false, controller: SuperUserAdminCtrl}).
reloadOnSearch: false, controller: SuperUserAdminCtrl, newLayout: true}).
when('/guide/', {title: 'Guide', description:'Guide to using private docker repositories on ' + title,
templateUrl: '/static/partials/guide.html',
controller: GuideCtrl}).
@ -6690,6 +6690,7 @@ quayApp.directive('ngBlur', function() {
};
});
quayApp.directive("filePresent", [function () {
return {
restrict: 'A',
@ -6763,7 +6764,6 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
var changeTab = function(activeTab, opt_timeout) {
var checkCount = 0;
$timeout(function() {
if (checkCount > 5) { return; }
checkCount++;
@ -6827,6 +6827,8 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
$rootScope.pageClass = current.$$route.pageClass;
}
$rootScope.newLayout = !!current.$$route.newLayout;
if (current.$$route.description) {
$rootScope.description = current.$$route.description;
} else {
@ -6842,26 +6844,28 @@ quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanServi
// Setup deep linking of tabs. This will change the search field of the URL whenever a tab
// is changed in the UI.
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var tabName = e.target.getAttribute('data-target').substr(1);
$rootScope.$apply(function() {
var isDefaultTab = $('a[data-toggle="tab"]')[0] == e.target;
var newSearch = $.extend($location.search(), {});
if (isDefaultTab) {
delete newSearch['tab'];
} else {
newSearch['tab'] = tabName;
}
$timeout(function() {
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var tabName = e.target.getAttribute('data-target').substr(1);
$rootScope.$apply(function() {
var isDefaultTab = $('a[data-toggle="tab"]')[0] == e.target;
var newSearch = $.extend($location.search(), {});
if (isDefaultTab) {
delete newSearch['tab'];
} else {
newSearch['tab'] = tabName;
}
$location.search(newSearch);
$location.search(newSearch);
});
e.preventDefault();
});
e.preventDefault();
});
if (activeTab) {
changeTab(activeTab);
}
if (activeTab) {
changeTab(activeTab);
}
}, 100); // 100ms to make sure angular has rendered.
});
var initallyChecked = false;

View file

@ -2831,7 +2831,7 @@ function SuperUserAdminCtrl($scope, ApiService, Features, UserService) {
}, ApiService.errorDisplay('Cannot load system usage. Please contact support.'))
}
$scope.loadLogs = function() {
$scope.loadUsageLogs = function() {
$scope.logsCounter++;
};

104
static/js/core-ui.js Normal file
View file

@ -0,0 +1,104 @@
angular.module("core-ui", [])
.directive('corTitle', function() {
var directiveDefinitionObject = {
priority: 1,
templateUrl: '/static/directives/cor-title.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTitleContent', function() {
var directiveDefinitionObject = {
priority: 1,
templateUrl: '/static/directives/cor-title-content.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTitleLink', function() {
var directiveDefinitionObject = {
priority: 1,
templateUrl: '/static/directives/cor-title-link.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTabPanel', function() {
var directiveDefinitionObject = {
priority: 1,
templateUrl: '/static/directives/cor-tab-panel.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTabContent', function() {
var directiveDefinitionObject = {
priority: 2,
templateUrl: '/static/directives/cor-tab-content.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTabs', function() {
var directiveDefinitionObject = {
priority: 3,
templateUrl: '/static/directives/cor-tabs.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
})
.directive('corTab', function() {
var directiveDefinitionObject = {
priority: 4,
templateUrl: '/static/directives/cor-tab.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {
'tabActive': '@tabActive',
'tabTitle': '@tabTitle',
'tabTarget': '@tabTarget',
'tabInit': '&tabInit'
},
controller: function($rootScope, $scope, $element) {
}
};
return directiveDefinitionObject;
});

View file

@ -1,157 +1,120 @@
<div class="container" quay-show="Features.SUPER_USERS && showInterface">
<div class="alert alert-info">
This panel provides administrator access to <strong>super users of this installation of the registry</strong>. Super users can be managed in the configuration for this installation.
<div class="page-content" quay-show="Features.SUPER_USERS && showInterface">
<div class="cor-title">
<span class="cor-title-link"></span>
<span class="cor-title-content">Enterprise Registry Setup</span>
</div>
<div class="row">
<!-- Side tabs -->
<div class="col-md-2">
<ul class="nav nav-pills nav-stacked">
<li class="active">
<a href="javascript:void(0)" data-toggle="tab" data-target="#users" ng-click="loadUsers()">Users</a>
</li>
<li>
<a href="javascript:void(0)" data-toggle="tab" data-target="#create-user">Create User</a>
</li>
<li>
<a href="javascript:void(0)" data-toggle="tab" data-target="#usage-counter" ng-click="getUsage()">System Usage</a>
</li>
<li>
<a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">System Logs</a>
</li>
</ul>
</div>
<div class="cor-tab-panel">
<div class="cor-tabs">
<!--<span class="cor-tab" tab-active="true" tab-title="Registry Settings" tab-target="#setup"
tab-init="loadConfig()">
<i class="fa fa-cog"></i>
</span>-->
<span class="cor-tab" tab-title="Manage Users" tab-target="#users" tab-init="loadUsers()">
<i class="fa fa-group"></i>
</span>
<span class="cor-tab" tab-title="Container Usage" tab-target="#usage-counter" tab-init="getUsage()">
<i class="fa fa-pie-chart"></i>
</span>
<span class="cor-tab" tab-title="Usage Logs" tab-target="#logs" tab-init="loadUsageLogs()">
<i class="fa fa-bar-chart"></i>
</span>
<!--<span class="cor-tab" tab-title="Internal Logs and Debugging" tab-target="#debug">
<i class="fa fa-bug"></i>
</span>-->
</div> <!-- /cor-tabs -->
<!-- Content -->
<div class="col-md-10">
<div class="tab-content">
<!-- Logs tab -->
<div id="logs" class="tab-pane">
<div class="logsView" makevisible="logsCounter" all-logs="true"></div>
<div class="cor-tab-content">
<!-- Logs tab -->
<div id="logs" class="tab-pane">
<div class="logsView" makevisible="logsCounter" all-logs="true"></div>
</div> <!-- /logs tab-->
<!-- Usage tab -->
<div id="usage-counter" class="tab-pane">
<div class="quay-spinner" ng-show="systemUsage == null"></div>
<div class="usage-chart" total="systemUsage.allowed" limit="systemUsageLimit"
current="systemUsage.usage" usage-title="Deployed Repositories"></div>
<!-- Alerts -->
<div class="alert alert-danger" ng-show="systemUsageLimit == 'over' && systemUsage">
You have deployed more repositories than your plan allows. Please
upgrade your subscription by contacting <a href="mailto:sales@coreos.com">CoreOS Sales</a>.
</div>
<!-- Usage tab -->
<div id="usage-counter" class="tab-pane">
<div class="quay-spinner" ng-show="systemUsage == null"></div>
<div class="usage-chart" total="systemUsage.allowed" limit="systemUsageLimit"
current="systemUsage.usage" usage-title="Deployed Repositories"></div>
<!-- Alerts -->
<div class="alert alert-danger" ng-show="systemUsageLimit == 'over' && systemUsage">
You have deployed more repositories than your plan allows. Please
upgrade your subscription by contacting <a href="mailto:sales@coreos.com">CoreOS Sales</a>.
</div>
<div class="alert alert-warning" ng-show="systemUsageLimit == 'at' && systemUsage">
You are at your current plan's number of allowed repositories. It might be time to think about
upgrading your subscription by contacting <a href="mailto:sales@coreos.com">CoreOS Sales</a>.
</div>
<div class="alert alert-success" ng-show="systemUsageLimit == 'near' && systemUsage">
You are nearing the number of allowed deployed repositories. It might be time to think about
upgrading your subscription by contacting <a href="mailto:sales@coreos.com">CoreOS Sales</a>.
</div>
<div class="alert alert-warning" ng-show="systemUsageLimit == 'at' && systemUsage">
You are at your current plan's number of allowed repositories. It might be time to think about
upgrading your subscription by contacting <a href="mailto:sales@coreos.com">CoreOS Sales</a>.
</div>
<!-- Create user tab -->
<div id="create-user" class="tab-pane">
<span class="quay-spinner" ng-show="creatingUser"></span>
<form name="createUserForm" ng-submit="createUser()" ng-show="!creatingUser">
<div class="form-group">
<label>Username</label>
<input class="form-control" type="text" ng-model="newUser.username" ng-pattern="/^[a-z0-9_]{4,30}$/" required>
<div class="alert alert-success" ng-show="systemUsageLimit == 'near' && systemUsage">
You are nearing the number of allowed deployed repositories. It might be time to think about
upgrading your subscription by contacting <a href="mailto:sales@coreos.com">CoreOS Sales</a>.
</div>
</div> <!-- /usage-counter tab-->
<!-- Users tab -->
<div id="users" class="tab-pane active">
<div class="quay-spinner" ng-show="!users"></div>
<div class="alert alert-error" ng-show="usersError">
{{ usersError }}
</div>
<div ng-show="users">
<div class="side-controls">
<div class="result-count">
Showing {{(users | filter:search | limitTo:100).length}} of
{{(users | filter:search).length}} matching users
</div>
<div class="form-group">
<label>Email address</label>
<input class="form-control" type="email" ng-model="newUser.email" required>
<div class="filter-input">
<input id="log-filter" class="form-control" placeholder="Filter Users" type="text" ng-model="search.$">
</div>
<button class="btn btn-primary" type="submit" ng-disabled="!createUserForm.$valid">Create User</button>
</form>
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #eee;" ng-show="createdUsers.length">
<table class="table">
<thead>
<th>Username</th>
<th>E-mail address</th>
<th>Temporary Password</th>
</thead>
<tr ng-repeat="created_user in createdUsers"
class="user-row">
<td>{{ created_user.username }}</td>
<td>{{ created_user.email }}</td>
<td>{{ created_user.password }}</td>
</tr>
</table>
</div>
</div>
<!-- Users tab -->
<div id="users" class="tab-pane active">
<div class="quay-spinner" ng-show="!users"></div>
<div class="alert alert-error" ng-show="usersError">
{{ usersError }}
</div>
<div ng-show="users">
<div class="side-controls">
<div class="result-count">
Showing {{(users | filter:search | limitTo:100).length}} of
{{(users | filter:search).length}} matching users
</div>
<div class="filter-input">
<input id="log-filter" class="form-control" placeholder="Filter Users" type="text" ng-model="search.$">
</div>
</div>
<table class="table">
<thead>
<th>Username</th>
<th>E-mail address</th>
<th style="width: 24px;"></th>
</thead>
<tr ng-repeat="current_user in (users | filter:search | orderBy:'username' | limitTo:100)"
class="user-row"
ng-class="current_user.super_user ? 'super-user' : ''">
<td>
<i class="fa fa-user" style="margin-right: 6px"></i>
{{ current_user.username }}
</td>
<td>
<a href="mailto:{{ current_user.email }}">{{ current_user.email }}</a>
</td>
<td style="text-align: center;">
<i class="fa fa-ge fa-lg" ng-if="current_user.super_user" data-title="Super User" bs-tooltip></i>
<div class="dropdown" style="text-align: left;" ng-if="user.username != current_user.username && !current_user.super_user">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<i class="caret"></i>
</button>
<ul class="dropdown-menu pull-right">
<li>
<a href="javascript:void(0)" ng-click="showChangePassword(current_user)">
<i class="fa fa-key"></i> Change Password
</a>
<a href="javascript:void(0)" ng-click="sendRecoveryEmail(current_user)" quay-show="Features.MAILING">
<i class="fa fa-envelope"></i> Send Recovery Email
</a>
<a href="javascript:void(0)" ng-click="showDeleteUser(current_user)">
<i class="fa fa-times"></i> Delete User
</a>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
<table class="table">
<thead>
<th>Username</th>
<th>E-mail address</th>
<th style="width: 24px;"></th>
</thead>
<tr ng-repeat="current_user in (users | filter:search | orderBy:'username' | limitTo:100)"
class="user-row"
ng-class="current_user.super_user ? 'super-user' : ''">
<td>
<i class="fa fa-user" style="margin-right: 6px"></i>
{{ current_user.username }}
</td>
<td>
<a href="mailto:{{ current_user.email }}">{{ current_user.email }}</a>
</td>
<td style="text-align: center;">
<i class="fa fa-ge fa-lg" ng-if="current_user.super_user" data-title="Super User" bs-tooltip></i>
<div class="dropdown" style="text-align: left;"
ng-if="user.username != current_user.username && !current_user.super_user">
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<i class="caret"></i>
</button>
<ul class="dropdown-menu pull-right">
<li>
<a href="javascript:void(0)" ng-click="showChangePassword(current_user)">
<i class="fa fa-key"></i> Change Password
</a>
<a href="javascript:void(0)" ng-click="sendRecoveryEmail(current_user)" quay-show="Features.MAILING">
<i class="fa fa-envelope"></i> Send Recovery Email
</a>
<a href="javascript:void(0)" ng-click="showDeleteUser(current_user)">
<i class="fa fa-times"></i> Delete User
</a>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div> <!-- /show if users -->
</div> <!-- users-tab -->
</div> <!-- /cor-tab-content -->
</div> <!-- /cor-tab-panel -->
<!-- Modal message dialog -->
<div class="modal fade" id="confirmDeleteUserModal">
@ -176,7 +139,7 @@
</div><!-- /.modal -->
<!-- Modal message dialog -->
<!-- Modal message dialog -->
<div class="modal fade" id="changePasswordModal">
<div class="modal-dialog">
<div class="modal-content">
@ -204,4 +167,5 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
</div> <!-- /page-content -->

View file

@ -96,10 +96,9 @@ mixpanel.init("{{ mixpanel_key }}", { track_pageview : false, debug: {{ is_debug
<body ng-class="pageClass + ' ' + (user.anonymous ? 'anon' : 'signedin')" class="co-img-bg-network">
<div id="co-l-footer-wrapper">
<nav class="navbar navbar-default header-bar co-m-navbar co-fx-box-shadow" role="navigation"></nav>
<div id="padding-container">
<div id="co-l-view-container">
<div class="main-panel co-fx-box-shadow-heavy">
<div ng-class="newLayout ? '' : 'main-panel co-fx-box-shadow-heavy'">
{% block body_content %}
{% endblock %}