atr/templates/projects.html (166 lines of code) (raw):
{% extends "layouts/base.html" %}
{% block title %}
Projects ~ ATR
{% endblock title %}
{% block description %}
Directory of current ASF projects and their releases.
{% endblock description %}
{% block content %}
<h1>Projects</h1>
<p>Directory of current ASF projects and their releases:</p>
<div class="mb-3">
<input type="text"
id="project-filter"
class="form-control d-inline-block w-auto" />
<button type="button" class="btn btn-primary" id="filter-button">Filter</button>
<button type="button"
class="btn btn-secondary ms-2"
id="filter-participant-button"
data-showing="all">Show my projects</button>
</div>
<div class="mb-3">
<p>
Total count: <span id="project-count">{{ projects|length }}</span>
</p>
</div>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
{% for project in projects %}
{% set is_part = false %}
{% if current_user and project.committee %}
{% if current_user.uid in project.committee.committee_members or
current_user.uid in project.committee.committers or
current_user.uid in project.committee.release_managers %}
{% set is_part = true %}
{% endif %}
{% endif %}
<div class="col">
<div class="card h-100 shadow-sm atr-cursor-pointer page-project-card {{ 'bg-body-secondary' if project.is_retired else '' }}"
data-project-url="{{ as_url(routes.projects.view, name=project.name) }}"
data-is-participant="{{ 'true' if is_part else 'false' }}">
<div class="card-body">
<div class="row g-1">
<div class="col-sm">
<h3 class="card-title fs-4 mb-3">{{ project.display_name }}</h3>
</div>
{% if project.is_retired %}
<div class="col-sm-2">
<span class="badge text-bg-secondary">retired</span>
</div>
{% endif %}
</div>
{% if project.category %}
<div class="row g-1">
{% set categories = project.category.split(', ') %}
{% for category in categories if category != "retired" %}
<div class="col-sm-auto">
<span class="badge text-bg-primary">{{ category }}</span>
</div>
{% endfor %}
</div>
{% endif %}
{% if project.programming_languages %}
<div class="row g-1">
{% set langs = project.programming_languages.split(', ') %}
{% for lang in langs %}
<div class="col-sm-auto">
<span class="badge text-bg-success">{{ lang }}</span>
</div>
{% endfor %}
</div>
{% endif %}
{# TODO: Could add "or is_viewing_as_admin_fn(current_user.uid)" #}
{# But then the page is noisy for admins #}
{% if project.created_by == current_user.uid %}
<div class="mt-3">
<form method="post"
action="{{ as_url(routes.projects.delete) }}"
class="d-inline-block m-0"
onsubmit="return confirm('Are you sure you want to delete the project \'{{ project.display_name }}\'? This cannot be undone.');">
<input type="hidden" name="project_name" value="{{ project.name }}" />
<button type="submit"
class="btn btn-sm btn-outline-danger"
title="Delete {{ project.display_name }}">
<i class="bi bi-trash"></i> Delete project
</button>
</form>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
{% block javascripts %}
{{ super() }}
<script>
function filter() {
const projectFilter = document.getElementById("project-filter").value;
const cards = document.querySelectorAll(".page-project-card");
let visibleCount = 0;
for (let card of cards) {
const nameElement = card.querySelector(".card-title");
const name = nameElement.innerHTML;
if (!projectFilter) {
card.parentElement.hidden = false;
visibleCount++;
} else {
card.parentElement.hidden = !name.match(new RegExp(projectFilter, 'i'));
if (!card.parentElement.hidden) {
visibleCount++;
}
}
}
document.getElementById("project-count").textContent = visibleCount;
}
// Add event listeners
document.getElementById("filter-button").addEventListener("click", filter);
document.getElementById("project-filter").addEventListener("keydown", function(event) {
if (event.key === "Enter") {
filter();
event.preventDefault();
}
});
// Add click handlers for project cards
document.querySelectorAll(".page-project-card").forEach(function(card) {
card.addEventListener("click", function(event) {
// Prevent card navigation if click is inside a form
if (event.target.closest("form")) {
return;
}
window.location.href = this.getAttribute("data-project-url");
});
});
// Participant filter logic
const participantButton = document.getElementById("filter-participant-button");
participantButton.addEventListener("click", function() {
const showing = this.dataset.showing;
const cards = document.querySelectorAll(".page-project-card");
let visibleCount = 0;
if (showing === "all") {
// Switch to showing only participant projects
cards.forEach(card => {
const isParticipant = card.dataset.isParticipant === 'true';
card.parentElement.hidden = !isParticipant;
if (!card.parentElement.hidden) {
visibleCount++;
}
});
this.textContent = "Show all projects";
this.dataset.showing = "participant";
} else {
// Switch to showing all projects
cards.forEach(card => {
card.parentElement.hidden = false;
visibleCount++;
});
this.textContent = "Show my projects";
this.dataset.showing = "all";
}
// Reset text filter when toggling participant view
document.getElementById("project-filter").value = "";
// Update count
document.getElementById("project-count").textContent = visibleCount;
});
</script>
{% endblock javascripts %}