1
0
Fork 0
mirror of https://github.com/YunoHost/apps.git synced 2024-09-03 20:06:07 +02:00
apps/store/templates/dash.html

476 lines
23 KiB
HTML

{% extends "base.html" %}
{% block title %}
{{ _("App packaging dashboard") }}
{% endblock %}
{% block main %}
<div class="mx-auto w-full text-center px-4 py-8">
<h1 class="text-2xl font-bold">
{{ _("App packaging dashboard") }}
</h1>
<p class="text-sm text-gray-700 mx-2 md:mx-32 mt-2">{{ _("This is where packagers can monitor the status of automatic tests (CI) and ongoing major pull requests accross all apps. If you want to get started with app packaging in YunoHost, please check out the <a class='text-blue-500' href='https://yunohost.org/packaging_apps'>packaging documentation</a> and come say hi to us on the <a class='text-blue-500' href='https://yunohost.org/chat_rooms'>app packaging chatroom</a>!") }}</p>
</div>
<div class="text-center justify-center space-y-3 md:space-y-0 md:space-x-3 px-2 md:px-10 lg:px-20 flex flex-col flex-wrap md:flex-row pb-5">
<div>
{{ _("Filter") }}
<select
name="selectfilter"
id="selectfilter"
class="rounded-md border-gray-200 text-sm h-8 py-0 max-w-72 sm:max-none"
>
<option {% if request.args.get("filter") in [None, "none"] %}selected{% endif %} value="none">{{ _("(None)") }}</option>
<option {% if request.args.get("filter") == "regressions_main_ci" %}selected{% endif %} value="regressions_main_ci">{{ _("Regressions on main CI") }}</option>
<option {% if request.args.get("filter") == "broken_low_quality" %}selected{% endif %} value="broken_low_quality">{{ _("Broken / low quality apps") }}</option>
<option {% if request.args.get("filter") == "outdated" %}selected{% endif %} value="outdated">{{ _("Outdated tests on main CI") }}</option>
<option {% if request.args.get("filter") == "regressions_bookworm" %}selected{% endif %} value="regressions_bookworm">{{ _("Major regressions on Bookworm CI") }}</option>
<option {% if request.args.get("filter") == "testings" %}selected{% endif %} value="testings">{{ _("Apps with testings PRs") }}</option>
<option {% if request.args.get("filter") == "autoupdate" %}selected{% endif %} value="autoupdate">{{ _("Apps with autoupdate PRs") }}</option>
<option {% if request.args.get("filter") == "nb_issues" %}selected{% endif %} value="nb_issues">{{ _("Apps with issues") }}</option>
<option {% if request.args.get("filter") == "packagingv1" %}selected{% endif %} value="packagingv1">{{ _("Packaging v1 apps") }}</option>
</select>
</div>
<div>
{{ _("Sort by") }}
<select
name="selectsort"
id="selectsort"
class="rounded-md border-gray-200 text-sm h-8 py-0 max-w-72 sm:max-none"
>
<option {% if request.args.get("sort") == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</option>
<option {% if request.args.get("sort") == "level" %}selected{% endif %} value="level">{{ _("Quality level") }}</option>
<option {% if request.args.get("sort") in [None, "stars"] %}selected{% endif %} value="stars">{{ _("Popularity stars") }}</option>
<option {% if request.args.get("sort") == "main_branch_update" %}selected{% endif %} value="main_branch_update">{{ _("Last update on main/master branch") }}</option>
<option {% if request.args.get("sort") == "testing_branch_update" %}selected{% endif %} value="testing_branch_update">{{ _("Last update on testing branch") }}</option>
<option {% if request.args.get("sort") == "nb_issues" %}selected{% endif %} value="nb_issues">{{ _("Number of opened issues") }}</option>
</select>
</div>
<div class="w-fit mx-auto flex items-center px-2 pt-2">
<label for="hidedeprecated" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<span class="sr-only">{{ _("Hide deprecated/unmaintained apps") }}</span>
<input type="checkbox" id="hidedeprecated" class="peer sr-only" {% if request.args.get("hidedeprecated") %}checked{% endif %} >
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span>
<span class="absolute inset-y-0 start-0 m-1 h-2 w-2 rounded-full bg-white transition-all peer-checked:start-3">
</span>
</label>
{{ _("Hide deprecated/unmaintained apps") }}
</div>
<div class="w-fit mx-auto flex items-center px-2 pt-2 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %} >
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span>
<span class="absolute inset-y-0 start-0 m-1 h-2 w-2 rounded-full bg-white transition-all peer-checked:start-3">
</span>
</label>
{{ _("Show only apps you starred") }}
</div>
</div>
<table id="appTable" class="mx-auto text-sm sm:text-base">
<tr class="h-40 md:h-20">
<th class="max-w-24 md:max-w-32">{{ _("App") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Catalog") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Main CI") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Bookworm CI") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Testing PR") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Autoupdate PR") }}</th>
<th class="-rotate-90 md:-rotate-45 max-w-10 md:max-w-16 text-left text-nowrap">{{ _("Issues") }}</th>
<th></th>
</tr>
{% for app, infos in data.items() %}
{% set this_app_stars = stars.get(app, {})|length %}
{% if user %}
{% set user_starred_this_app = user['id'] in stars.get(app, {}) %}
{% else %}
{% set user_starred_this_app = False %}
{% endif %}
<tr
class="app h-8 hover:bg-gray-100
{% if "deprecated-software" in infos["antifeatures"] or "replaced-by-another-app" in infos["antifeatures"] or "package-not-maintained" in infos["antifeatures"] %}
opacity-50
{% endif %}"
data-app="{{ app }}"
data-popularity-stars="{{ this_app_stars }}"
data-starred="{{ user_starred_this_app }}"
data-public-level="{{ infos["public_level"] if infos["public_level"] != "?" else -1 }}"
data-main-ci-level="{% if infos["ci_results"]["main"] %}{{ infos["ci_results"]["main"]["level"] }}{% else %}-1{% endif %}"
data-main-ci-daysago="{% if infos["ci_results"]["main"] %}{{ infos["ci_results"]["main"]["timestamp"] | days_ago }}{% else %}-9999{% endif %}"
data-nextdebian-ci-level="{% if infos["ci_results"]["nextdebian"] %}{{ infos["ci_results"]["nextdebian"]["level"] }}{% else %}-1{% endif %}"
data-last-update-master="{{ infos["ci_results"]["main"]["timestamp"] }}"
data-last-update-testing="{% if infos["testing"] %}{{ infos["testing"]["timestamp_updated"] }}{% else %}-1{% endif %}"
data-last-update-autoupdate="{% if infos["ci-auto-update"] %}{{ infos["ci-auto-update"]["timestamp_updated"] }}{% else %}-1{% endif %}"
data-packaging-format="{{ infos["packaging_format"] }}"
data-deprecated="{% if "deprecated-software" in infos["antifeatures"] or "replaced-by-another-app" in infos["antifeatures"] or "package-not-maintained" in infos["antifeatures"] %}True{% else %}False{% endif %}"
data-nb-issues="{{ infos["nb_issues"] }}"
>
<td class="truncate max-w-24 md:max-w-64 text-center text-blue-600 font-medium">
<a href="{{ infos["url"] }}">{{ app }}</a>
</td>
<td class="font-bold text-center">
<a href="{{ url_for('app_info', app_id=app) }}">
{{ infos["public_level"] }}
{% if infos["public_level"] == "?" %}
{% elif infos["public_level"] == 0 %}
<i class="fa fa-exclamation-circle text-red-500" title="{{ _("Broken") }}"></i>
{% elif infos["public_level"] <= 4 %}
<i class="fa fa-exclamation-triangle text-amber-500" title="{{ _("Low quality") }}"></i>
{% endif %}
</a>
</td>
<td class="border-l-2 border-gray-100 text-center">
<a class="{% if infos["public_level"] == infos["ci_results"]["main"]["level"] or (infos["ci_results"]["main"]["timestamp"] or -9999) | days_ago > 30 %}opacity-50{% endif %}" href="https://ci-apps.yunohost.org/ci/apps/{{ app }}/">
{% if infos["public_level"] == infos["ci_results"]["main"]["level"] %}
=
{% else %}
{{ infos["ci_results"]["main"]["level"] }}
{% endif %}
{% if (infos["ci_results"]["main"]["timestamp"] or -9999) | days_ago > 30 %}
<i class="fa fa-hourglass-o" title="{{ _("Outdated test (%(days)s days ago)", days=(infos["ci_results"]["main"]["timestamp"] or -9999) | days_ago) }}"></i>
{% endif %}
{% if infos["public_level"] == "?" %}
{% elif infos["ci_results"]["main"]["level"] < infos["public_level"] and infos["ci_results"]["main"]["level"] == 0 %}
<i class="fa fa-exclamation-circle text-red-500" title="{{ _("Broken") }}"></i>
{% elif infos["ci_results"]["main"]["level"] < infos["public_level"] and infos["ci_results"]["main"]["level"] <= 4 %}
<i class="fa fa-exclamation-triangle text-amber-500" title="{{ _("Low quality") }}"></i>
{% endif %}
</a>
</td>
<td class="text-center">
<a class="{% if infos["ci_results"]["nextdebian"] and ((infos["public_level"] == infos["ci_results"]["nextdebian"]["level"]) or (infos["ci_results"]["nextdebian"]["timestamp"] | days_ago) > 30) %}opacity-50{% endif %}" href="https://ci-apps-bookworm.yunohost.org/ci/apps/{{ app }}/">
{% if infos["ci_results"]["nextdebian"] %}
{% if infos["public_level"] == infos["ci_results"]["nextdebian"]["level"] %}
=
{% else %}
{{ infos["ci_results"]["nextdebian"]["level"] }}
{% endif %}
{% if infos["ci_results"]["nextdebian"]["timestamp"] | days_ago > 30 %}
<i class="fa fa-hourglass-o" title="{{ _("Outdated test (%(days)s days ago)", days=infos["ci_results"]["nextdebian"]["timestamp"] | days_ago) }}"></i>
{% endif %}
{% if infos["public_level"] == "?" %}
{% elif infos["ci_results"]["nextdebian"]["level"] < infos["public_level"] and infos["ci_results"]["nextdebian"]["level"] == 0 %}
<i class="fa fa-exclamation-circle text-red-500" title="{{ _("Broken") }}"></i>
{% elif infos["ci_results"]["nextdebian"]["level"] < infos["public_level"] and infos["ci_results"]["nextdebian"]["level"] <= 4 %}
<i class="fa fa-exclamation-triangle text-amber-500" title="{{ _("Low quality") }}"></i>
{% endif %}
{% else %}
?
{% endif %}
</a>
</td>
<td class="border-l-2 border-gray-100 text-center">
{% if "testing" in infos %}
<a href="{{ infos["testing"]["url"] }}">
<i class="fa fa-flask"></i>
{% for s in infos["testing"]["statuses"] %}
{% if s["context"] == "ci-apps-dev" %}
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-500{% endif %}"></i>
{% endif %}
{% endfor %}
{% if infos["testing"]["timestamp_updated"] | days_ago > 30 %}
<i class="text-gray-500 fa fa-clock-o" title="{{ _("Inactive (%(days)s days ago)", days=infos["testing"]["timestamp_updated"] | days_ago) }}"></i>
{% endif %}
</a>
{% endif %}
</td>
<td class="border-r-2 border-gray-100 text-center">
{% if "ci-auto-update" in infos %}
<a href="{{ infos["ci-auto-update"]["url"] }}">
<i class="fa fa-arrow-up"></i>
{% for s in infos["ci-auto-update"]["statuses"] %}
{% if s["context"] == "ci-apps-dev" %}
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-500{% endif %}"></i>
{% endif %}
{% endfor %}
{% if infos["ci-auto-update"]["timestamp_updated"] | days_ago > 30 %}
<i class="text-gray-500 fa fa-clock-o" title="{{ _("Inactive (%(days)s days ago)", days=infos["ci-auto-update"]["timestamp_updated"] | days_ago) }}"></i>
{% endif %}
</a>
{% endif %}
</td>
<td class="border-r-2 border-gray-100 text-center">
{% if infos["nb_issues"] != 0 %}
<a href="{{ infos["url"] }}/issues">
{{ infos["nb_issues"] }}
</a>
{% endif %}
</td>
<td class="px-3 truncate text-sm max-w-16 sm:max-w-full">
{% if this_app_stars > 0 %}
<span class="text-xs border-purple-400 text-purple-600 px-1 py-0 border rounded">{{ this_app_stars }}
<i class="fa {% if not user_starred_this_app %}fa-star-o{% else %}fa-star{% endif %}" aria-hidden="true" title="{{ _("Popularity stars") }}"></i>
</span>
{% endif %}
{% if infos["packaging_format"] == 1 %}
<span class="text-xs border-gray-400 px-1 py-0 border rounded"><i class="fa fa-meh-o"></i> {{ _("Packaging v1") }}</span>
{% endif %}
{% if "deprecated-software" in infos["antifeatures"] or "replaced-by-another-app" in infos["antifeatures"] %}
<span class="text-xs border-gray-400 px-1 py-0 border rounded"><i class="fa fa-flag-o"></i> {{ _("Deprecated") }}</span>
{% endif %}
{% if "package-not-maintained" in infos["antifeatures"] %}
<span class="text-xs border-gray-400 px-1 py-0 border rounded"><i class="fa fa-flag-o"></i> {{ _("Not maintained") }}</span>
{% endif %}
<!--
Maintainers
NB issues, PRs
Autoupdate enabled?
-->
</td>
</tr>
{% endfor %}
</table>
<div id="nbEntriesFound" class="text-center text-gray-600 text-sm py-3"></div>
<div class="text-center text-gray-600 text-sm py-3">{{ _("Last data update %(time)s ago", time=last_data_update|hours_ago) }}</div>
<script type="text/javascript">
function updateFilter() {
// Locate the card elements
let entries = document.getElementsByClassName('app')
let filterName = selectFilter.value.trim();
let nb_found = 0;
let starsOnly = toggleStarsonly.checked;
let hideDeprecated = toggleHidedeprecated.checked;
// Loop through the entries
for (var i = 0; i < entries.length; i++) {
if ((starsOnly) && (entries[i].dataset.starred != "True"))
{
entries[i].classList.add("hidden");
continue;
}
if ((hideDeprecated) && (entries[i].dataset.deprecated != "False"))
{
entries[i].classList.add("hidden");
continue;
}
if (filterName == "none")
{
entries[i].classList.remove("hidden");
nb_found++;
}
else if (filterName == "regressions_main_ci")
{
if (entries[i].dataset.publicLevel > entries[i].dataset.mainCiLevel)
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "broken_low_quality")
{
if (entries[i].dataset.publicLevel <= 4)
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "outdated")
{
if (entries[i].dataset.mainCiDaysago >= 30)
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "regressions_bookworm")
{
if ((entries[i].dataset.publicLevel >= 6) && (entries[i].dataset.nextdebianCiLevel < 6) && (entries[i].dataset.nextdebianCiLevel != entries[i].dataset.mainCiLevel))
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "testings")
{
if (parseInt(entries[i].dataset.lastUpdateTesting) > -1)
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "autoupdate")
{
if (parseInt(entries[i].dataset.lastUpdateAutoupdate) > -1)
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "packagingv1")
{
if (entries[i].dataset.packagingFormat == "1")
{
entries[i].classList.remove("hidden");
nb_found++;
}
else
{
entries[i].classList.add("hidden");
}
}
else if (filterName == "nb_issues")
{
if (entries[i].dataset.nbIssues > 0)
{
entries[i].classList.remove("hidden");
nb_found++
}
else
{
entries[i].classList.add("hidden");
}
}
}
document.getElementById('nbEntriesFound').innerHTML = "(" + nb_found + " apps)";
updateQueryArgsInURL()
}
function updateSort() {
let table = document.getElementById('appTable')
let toSort = document.getElementsByClassName('app')
let sortBy = selectSort.value.trim();
toSort = Array.prototype.slice.call(toSort, 0);
if (sortBy === "level") {
toSort.sort(function(a, b) {
return a.dataset.publicLevel - b.dataset.publicLevel;
});
}
else if (sortBy === "stars") {
toSort.sort(function(a, b) {
return parseInt(a.dataset.popularityStars) < parseInt(b.dataset.popularityStars) ? 1 : -1;
});
}
else if (sortBy === "main_branch_update") {
toSort.sort(function(a, b) {
return parseInt(a.dataset.lastUpdateMaster) < parseInt(b.dataset.lastUpdateMaster) ? 1 : -1;
});
}
else if (sortBy === "testing_branch_update") {
toSort.sort(function(a, b) {
return parseInt(a.dataset.lastUpdateTesting) < parseInt(b.dataset.lastUpdateTesting) ? 1 : -1;
});
}
else if (sortBy === "alpha") {
toSort.sort(function(a, b) {
return a.dataset.app > b.dataset.app ? 1 : -1;
});
}
else if (sortBy === "nb_issues") {
toSort.sort(function(a, b) {
return b.dataset.nbIssues - a.dataset.nbIssues;
});
}
for(var i = 0, l = toSort.length; i < l; i++) {
toSort[i].remove()
table.appendChild(toSort[i]);
}
updateQueryArgsInURL()
}
function updateQueryArgsInURL() {
let sortBy = selectSort.value.trim();
let filterName = selectFilter.value.trim();
let starsOnly = toggleStarsonly.checked;
let hideDeprecated = toggleHidedeprecated.checked;
if ('URLSearchParams' in window) {
var queryArgs = new URLSearchParams(window.location.search)
if (filterName != "none") { queryArgs.set("filter", filterName) } else { queryArgs.delete("filter"); };
if (sortBy != "stars") { queryArgs.set("sort", sortBy) } else { queryArgs.delete("sort"); };
if (starsOnly) { queryArgs.set("starsonly", true) } else { queryArgs.delete("starsonly"); };
if (hideDeprecated) { queryArgs.set("hidedeprecated", true) } else { queryArgs.delete("hidedeprecated"); };
let newUrl;
if (queryArgs.toString())
{
newUrl = window.location.pathname + '?' + queryArgs.toString();
}
else
{
newUrl = window.location.pathname;
}
if (newUrl != window.location.pathname + window.location.search)
{
history.pushState(null, '', newUrl);
}
}
}
let selectFilter = document.getElementById('selectfilter');
let selectSort = document.getElementById('selectsort');
let toggleStarsonly = document.getElementById('starsonly');
let toggleHidedeprecated = document.getElementById('hidedeprecated');
selectFilter.addEventListener('change', () => {
updateFilter();
});
selectSort.addEventListener('change', () => {
updateSort();
});
toggleStarsonly.addEventListener('change', () => {
updateFilter();
});
toggleHidedeprecated.addEventListener('change', () => {
updateFilter();
});
updateFilter();
updateSort();
</script>
{% endblock %}