Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Superuser page for all opportunities. #450

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions commcare_connect/opportunity/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from commcare_connect.opportunity import views
from commcare_connect.opportunity.views import (
AllOpportunitiesView,
OpportunityCompletedWorkTable,
OpportunityCreate,
OpportunityDeliverStatusTable,
Expand Down Expand Up @@ -54,6 +55,7 @@
app_name = "opportunity"
urlpatterns = [
path("", view=OpportunityList.as_view(), name="list"),
path("all_opportunities", view=AllOpportunitiesView.as_view(), name="all_opportunities"),
path("create/", view=OpportunityCreate.as_view(), name="create"),
path("init/", view=OpportunityInit.as_view(), name="init"),
path("<int:pk>/finalize/", view=OpportunityFinalize.as_view(), name="finalize"),
Expand Down
53 changes: 53 additions & 0 deletions commcare_connect/opportunity/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.forms import modelformset_factory
from django.http import FileResponse, Http404, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import mark_safe
Expand Down Expand Up @@ -122,6 +123,11 @@ def test_func(self):
) or self.request.user.is_superuser


class SuperUserMixin(LoginRequiredMixin, UserPassesTestMixin):
def test_func(self):
return self.request.user.is_superuser


def get_opportunity_or_404(pk, org_slug):
opp = get_object_or_404(Opportunity, id=pk)

Expand Down Expand Up @@ -1222,3 +1228,50 @@ def invoice_approve(request, org_slug, pk):
)
payment.save()
return HttpResponse(headers={"HX-Trigger": "newInvoice"})


class AllOpportunitiesView(SuperUserMixin, ListView):
model = Opportunity
paginate_by = 10
template_name = "opportunity/all_opportunities_view.html"

def get_queryset(self):
search_query = self.request.GET.get("search", "")
status_filter = self.request.GET.get("status", "")
ordering = self.request.GET.get("sort", "name")

valid_ordering = [
"name",
"-name",
"start_date",
"-start_date",
"end_date",
"-end_date",
]

if ordering not in valid_ordering:
ordering = "name"

opportunities = Opportunity.objects.all()

if search_query:
opportunities = opportunities.filter(name__icontains=search_query)

if status_filter:
if status_filter == "active":
opportunities = opportunities.filter(active=True, end_date__gte=now().date())
elif status_filter == "inactive":
opportunities = opportunities.filter(end_date__lt=now().date())

return opportunities.order_by(ordering)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["table"] = self.get_queryset()
return context

def render_to_response(self, context, **response_kwargs):
if self.request.htmx:
html = render_to_string("opportunity/partial_opportunity_table.html", context, request=self.request)
return HttpResponse(html)
return super().render_to_response(context, **response_kwargs)
52 changes: 52 additions & 0 deletions commcare_connect/templates/opportunity/all_opportunities_view.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{% extends "opportunity/base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{{ request.org }} - All Opportunities{% endblock %}

{% block breadcrumbs_inner %}
{{ block.super }}
<li class="breadcrumb-item active" aria-current="page">All Opportunities</li>
{% endblock %}

{% block content %}
<div class="container bg-white shadow">
<div class="mt-5 py-3">
<h1>All Opportunities</h1>
</div>
<div class="pb-4">
<form method="get"
hx-get="{% url 'opportunity:all_opportunities' org_slug=request.org.slug %}"
hx-target="#opportunity-table"
hx-push-url="true"
class="mb-3">
<div class="row g-3 align-items-center">
<div class="col-md-4">
<input type="text"
name="search"
value="{{ request.GET.search }}"
class="form-control"
placeholder="Search by name"
aria-label="Search opportunities">
</div>
<div class="col-md-3">
<select name="status" class="form-select">
<option value="">All</option>
<option value="active" {% if request.GET.status == 'active' %}selected{% endif %}>
Active
</option>
<option value="inactive" {% if request.GET.status == 'inactive' %}selected{% endif %}>
Inactive
</option>
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
</form>
<div id="opportunity-table">
{% include 'opportunity/partial_opportunity_table.html' %}
</div>
</div>
</div>
{% endblock content %}
75 changes: 8 additions & 67 deletions commcare_connect/templates/opportunity/opportunity_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
<div class="mt-5 py-3">
<h1> Opportunities
<span class="float-end">
{% if request.user.is_superuser %}
<a class="btn btn-info"
href="{% url 'opportunity:all_opportunities' request.org.slug %}"
title="View opportunities across organizations">
All Opportunities
</a>
{% endif %}
{% if request.org_membership.is_viewer %}
<button class="btn btn-primary" disabled>
<span class="bi bi-plus"></span> Add new
Expand All @@ -34,74 +41,8 @@ <h1> Opportunities
</h1>
</div>
<div class="pb-4">
<table class="table border table-responsive">
<thead class="table-light">
<tr>
<th>{% sort_link 'name' 'Name' %}</th>
<th>{% sort_link 'start_date' 'Start Date' %}</th>
<th>{% sort_link 'end_date' 'End Date' %}</th>
<th>Status</th>
<th>Program</th>
<th>Manage</th>
</tr>
</thead>
<tbody x-ref="tbody">
{% for opportunity in page_obj %}
<tr>
<td>{{ opportunity.name }}</td>
<td>{{ opportunity.start_date|default:"Not Set" }}</td>
<td>{{ opportunity.end_date|default:"Not Set" }}</td>
<td>
{% if opportunity.is_setup_complete %}
{% if opportunity.is_active %}
<span class="badge bg-success rounded-pill">Active</span>
{% else %}
<span class="badge bg-secondary text-white rounded-pill">Inactive</span>
{% endif %}
{% else %}
<span class="badge bg-warning text-white rounded-pill">Pending Setup</span>
{% endif %}
</td>
<td>{% if opportunity.managed %} {{ opportunity.managedopportunity.program.name }} {% else %} - {% endif %}</td>
<td width="300">
<div>
<a class="btn btn-primary btn-sm"
href="{% url 'opportunity:detail' org_slug=request.org.slug pk=opportunity.id %}">
<span class="bi bi-eye"></span><span class="d-none d-md-inline">&nbsp;View</span>
</a>
{% if request.org_membership.is_viewer %}
<button class="btn btn-warning btn-sm" disabled>
<span class="bi bi-pen"></span><span class="d-none d-md-inline">&nbsp;Edit</span>
</button>
{% if not opportunity.managed %}
<button class="btn btn-primary btn-sm" disabled>
<span class="bi bi-plus"></span><span class="d-none d-md-inline">&nbsp;Add Budget</span>
</button>
{% endif %}
{% else %}
<a class="btn btn-warning btn-sm"
href="{% url 'opportunity:edit' org_slug=request.org.slug pk=opportunity.id %}"><span
class="bi bi-pen"></span><span class="d-none d-md-inline">&nbsp;Edit</span></a>
{% if not opportunity.managed %}
<a class="btn btn-primary btn-sm"
href="{% url 'opportunity:add_budget_existing_users' org_slug=request.org.slug pk=opportunity.id %}"><span
class="bi bi-plus"></span><span class="d-none d-md-inline">&nbsp;Add Budget</span>
</a>
{% endif %}
{% endif %}
</div>
</td>

</tr>
{% empty %}
<tr>
<td colspan="3">{% translate "No opportunities yet." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include 'opportunity/partial_opportunity_table.html' %}
</div>
{% include 'pagination.html' %}
</div>

{% if program_invitation_table and program_invitation_table.data %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{% load sort_link %}
{% load django_tables2 %}
{% load i18n %}
<div d="opportunity-table">
<table class="table border table-responsive">
<thead class="table-light">
<tr>
<th>{% sort_link 'name' 'Name' %}</th>
<th>{% sort_link 'start_date' 'Start Date' %}</th>
<th>{% sort_link 'end_date' 'End Date' %}</th>
<th>Status</th>
<th>Program</th>
<th>Manage</th>
</tr>
</thead>
<tbody x-ref="tbody">
{% for opportunity in page_obj %}
<tr>
<td>{{ opportunity.name }}</td>
<td>{{ opportunity.start_date|default:"Not Set" }}</td>
<td>{{ opportunity.end_date|default:"Not Set" }}</td>
<td>
{% if opportunity.is_setup_complete %}
{% if opportunity.is_active %}
<span class="badge bg-success rounded-pill">Active</span>
{% else %}
<span class="badge bg-secondary text-white rounded-pill">Inactive</span>
{% endif %}
{% else %}
<span class="badge bg-warning text-white rounded-pill">Pending Setup</span>
{% endif %}
</td>
<td>{% if opportunity.managed %} {{ opportunity.managedopportunity.program.name }} {% else %} - {% endif %}</td>
<td width="300">
<div>
<a class="btn btn-primary btn-sm"
href="{% url 'opportunity:detail' org_slug=request.org.slug pk=opportunity.id %}">
<span class="bi bi-eye"></span><span class="d-none d-md-inline">&nbsp;View</span>
</a>
{% if request.org_membership.is_viewer %}
<button class="btn btn-warning btn-sm" disabled>
<span class="bi bi-pen"></span><span class="d-none d-md-inline">&nbsp;Edit</span>
</button>
{% if not opportunity.managed %}
<button class="btn btn-primary btn-sm" disabled>
<span class="bi bi-plus"></span><span class="d-none d-md-inline">&nbsp;Add Budget</span>
</button>
{% endif %}
{% else %}
<a class="btn btn-warning btn-sm"
href="{% url 'opportunity:edit' org_slug=request.org.slug pk=opportunity.id %}"><span
class="bi bi-pen"></span><span class="d-none d-md-inline">&nbsp;Edit</span></a>
{% if not opportunity.managed %}
<a class="btn btn-primary btn-sm"
href="{% url 'opportunity:add_budget_existing_users' org_slug=request.org.slug pk=opportunity.id %}"><span
class="bi bi-plus"></span><span class="d-none d-md-inline">&nbsp;Add Budget</span>
</a>
{% endif %}
{% endif %}
</div>
</td>

</tr>
{% empty %}
<tr>
<td colspan="3">{% translate "No opportunities yet." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div>
{% include 'pagination.html' %}
</div>
</div>
72 changes: 59 additions & 13 deletions commcare_connect/templates/pagination.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,67 @@
</li>
{% endif %}

{% for page_number in page_obj.paginator.page_range %}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to make this change because the previous pagination template displayed all the page numbers, and as the number of pages increased, it started overflowing.

{% if page_obj.number == page_number %}
<li class="page-item active">
<a class="page-link" href="#">
{{ page_number }}
</a>
</li>
{% with total_pages=page_obj.paginator.num_pages current_page=page_obj.number %}
{% if total_pages > 10 %}
{# First page #}
{% if current_page > 4 %}
<li class="page-item">
<a class="page-link" href="?{% update_query_params page=1 %}">1</a>
</li>
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}

{# Intelligent page range #}
{% with start=current_page|add:"-2" end=current_page|add:"2" %}
{% for page_number in page_obj.paginator.page_range %}
{% if page_number >= start and page_number <= end %}
{% if current_page == page_number %}
<li class="page-item active">
<a class="page-link" href="#">{{ page_number }}</a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?{% update_query_params page=page_number %}">
{{ page_number }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% endwith %}

{# Last page #}
{% if current_page < total_pages|add:"-3" %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
<li class="page-item">
<a class="page-link" href="?{% update_query_params page=total_pages %}">
{{ total_pages }}
</a>
</li>
{% endif %}

{% else %}
<li class="page-item">
<a class="page-link" href="?{% update_query_params page=page_number %}">
{{ page_number }}
</a>
</li>
{# If total pages are 10 or less, show all pages normally #}
{% for page_number in page_obj.paginator.page_range %}
{% if page_obj.number == page_number %}
<li class="page-item active">
<a class="page-link" href="#">{{ page_number }}</a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?{% update_query_params page=page_number %}">
{{ page_number }}
</a>
</li>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endwith %}


{% if page_obj.has_next %}
<li class="page-item">
Expand Down
Loading