mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
Merge pull request #205 from e-lie/enh-add-levels-to-applist
Redesign app install list : levels, multinstance confusing
This commit is contained in:
commit
15a430f16d
6 changed files with 237 additions and 23 deletions
|
@ -10,6 +10,7 @@
|
||||||
"handlebars": "4.0.11",
|
"handlebars": "4.0.11",
|
||||||
"sammy": "0.7.6",
|
"sammy": "0.7.6",
|
||||||
"js-cookie": "2.1.0",
|
"js-cookie": "2.1.0",
|
||||||
|
"isotope-layout": "3.0.6",
|
||||||
"source-sans-pro": "git://github.com/adobe-fonts/source-sans-pro.git#2.020R-ro/1.075R-it",
|
"source-sans-pro": "git://github.com/adobe-fonts/source-sans-pro.git#2.020R-ro/1.075R-it",
|
||||||
"source-code-pro": "git://github.com/adobe-fonts/source-code-pro.git#2.010R-ro/1.030R-it"
|
"source-code-pro": "git://github.com/adobe-fonts/source-code-pro.git#2.010R-ro/1.030R-it"
|
||||||
}
|
}
|
||||||
|
|
|
@ -557,6 +557,60 @@ input[type='radio'].nice-radio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only one card for small screens
|
||||||
|
.app-card {
|
||||||
|
width: 100%;
|
||||||
|
.btn-group {
|
||||||
|
width: 100%;
|
||||||
|
.btn{
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#app-filter-input{
|
||||||
|
.dropdown-menu{
|
||||||
|
min-width: 120px;
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
.menu-item{
|
||||||
|
padding: 3px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.app-title {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-card-desc {
|
||||||
|
height: 6rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-card-date-maintainer {
|
||||||
|
text-align: right;
|
||||||
|
max-height: 18px;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
margin-right: 7px;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.app-card .panel-body {
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Flash messages **/
|
/** Flash messages **/
|
||||||
#flashMessage {
|
#flashMessage {
|
||||||
|
@ -600,6 +654,17 @@ input[type='radio'].nice-radio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display 2 cards between 640 and 992px
|
||||||
|
.app-card {
|
||||||
|
width: 47.9%;
|
||||||
|
margin: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
#flashMessage {
|
#flashMessage {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -689,3 +754,12 @@ input[type='radio'].nice-radio {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bootstrap breakpoint for large screen is 992px
|
||||||
|
@media screen and (min-width: 992px) {
|
||||||
|
.app-card {
|
||||||
|
// display 3 cards by row
|
||||||
|
width: 31.3%;
|
||||||
|
margin: 1%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ gulp.task('js', function() {
|
||||||
'bower_components/sammy/lib/plugins/sammy.json.js',
|
'bower_components/sammy/lib/plugins/sammy.json.js',
|
||||||
'bower_components/sammy/lib/plugins/sammy.storage.js',
|
'bower_components/sammy/lib/plugins/sammy.storage.js',
|
||||||
'bower_components/bootstrap/dist/js/bootstrap.js',
|
'bower_components/bootstrap/dist/js/bootstrap.js',
|
||||||
|
'bower_components/isotope-layout/dist/isotope.pkgd.js',
|
||||||
'js/yunohost/y18n.js',
|
'js/yunohost/y18n.js',
|
||||||
'js/yunohost/main.js',
|
'js/yunohost/main.js',
|
||||||
'js/yunohost/helpers.js',
|
'js/yunohost/helpers.js',
|
||||||
|
|
|
@ -17,16 +17,68 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function levelToColor(level) {
|
||||||
|
if (level > 6) {
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
else if (level >= 2) {
|
||||||
|
return 'warning';
|
||||||
|
}
|
||||||
|
else if (isNaN(level)) {
|
||||||
|
return 'default';
|
||||||
|
} else {
|
||||||
|
return 'danger'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stateToColor(state) {
|
||||||
|
if (state === "working" || state === "official") {
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'danger';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function combineColors(stateColor, levelColor, installable) {
|
||||||
|
if (stateColor === "dangers" || levelColor === "danger") {
|
||||||
|
return 'danger';
|
||||||
|
}
|
||||||
|
if (stateColor === "warnings" || levelColor === "warnings" || levelColor === "default") {
|
||||||
|
return 'warning';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// List available apps
|
// List available apps
|
||||||
app.get('#/apps/install', function (c) {
|
app.get('#/apps/install', function (c) {
|
||||||
c.api('/apps', function(data) { // http://api.yunohost.org/#!/app/app_list_get_8
|
c.api('/apps', function (data) { // http://api.yunohost.org/#!/app/app_list_get_8
|
||||||
c.api('/apps?raw', function(dataraw) { // http://api.yunohost.org/#!/app/app_list_get_8
|
c.api('/apps?raw', function (dataraw) { // http://api.yunohost.org/#!/app/app_list_get_8
|
||||||
var apps = [];
|
var apps = []
|
||||||
$.each(data['apps'], function(k, v) {
|
$.each(data['apps'], function(k, v) {
|
||||||
// Keep only uninstalled apps, or multi-instance apps
|
if (dataraw[v['id']]['state'] === "validated")
|
||||||
if ((!v['installed'] || dataraw[v['id']].manifest.multi_instance) && !v['id'].match(/__[0-9]{1,5}$/)) {
|
{
|
||||||
// Check app source
|
dataraw[v['id']]['state'] = "official";
|
||||||
dataraw[v['id']]['official'] = (dataraw[v['id']]['repository'] == 'yunohost');
|
}
|
||||||
|
var state = dataraw[v['id']]['state'];
|
||||||
|
var levelFormatted = parseInt(dataraw[v['id']]['level']);
|
||||||
|
var isWorking = (state === 'working' || state === 'official') && levelFormatted > 0;
|
||||||
|
// Keep only the first instance of each app and remove community not working apps
|
||||||
|
if (!v['id'].match(/__[0-9]{1,5}$/) && (dataraw[v['id']]['repository'] === 'yunohost' || state !== 'notworking')) {
|
||||||
|
|
||||||
|
dataraw[v['id']]['installable'] = (!v['installed'] || dataraw[v['id']].manifest.multi_instance)
|
||||||
|
dataraw[v['id']]['isCommunity'] = !(dataraw[v['id']]['repository'] === 'yunohost');
|
||||||
|
dataraw[v['id']]['levelFormatted'] = isNaN(levelFormatted) ? '?' : levelFormatted;
|
||||||
|
dataraw[v['id']]['levelColor'] = levelToColor(levelFormatted);
|
||||||
|
dataraw[v['id']]['stateColor'] = stateToColor(state);
|
||||||
|
dataraw[v['id']]['installColor'] = combineColors(dataraw[v['id']]['stateColor'], dataraw[v['id']]['levelColor']);
|
||||||
|
dataraw[v['id']]['displayLicense'] = (dataraw[v['id']]['manifest']['license'] !== undefined
|
||||||
|
&& dataraw[v['id']]['manifest']['license'] !== 'free');
|
||||||
|
dataraw[v['id']]['updateDate'] = dataraw[v['id']]['lastUpdate'] * 1000 || 0;
|
||||||
|
dataraw[v['id']]['isSafe'] = (dataraw[v['id']]['installColor'] !== 'danger');
|
||||||
|
dataraw[v['id']]['isWorking'] = isWorking ? "isworking" : "notFullyWorking";
|
||||||
|
|
||||||
jQuery.extend(dataraw[v['id']], v);
|
jQuery.extend(dataraw[v['id']], v);
|
||||||
apps.push(dataraw[v['id']]);
|
apps.push(dataraw[v['id']]);
|
||||||
|
@ -35,7 +87,45 @@
|
||||||
|
|
||||||
// Sort app list
|
// Sort app list
|
||||||
c.arraySortById(apps);
|
c.arraySortById(apps);
|
||||||
c.view('app/app_list_install', {apps: apps});
|
|
||||||
|
// setup filtering of apps once the view is loaded
|
||||||
|
function setupFilterEvents () {
|
||||||
|
// Uses plugin isotope to filter apps (we could had ordering to)
|
||||||
|
var cardGrid = jQuery('.grid').isotope({
|
||||||
|
itemSelector: '.app-card',
|
||||||
|
layoutMode: 'fitRows',
|
||||||
|
transitionDuration: 200
|
||||||
|
});
|
||||||
|
|
||||||
|
filterByClassAndName = function () {
|
||||||
|
var input = jQuery("#filter-app-cards").val().toLowerCase();
|
||||||
|
var inputMatch = (jQuery(this).find('.app-title').text().toLowerCase().indexOf(input) > -1);
|
||||||
|
|
||||||
|
var filterClass = jQuery("#dropdownFilter").attr("data-filter");
|
||||||
|
var classMatch = (filterClass === '*') ? true : jQuery(this).hasClass(filterClass);
|
||||||
|
return inputMatch && classMatch;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Keep only official apps at first render
|
||||||
|
cardGrid.isotope({ filter: '.isworking' });
|
||||||
|
|
||||||
|
jQuery('.dropdownFilter').on('click', function() {
|
||||||
|
// change dropdown label
|
||||||
|
jQuery('#app-cards-list-filter-text').text(jQuery(this).find('.menu-item').text());
|
||||||
|
// change filter attribute
|
||||||
|
jQuery('#dropdownFilter').attr("data-filter", jQuery(this).attr("data-filter"));
|
||||||
|
// filter !
|
||||||
|
cardGrid.isotope({ filter: filterByClassAndName });
|
||||||
|
});
|
||||||
|
|
||||||
|
jQuery("#filter-app-cards").on("keyup", function() {
|
||||||
|
cardGrid.isotope({ filter: filterByClassAndName });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// render
|
||||||
|
c.view('app/app_list_install', {apps: apps}, setupFilterEvents);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -681,7 +771,7 @@
|
||||||
c.view('app/app_changelabel', data);
|
c.view('app/app_changelabel', data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Change app label
|
// Change app label
|
||||||
app.post('#/apps/:app/changelabel', function (c) {
|
app.post('#/apps/:app/changelabel', function (c) {
|
||||||
params = {'new_label': c.params['label']};
|
params = {'new_label': c.params['label']};
|
||||||
|
@ -718,7 +808,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Change app URL
|
// Change app URL
|
||||||
app.post('#/apps/:app/changeurl', function (c) {
|
app.post('#/apps/:app/changeurl', function (c) {
|
||||||
c.confirm(
|
c.confirm(
|
||||||
|
@ -736,5 +826,5 @@
|
||||||
c.redirect('#/apps/'+ c.params['app'] + '/changeurl');
|
c.redirect('#/apps/'+ c.params['app'] + '/changeurl');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"administration_password": "Administration password",
|
"administration_password": "Administration password",
|
||||||
"allowed_users": "Allowed users",
|
"allowed_users": "Allowed users",
|
||||||
|
"all_apps": "All apps",
|
||||||
"api_not_responding": "API is not responding",
|
"api_not_responding": "API is not responding",
|
||||||
"app_access": "Access",
|
"app_access": "Access",
|
||||||
"app_access_addall_btn": "Enable access to all",
|
"app_access_addall_btn": "Enable access to all",
|
||||||
|
@ -28,6 +29,8 @@
|
||||||
"app_install_cancel": "Installation cancelled.",
|
"app_install_cancel": "Installation cancelled.",
|
||||||
"app_install_custom_no_manifest": "No manifest.json file",
|
"app_install_custom_no_manifest": "No manifest.json file",
|
||||||
"app_list": "App list",
|
"app_list": "App list",
|
||||||
|
"app_license": "License of the app",
|
||||||
|
"app_level": "App level",
|
||||||
"app_make_default": "Make default",
|
"app_make_default": "Make default",
|
||||||
"app_no_actions": "This application doesn't have any actions",
|
"app_no_actions": "This application doesn't have any actions",
|
||||||
"app_repository": "Application origin: ",
|
"app_repository": "Application origin: ",
|
||||||
|
@ -95,6 +98,7 @@
|
||||||
"cpu_load": "CPU Load",
|
"cpu_load": "CPU Load",
|
||||||
"created_at": "Created at",
|
"created_at": "Created at",
|
||||||
"cumulative_usage": "Cumulative usage",
|
"cumulative_usage": "Cumulative usage",
|
||||||
|
"current_maintainer_title": "Current maintainer of this package",
|
||||||
"custom_app_install": "Install custom app",
|
"custom_app_install": "Install custom app",
|
||||||
"custom_app_url_only_github": "Currently only from GitHub",
|
"custom_app_url_only_github": "Currently only from GitHub",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
|
@ -178,6 +182,7 @@
|
||||||
"ipv6": "IPv6",
|
"ipv6": "IPv6",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"label_for_manifestname": "Label for %s",
|
"label_for_manifestname": "Label for %s",
|
||||||
|
"level": "level",
|
||||||
"loading": "Loading …",
|
"loading": "Loading …",
|
||||||
"local_archives": "Local archives",
|
"local_archives": "Local archives",
|
||||||
"local_ip": "Local IP",
|
"local_ip": "Local IP",
|
||||||
|
@ -215,6 +220,8 @@
|
||||||
"no_user_to_add": "No more users to add.",
|
"no_user_to_add": "No more users to add.",
|
||||||
"non_compatible_api": "Non-compatible API",
|
"non_compatible_api": "Non-compatible API",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
|
"only_validated_apps": "Only validated apps",
|
||||||
|
"only_working_apps": "Only working apps",
|
||||||
"open": "Open",
|
"open": "Open",
|
||||||
"operations": "Operations",
|
"operations": "Operations",
|
||||||
"os": "OS",
|
"os": "OS",
|
||||||
|
@ -270,6 +277,7 @@
|
||||||
"run": "Run",
|
"run": "Run",
|
||||||
"running": "Running",
|
"running": "Running",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
|
"search_for_apps": "Search for apps...",
|
||||||
"select_user": "Select user",
|
"select_user": "Select user",
|
||||||
"service_description": "Description:",
|
"service_description": "Description:",
|
||||||
"service_log": "%s log",
|
"service_log": "%s log",
|
||||||
|
@ -330,6 +338,7 @@
|
||||||
"uninstall": "Uninstall",
|
"uninstall": "Uninstall",
|
||||||
"unknown_action": "Unknown action %s",
|
"unknown_action": "Unknown action %s",
|
||||||
"unknown_argument": "Unknown argument : %s",
|
"unknown_argument": "Unknown argument : %s",
|
||||||
|
"unmaintained": "Unmaintained",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"upload_archive": "Archive upload",
|
"upload_archive": "Archive upload",
|
||||||
"upnp": "UPnP",
|
"upnp": "UPnP",
|
||||||
|
|
|
@ -15,19 +15,58 @@
|
||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
|
||||||
<div class="list-group">
|
<div class="input-group" id="app-filter-input">
|
||||||
|
<span class="input-group-addon"><i class="fas fa-search"></i></span>
|
||||||
|
<input type="text" id="filter-app-cards" class="form-control" role="textbox" placeholder="{{t 'search_for_apps'}}" aria-describedby="basic-addon0"/>
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" role="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span id="app-cards-list-filter-text">{{t 'only_working_apps'}}</span> <span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul id="dropdownFilter" class="dropdown-menu" data-filter="isworking" role="menu">
|
||||||
|
<li role="presentation" class="button dropdownFilter" data-filter="isworking"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'only_working_apps'}}</a></li>
|
||||||
|
<li role="presentation" class="button dropdownFilter" data-filter="validated"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'only_validated_apps'}}</a></li>
|
||||||
|
<li role="presentation" class="button dropdownFilter" data-filter="*"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'all_apps'}}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<div class="list-group grid">
|
||||||
{{#apps}}
|
{{#apps}}
|
||||||
<a href="#/apps/install/{{id}}" class="list-group-item slide" title="{{t 'install'}}">
|
<div class="app-card panel panel-default {{status}} {{state}} {{isWorking}} {{level}}-level">
|
||||||
<span class="fa-chevron-right pull-right"></span>
|
<div class="panel-body">
|
||||||
<h2 class="list-group-item-heading">
|
<h2 class="app-title">{{name}}</h2>
|
||||||
{{name}} <small>{{id}}</small>
|
<div class="category">
|
||||||
</h2>
|
{{#isCommunity}} <span class="label label-info label-as-badge app-status">{{t 'community'}}</span>{{/isCommunity}}
|
||||||
<p class="list-group-item-text">{{description}}</p>
|
<span class="label label-{{stateColor}} label-as-badge app-state">{{t state}}</span>
|
||||||
{{^official}}
|
<a target="_BLANK" href="https://yunohost.org/#/packaging_apps_levels"><span class="label label-{{levelColor}} label-as-badge app-level" title="{{t 'app_level'}}">{{t 'level'}} {{levelFormatted}}</span></a>
|
||||||
<p class="list-group-item-text">{{t 'app_repository'}}{{repository}}</p>
|
{{#displayLicense}}<span class="label label-default app-license" title="{{t 'app_license'}}">{{license}}</span>{{/displayLicense}}
|
||||||
<p class="list-group-item-text">{{t 'app_state'}}{{state}}</p>
|
</div>
|
||||||
{{/official}}
|
<div class="app-card-desc">{{description}}</div>
|
||||||
</a>
|
</div>
|
||||||
|
<div class="app-card-date-maintainer">
|
||||||
|
<i class="fas fa-sync"></i>{{formatDate updateDate day="numeric" month="long" year="numeric"}} -
|
||||||
|
{{#maintained}}<span title="{{t 'current_maintainer_title'}}" class="maintained"></span><i class="fa-user"></i> {{manifest.maintainer.name}}</span>{{/maintained}}
|
||||||
|
{{^maintained}}<i class="fas fa-warning"></i> {{t 'unmaintained'}}{{/maintained}}
|
||||||
|
</div>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<a href="{{git.url}}" target="_BLANK" type="button" role="button" class="btn btn-default col-xs-4">
|
||||||
|
<i class="fa-globe"></i> Code
|
||||||
|
</a>
|
||||||
|
<a href="{{git.url}}/blob/master/README.md" target="_BLANK" type="button" role="button" class="btn btn-default col-xs-4">
|
||||||
|
<i class="fa-book"></i> Readme
|
||||||
|
</a>
|
||||||
|
{{#installable}}
|
||||||
|
<a href="#/apps/install/{{id}}" type="button" role="button" class="btn btn-{{installColor}} col-xs-4 active">
|
||||||
|
<i class="fa-plus"></i> {{t 'install'}}{{^isSafe}} <i class="fa-warning"></i>{{/isSafe}}
|
||||||
|
</a>
|
||||||
|
{{/installable}}
|
||||||
|
{{^installable}}
|
||||||
|
<span type="button" class="btn btn-default col-sm-4 active disabled"> {{t 'installed'}}</span>
|
||||||
|
{{/installable}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{/apps}}
|
{{/apps}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue