Initial stab at a grid layout for apps.

See title.

Change-Id: I482626db844aed9877ffc73d64167fbf581689fa
This commit is contained in:
Kevin Fox 2015-08-10 17:19:57 -07:00
parent 9fe9cbc99a
commit 5d92b1cd12
10 changed files with 269 additions and 101 deletions

View File

@ -1,3 +1,7 @@
Some icons from here:
http://jxnblk.com/ - License: MIT
The rest is Apache 2.0:
Apache License
Version 2.0, January 2004

View File

@ -20,8 +20,7 @@ How to try this package
git clone http://github.com/openstack/horizon.git
git clone http://github.com/stackforge/apps-catalog-ui.git
cd horizon
git fetch https://review.openstack.org/openstack/horizon refs/changes/73/206773/13 && git checkout FETCH_HEAD
git fetch https://review.openstack.org/openstack/horizon refs/changes/82/201582/7 && git cherry-pick FETCH_HEAD
git fetch https://review.openstack.org/openstack/horizon refs/changes/73/206773/21 && git checkout FETCH_HEAD
./run_tests.sh -f --docs
cp ./openstack_dashboard/local/local_settings.py.example ./openstack_dashboard/local/local_settings.py
pushd ../apps-catalog-ui

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

View File

@ -0,0 +1,50 @@
<div>
<dl class="dl-horizontal">
<dt>
<div style="
width:64px;
height:64px;
overflow: hidden;
">
<img style="
margin: {$ asset.icon.top/2 $}px 0px 0px {$ asset.icon.left/2 $}px;
height: {$ asset.icon.height/2 $}px;
" src="{$ asset.icon.url $}">
</div>
</dt>
<dd>
<div>{$ asset.name $}</div>
<div>{$ asset.provided_by.company $}</div>
<div ng-switch="appaction" style="float:right">
<div ng-switch-when='true' app-action ng-scope ng-init='extraclasses="btn-lg"'></div>
</div>
</dd>
<dt>License</dt>
<dd ng-switch="asset.license_url || '_undefined_'">
<div>{$ asset.license $}</div>
<a ng-switch-default class="btn btn-default btn-sm" target="_blank" href="{$ asset.license_url $}">License Details</a>
<div ng-switch-when="_undefined_"></div>
</dd>
<dt>Description</dt>
<dd>{$ asset.description $}</dd>
<dt ng-switch="asset.depends || '_undefined_'">
<div ng-switch-default>Dependencies</div>
<div ng-switch-when="_undefined_"></div>
</dt>
<dd ng-switch="asset.depends || '_undefined_'">
<div ng-switch-default>
<table>
<tr ng-repeat="dep in asset.depends">
<td>{$ dep.name $}</td>
<td ng-switch="dep.asset.installed">
<a ng-switch-default class="btn btn-default btn-sm disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-when="false" class="btn btn-default btn-sm ajax-modal" href="/project/images/create/?name={$ dep.asset.name | encodeURIComponent $}&source_type=url&image_url={$ dep.asset.attributes.url | encodeURIComponent $}&disk_format={$ dep.asset.service.disk_format $}&architecture={$ dep.asset.service.architecture $}&minimum_disk={$ dep.asset.service.min_disk $}&minimum_ram={$ dep.asset.service.min_ram $}&description={$ dep.asset.description $}">Install</a>
<a ng-switch-when="true" class="btn ntn-default btn-sm disbled">Installed</a>
</td>
</tr>
</table>
<div ng-switch-when="_undefined_"></div>
</dd>
</dl>
</div>

View File

@ -0,0 +1,17 @@
<div ng-switch="asset.service.type">
<div ng-switch-when="heat" ng-switch="asset.validated">
<a ng-switch-default ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-when="error" ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Error</a>
<a ng-switch-when="unsupported" ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/">Unsupported</a>
<a ng-switch-when="true" ng-class="extraclasses" class="btn btn-success btn-default ajax-modal" href="/project/stacks/select_template?template_source=url&template_url={$ asset.attributes.url | encodeURIComponent $}">Launch</a>
</div>
<div ng-switch-when="glance" ng-switch="asset.attributes.indirect_url || '_undefined_'">
<a ng-switch-default ng-class="extraclasses" class="btn btn-primary btn-default" target="_blank" href="{$ asset.attributes.indirect_url $}">Install Instructions</a>
<div ng-switch-when="_undefined_" ng-switch="asset.installed">
<a ng-switch-default ng-class="extraclasses" class="btn btn-default disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-when="false" ng-class="extraclasses" class="btn btn-primary btn-default ajax-modal" href="/project/images/create/?name={$ asset.name | encodeURIComponent $}&source_type=url&image_url={$ asset.attributes.url | encodeURIComponent $}&disk_format={$ asset.service.disk_format $}&architecture={$ asset.service.architecture $}&minimum_disk={$ asset.service.min_disk $}&minimum_ram={$ asset.service.min_ram $}&description={$ asset.description $}">Install</a>
<a ng-switch-when="true" ng-class="extraclasses" class="btn btn-success btn-default ajax-modal" href="/project/instances/launch?source_type=image_id&source_id={$ asset.installed_id $}">Launch</a>
</a>
</div>
</div>
</div>

View File

@ -19,13 +19,20 @@
'use strict';
angular
.module('hz.dashboard.project.app_catalog', ['hz.dashboard'])
.module('horizon.dashboard.project.app_catalog', [])
.filter('encodeURIComponent', function() {
return window.encodeURIComponent;
}).directive('appAction', function () {
return {
restrict: 'EA',
//FIXME static
templateUrl: '/static/dashboard/project/app_catalog/action.html'
};
}).controller('appCatalogTableCtrl', [
'$scope',
'$http',
'$timeout',
'$modal',
'appCatalogModel',
appCatalogTableCtrl
]).controller('appComponentCatalogTableCtrl', [
@ -228,7 +235,7 @@
$scope.init = appCatalogModel.init;
}
function appCatalogTableCtrl($scope, $http, $timeout, appCatalogModel) {
function appCatalogTableCtrl($scope, $http, $timeout, $modal, appCatalogModel) {
$scope.assets = []
var update = function(){
$scope.assets = []
@ -239,8 +246,28 @@
}
}
};
//FIXME remove. probably belongs in its own directive...
// var textSearchWatcher = $scope.$on('textSearch', function(event, text) {
// console.log(text);
// });
appCatalogModel.register_callback(update);
common_init($scope, appCatalogModel);
$scope.switcher = {pannel: 'app', active: 'grid'};
$scope.changeActivePanel = function(name) {
$scope.switcher['active'] = name;
};
$scope.details = function(asset) {
var newscope = $scope.$new();
newscope.asset = asset;
var modal = $modal.open({
//FIXME static from where?
templateUrl: "/static/dashboard/project/app_catalog/details_panel.html",
scope: newscope
});
newscope.cancel = function() {
modal.dismiss('');
};
};
}
function appComponentCatalogTableCtrl($scope, $http, $timeout, appCatalogModel) {
@ -252,6 +279,7 @@
};
appCatalogModel.register_callback(update);
common_init($scope, appCatalogModel);
$scope.switcher = {pannel: 'component', active: 'list'};
}
function update_found_assets($scope) {

View File

@ -0,0 +1,12 @@
<div>
<div class="modal-header">
<h3 class="modal-title">Details</h3>
</div>
<div class="modal-body">
<!--FIXME static -->
<ng-include src="'/static/dashboard/project/app_catalog/_details_panel.html'" onload="appaction=true"></ng-include>
</div>
<div class="modal-footer">
<button class="btn btn-default" ng-click="cancel()">Close</button>
</div>
</div>

View File

@ -1,100 +1,158 @@
Service Types: <label ng-repeat="service in service_filters">
<input
type="checkbox"
name="selected_filters[]"
value="{$ service.id $}"
ng-checked="service_filters_selections[service.id]"
ng-click="toggle_service_filter(service.id)"
> {$ service.name $}
</label>
<table hz-table ng-cloak st-table="dispassets" st-safe-src="assets"
class="table-striped table-rsp table-detail modern">
<thead>
<tr>
<th colspan="4">
<st-magic-search>
<hz-magic-search-bar filter-facets="asset_filter_facets" filter-strings="asset_filter_strings">
</hz-magic-search-bar>
</st-magic-search>
</th>
</tr>
<tr>
<th class="expander"></th>
<th st-sort="name" st-sort-default="true" class="rsp-p1">Name</th>
<th st-sort='license' class="rsp-p1">License</th>
<th class="rsp-p1"></th>
</tr>
</thead>
<div style="margin-bottom: 4px;">
Service Types:
<label ng-repeat="service in service_filters">
<input
type="checkbox"
name="selected_filters[]"
value="{$ service.id $}"
ng-checked="service_filters_selections[service.id]"
ng-click="toggle_service_filter(service.id)"
> {$ service.name $}
</label>
<span ng-switch="switcher.pannel" style="float:right">
<span ng-switch-when="app" style="float: right" class="toggleView btn-group">
<!--FIXME path?-->
<img class="btn btn-default" ng-class="{active: (switcher.active == 'grid')}" src="/static/dashboard/project/app_catalog/1439233859_grid.png" ng-click="changeActivePanel('grid')"/>
<img class="btn btn-default" ng-class="{active: (switcher.active == 'list')}" src="/static/dashboard/project/app_catalog/1439233889_list.png" ng-click="changeActivePanel('list')"/>
</span>
</span>
</div>
<tbody>
<tr ng-repeat-start="asset in dispassets track by asset.name">
<td class="expander">
<i class="fa fa-chevron-right" hz-expand-detail duration="200"></i>
</td>
<td class="rsp-p1">{$ asset.name $}</td>
<td class="rsp-p2">{$ asset.license $}</td>
<td ng-switch="asset.service.type">
<div ng-switch-when="heat" ng-switch="asset.validated">
<a ng-switch-default class="btn btn-default btn-sm disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-when="error" class="btn btn-default btn-sm disabled ajax-modal" href="/">Error</a>
<a ng-switch-when="unsupported" class="btn btn-default btn-sm disabled ajax-modal" href="/">Unsupported</a>
<a ng-switch-when="true" class="btn btn-default btn-sm ajax-modal" href="/project/stacks/select_template?template_source=url&template_url={$ asset.attributes.url | encodeURIComponent $}">Launch</a>
<div ng-switch="switcher.active">
<!-- <hz-magic-search-bar filter-facets="asset_filter_facets" filter-strings="asset_filter_strings">
</hz-magic-search-bar>-->
<div ng-switch-when="grid" style="
background-color:#f9f9f9;
overflow: auto;
border-color:#cccccc; border-width:1px; border-style: solid;
">
<div ng-repeat="asset in assets | orderBy:'name':false" style="border:1px;
margin: 10px;
width: 200px; height: 180px; float: left;
">
<div ng-click="details(asset)" style="
margin:10px;
border-color:#cccccc; border-width:1px;
border-radius: 2px;
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.1);
padding:8px;
height: 170px;
overflow: hidden;
background-color:#ffffff;
position: relative;
">
<div style="">
<div style="
width:128px;
height:128px;
overflow: hidden;
margin-left: auto; margin-right:auto;
">
<img style="
margin: {$ asset.icon.top $}px 0px 0px {$ asset.icon.left $}px;
height: {$ asset.icon.height $}px;
" src="{$ asset.icon.url $}">
</div>
<span style="
max-height: 100%;
width: 23px;
height: 23px;
top: 0;
right: 0;
position: absolute;
background: rgba(0, 0, 0, 0) url('http://apps.openstack.org/static/images/featured-corner-{$ asset.service.type $}.png') no-repeat scroll right top;
">
</span>
</div>
<div ng-switch-when="glance" ng-switch="asset.attributes.indirect_url || '_undefined_'">
<a ng-switch-default class="btn btn-default btn-sm" target="_blank" href="{$ asset.attributes.indirect_url $}">Install Instructions</a>
<div ng-switch-when="_undefined_" ng-switch="asset.installed">
<a ng-switch-default class="btn btn-default btn-sm disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-when="false" class="btn btn-default btn-sm ajax-modal" href="/project/images/create/?name={$ asset.name | encodeURIComponent $}&source_type=url&image_url={$ asset.attributes.url | encodeURIComponent $}&disk_format={$ asset.service.disk_format $}&architecture={$ asset.service.architecture $}&minimum_disk={$ asset.service.min_disk $}&minimum_ram={$ asset.service.min_ram $}&description={$ asset.description $}">Install</a>
<a ng-switch-when="true" class="btn btn-default btn-sm ajax-modal" href="/project/instances/launch?source_type=image_id&source_id={$ asset.installed_id $}">Launch</a>
</a>
</div>
<div title="{$ asset.name $}" style="
overflow: hidden;
white-space: nowrap;
position: relative;
line-height: 18px;
font-size: 16px;
"> {$ asset.name $}
<span style="
max-height: 100%;
width: 40px;
height: 20px;
bottom: 0;
right: 0;
position: absolute;
background: rgba(0, 0, 0, 0) linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)) repeat scroll 0 0;
">
</span>
</div>
</td>
</tr>
<div title="{$ asset.provided_by.name $}" style="
overflow: hidden;
white-space: nowrap;
position: relative;
line-height: 14px;
font-size: 12px;
"> {$ asset.provided_by.name $}
<span style="
max-height: 100%;
width: 40px;
height: 20px;
bottom: 0;
right: 0;
position: absolute;
background: rgba(0, 0, 0, 0) linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)) repeat scroll 0 0;
">
</span>
</div>
</div>
</div>
</div>
<tr ng-repeat-end class="detail-row">
<td class="detail" colspan="4">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd>{$ asset.name $}</dd>
<dt>Description</dt>
<dd>{$ asset.description $}</dd>
<dt>Company</dt>
<dd>{$ asset.provided_by.company $}</dd>
<dt></dt>
<dd ng-switch="asset.license_url || '_undefined_'">
<a ng-switch-default class="btn btn-default btn-sm" target="_blank" href="{$ asset.license_url $}">License Details</a>
<div ng-switch-when="_undefined_"></div>
</dd>
<dt ng-switch="asset.depends || '_undefined_'">
<div ng-switch-default>Dependencies</div>
<div ng-switch-when="_undefined_"></div>
</dt>
<dd ng-switch="asset.depends || '_undefined_'">
<div ng-switch-default>
<table>
<tr ng-repeat="dep in asset.depends">
<td>{$ dep.name $}</td>
<td ng-switch="dep.asset.installed">
<a ng-switch-default class="btn btn-default btn-sm disabled ajax-modal" href="/}">Checking</a>
<a ng-switch-when="false" class="btn btn-default btn-sm ajax-modal" href="/project/images/create/?name={$ dep.asset.name | encodeURIComponent $}&source_type=url&image_url={$ dep.asset.attributes.url | encodeURIComponent $}&disk_format={$ dep.asset.service.disk_format $}&architecture={$ dep.asset.service.architecture $}&minimum_disk={$ dep.asset.service.min_disk $}&minimum_ram={$ dep.asset.service.min_ram $}&description={$ dep.asset.description $}">Install</a>
<a ng-switch-when="true" class="btn ntn-default btn-sm disbled">Installed</a>
</td>
</tr>
</table>
<div ng-switch-when="_undefined_"></div>
</dd>
</dl>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-center">
<div st-pagination="" st-items-by-page="itemsByPage" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
<div ng-switch-when="list">
<table hz-table ng-cloak st-table="dispassets" st-safe-src="assets"
class="table-striped table-rsp table-detail modern">
<thead>
<tr>
<th colspan="4">
<st-magic-search>
<hz-magic-search-bar filter-facets="asset_filter_facets" filter-strings="asset_filter_strings">
</hz-magic-search-bar>
</st-magic-search>
</th>
</tr>
<tr>
<th class="expander"></th>
<th st-sort="name" st-sort-default="true" class="rsp-p1">Name</th>
<th st-sort='license' class="rsp-p1">License</th>
<th class="rsp-p1"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat-start="asset in dispassets track by asset.name">
<td class="expander">
<i class="fa fa-chevron-right" hz-expand-detail duration="200"></i>
</td>
<td class="rsp-p1">{$ asset.name $}</td>
<td class="rsp-p2">{$ asset.license $}</td>
<td class="rsp-p2"><div app-action ng-scope ng-init="extraclasses='btn-sm'"></div></td>
</tr>
<tr ng-repeat-end class="detail-row">
<td class="detail" colspan="4">
<!--FIXME static-->
<ng-include src="'/static/dashboard/project/app_catalog/_details_panel.html'"></ng-include>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-center">
<div st-pagination="" st-items-by-page="itemsByPage" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>

View File

@ -22,7 +22,7 @@ ADD_PANEL = 'app_catalog.panel.AppCatalog'
ADD_INSTALLED_APPS = ['app_catalog']
ADD_ANGULAR_MODULES = ['hz.dashboard.project.app_catalog']
ADD_ANGULAR_MODULES = ['horizon.dashboard.project.app_catalog']
ADD_JS_FILES = [
'dashboard/project/app_catalog/app_catalog.js'