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 %}