From 66a890708ae446ef9a31cbce7bd31a3293e808d5 Mon Sep 17 00:00:00 2001 From: Sravan Reddy Date: Mon, 14 Apr 2025 18:02:51 +0530 Subject: [PATCH 1/7] Implement invoice/payment report --- commcare_connect/opportunity/tables.py | 8 ++ commcare_connect/opportunity/urls.py | 68 ++++++----- commcare_connect/opportunity/views.py | 90 +++++++++++--- .../templates/tailwind/base_table.html | 52 +++++++- .../tailwind/pages/invoice_base.html | 42 +++++++ .../tailwind/pages/invoice_list.html | 113 ++---------------- .../pages/invoice_payment_report.html | 30 +++++ 7 files changed, 250 insertions(+), 153 deletions(-) create mode 100644 commcare_connect/templates/tailwind/pages/invoice_base.html create mode 100644 commcare_connect/templates/tailwind/pages/invoice_payment_report.html diff --git a/commcare_connect/opportunity/tables.py b/commcare_connect/opportunity/tables.py index cd3800e70..2e169e334 100644 --- a/commcare_connect/opportunity/tables.py +++ b/commcare_connect/opportunity/tables.py @@ -531,6 +531,14 @@ def render_payment_date(self, value): return +class TWPaymentInvoiceTable(PaymentInvoiceTable): + pk = None + + class Meta(PaymentInvoiceTable.Meta): + fields = ("amount", "date", "invoice_number", "service_delivery") + sequence = ("amount", "date", "invoice_number", "payment_status", "payment_date", "service_delivery") + + def popup_html(value, popup_title, popup_direction="top", popup_class="", popup_attributes=""): return format_html( "{}", diff --git a/commcare_connect/opportunity/urls.py b/commcare_connect/opportunity/urls.py index cb8e44c42..db620b1ef 100644 --- a/commcare_connect/opportunity/urls.py +++ b/commcare_connect/opportunity/urls.py @@ -54,10 +54,6 @@ user_visits_list, verification_flags_config, visit_verification, - worker_deliver, - worker_learn, - worker_list, - worker_verify, ) app_name = "opportunity" @@ -119,10 +115,6 @@ path("/suspend_user//", view=suspend_user, name="suspend_user"), path("/revoke_user_suspension//", view=revoke_user_suspension, name="revoke_user_suspension"), path("/delete_form_json_rule//", view=delete_form_json_rule, name="delete_form_json_rule"), - path("/workers/list/", view=worker_list, name="worker_list"), - path("/workers/learn/", view=worker_learn, name="worker_learn"), - path("/workers/deliver/", view=worker_deliver, name="worker_deliver"), - path("/workers/verify/", view=worker_verify, name="worker_verify"), path("/catchment_area_export/", view=export_catchment_area, name="catchment_area_export"), path("/catchment_area_import/", view=import_catchment_area, name="catchment_area_import"), path("/payment_report/", payment_report, name="payment_report"), @@ -142,40 +134,56 @@ path("/tw/opportunities/", tw_views.opportunities, name="tw_opportunities"), path("/tw/opportunities_list/", tw_views.opportunities_list, name="tw_opportunities"), path("/tw/pm_opportunities_list/", tw_views.pm_opportunities_list, name="tw_pm_opportunities"), - path("/tw/worker_learn/", tw_views.worker_learn, name="tw_worker_learn"), # TABLE + path("/tw/worker_learn/", tw_views.worker_learn, name="tw_worker_learn"), # TABLE path("/tw/worker_delivery/", tw_views.worker_delivery, name="tw_worker_deliver"), path("/tw/worker_payments/", tw_views.worker_payments, name="tw_opportunities"), # TABLE path("/tw/worker_main/", tw_views.worker_main, name="tw_opportunities"), # TABLE path("/tw/get_worker_last_payment/", tw_views.get_worker_last_payment, name="tw_opportunities"), path("/tw/workers_list/", tw_views.opportunity_worker, name="tw_opportunities"), - path("/tw/workers_list/learn_progress", tw_views.opportunity_worker_learn_progress, name="tw_opportunity_worker_learn_progress"), - path("/tw/workers_list/payments", tw_views.opportunity_worker_payment, name="tw_opportunity_worker_learn_progress"), - path("/tw/invoice_list/", tw_views.invoice_list, name="tw_invoice_list"), + path( + "/tw/workers_list/learn_progress", + tw_views.opportunity_worker_learn_progress, + name="tw_opportunity_worker_learn_progress", + ), + path( + "/tw/workers_list/payments", + tw_views.opportunity_worker_payment, + name="tw_opportunity_worker_learn_progress", + ), + path("/tw/invoice_list/", views.tw_invoice_list, name="tw_invoice_list"), + path("/tw/invoice_payment_report/", views.tw_payment_report, name="tw_payment_report"), path("/tw/my_organization/", tw_views.my_organization, name="tw_my_organization"), path("/tw/add_budget/", tw_views.add_budget, name="tw_add_budget"), - path("/tw/visits/", tw_views.opportunity_visits, name="tw_visits"), # TODO - path("/tw/create/", tw_views.create_opportunity, name="tw_visits"), # TODO + path("/tw/visits/", tw_views.opportunity_visits, name="tw_visits"), # TODO + path("/tw/create/", tw_views.create_opportunity, name="tw_visits"), # TODO # Tables path("/tw/tables/flagged_workers/", tw_views.flagged_workers, name="tw_flagged_workers"), - path("/tw/tables/opportunities_list/", tw_views.opportunities_list_table_view, name="tw_flagged_workers"), # TODO - path("/tw/tables/pm_opportunities_list/", tw_views.pm_opportunities_list_table_view, name="pm_opp_list"), + path( + "/tw/tables/opportunities_list/", tw_views.opportunities_list_table_view, name="tw_flagged_workers" + ), # TODO + path( + "/tw/tables/pm_opportunities_list/", tw_views.pm_opportunities_list_table_view, name="pm_opp_list" + ), path("/tw/tables/pay_worker", tw_views.pay_worker, name="tw_pay_worker"), - path("/tw/tables/all_invoice", tw_views.all_invoice_table, name="tw_all_invoice"), - path("/tw/tables/invoice_report", tw_views.invoice_report_table, name="tw_invoice_report"), - path("/tw/tables/my_organization_members", tw_views.my_organization_members_table, name="tw_my_organization_members_table"), - path("/tw/tables/learn_app",tw_views.learn_app_table, name="tw_learn_app"), - path("/tw/tables/delivery_app",tw_views.delivery_app_table, name="tw_delivery_app"), - path("/tw/tables/payment_app",tw_views.payment_app_table, name="tw_payment_app"), - path("/tw/tables/worker_flagged_table",tw_views.worker_flagged_table, name="tw_flagged_table"), - path("/tw/tables/worker_review_table",tw_views.worker_review_table, name="tw_review_table"), - path("/tw/tables/worker_revalidate_table",tw_views.worker_revalidate_table, name="tw_revalidate_table"), - path("/tw/tables/worker_approved_table",tw_views.worker_approved_table, name="tw_approved_table"), - path("/tw/tables/worker_rejected_table",tw_views.worker_rejected_table, name="tw_rejected_table"), - path("/tw/tables/worker_all_table",tw_views.worker_all_table, name="tw_all_table"), + path( + "/tw/tables/my_organization_members", + tw_views.my_organization_members_table, + name="tw_my_organization_members_table", + ), + path("/tw/tables/learn_app", tw_views.learn_app_table, name="tw_learn_app"), + path("/tw/tables/delivery_app", tw_views.delivery_app_table, name="tw_delivery_app"), + path("/tw/tables/payment_app", tw_views.payment_app_table, name="tw_payment_app"), + path("/tw/tables/worker_flagged_table", tw_views.worker_flagged_table, name="tw_flagged_table"), + path("/tw/tables/worker_review_table", tw_views.worker_review_table, name="tw_review_table"), + path( + "/tw/tables/worker_revalidate_table", tw_views.worker_revalidate_table, name="tw_revalidate_table" + ), + path("/tw/tables/worker_approved_table", tw_views.worker_approved_table, name="tw_approved_table"), + path("/tw/tables/worker_rejected_table", tw_views.worker_rejected_table, name="tw_rejected_table"), + path("/tw/tables/worker_all_table", tw_views.worker_all_table, name="tw_all_table"), # API - path("/tw/api/payment_app_expand",tw_views.payment_app_table_expand, name="tw_payment_app"), + path("/tw/api/payment_app_expand", tw_views.payment_app_table_expand, name="tw_payment_app"), path("/tw/api/payment_history/", tw_views.payment_history, name="tw_payment_history"), path("/tw/api/opportunities/", tw_views.opportunities_card, name="tw_opportunities_card"), - path("/tw/api/invoice_report_card/", tw_views.invoice_report_card, name="tw_invoice_report_card"), path("/tw/api/pm_cards/", tw_views.pm_opportunities_card, name="tw_pm_cards"), ] diff --git a/commcare_connect/opportunity/views.py b/commcare_connect/opportunity/views.py index 4faaa96ae..46b7e28c0 100644 --- a/commcare_connect/opportunity/views.py +++ b/commcare_connect/opportunity/views.py @@ -86,6 +86,7 @@ PaymentReportTable, PaymentUnitTable, SuspendedUsersTable, + TWPaymentInvoiceTable, UserPaymentsTable, UserStatusTable, UserVisitFilter, @@ -1263,6 +1264,60 @@ def payment_report(request, org_slug, pk): ) +@org_member_required +def tw_payment_report(request, org_slug, pk): + opportunity = get_opportunity_or_404(pk, org_slug) + if not opportunity.managed: + return redirect("opportunity:detail", org_slug, pk) + total_paid_users = ( + Payment.objects.filter(opportunity_access__opportunity=opportunity).aggregate(total=Sum("amount"))["total"] + or 0 + ) + total_paid_nm = ( + Payment.objects.filter(organization=opportunity.organization).aggregate(total=Sum("amount"))["total"] or 0 + ) + data, total_user_payment_accrued, total_nm_payment_accrued = get_payment_report_data(opportunity) + table = PaymentReportTable(data) + RequestConfig(request, paginate={"per_page": 2}).configure(table) + + cards = [ + { + "amount": f"{opportunity.currency} {total_user_payment_accrued}", + "icon": "fa-user-friends", + "label": "Worker", + "subtext": "Total Accrued", + }, + { + "amount": f"{opportunity.currency} {total_paid_users}", + "icon": "fa-user-friends", + "label": "Worker", + "subtext": "Total Paid", + }, + { + "amount": f"{opportunity.currency} {total_nm_payment_accrued}", + "icon": "fa-building", + "label": "Organization", + "subtext": "Total Accrued", + }, + { + "amount": f"{opportunity.currency} {total_paid_nm}", + "icon": "fa-building", + "label": "Organization", + "subtext": "Total Paid", + }, + ] + + return render( + request, + "tailwind/pages/invoice_payment_report.html", + context=dict( + table=table, + opportunity=opportunity, + cards=cards, + ), + ) + + class PaymentInvoiceTableView(OrganizationUserMixin, SingleTableView): model = PaymentInvoice paginate_by = 25 @@ -1298,6 +1353,25 @@ def invoice_list(request, org_slug, pk): ) +@org_member_required +def tw_invoice_list(request, org_slug=None, pk=None): + opportunity = get_opportunity_or_404(pk, org_slug) + if not opportunity.managed: + return redirect("opportunity:detail", org_slug, pk) + + filter_kwargs = dict(opportunity=opportunity) + + queryset = PaymentInvoice.objects.filter(**filter_kwargs).order_by("date") + table = TWPaymentInvoiceTable(queryset) + + RequestConfig(request, paginate={"per_page": 2}).configure(table) + return render( + request, + "tailwind/pages/invoice_list.html", + {"header_title": "Invoices", "opportunity": opportunity, "table": table}, + ) + + @org_member_required def invoice_create(request, org_slug, pk): opportunity = get_opportunity_or_404(pk, org_slug) @@ -1366,21 +1440,6 @@ def resend_user_invite(request, org_slug, opp_id, pk): return HttpResponse("The invitation has been successfully resent to the user.") -def worker_list(request, org_slug=None, opp_id=None): - return render(request, "opportunity/tailwind/workers.html") - - -def worker_learn(request, org_slug=None, opp_id=None): - return render(request, "opportunity/tailwind/learn.html") - - -def worker_deliver(request, org_slug=None, opp_id=None): - return render(request, "opportunity/tailwind/deliver.html") - - -def worker_verify(request, org_slug=None, opp_id=None): - return render(request, "opportunity/tailwind/verify.html") - def sync_deliver_units(request, org_slug, opp_id): status = HTTPStatus.OK @@ -1392,4 +1451,3 @@ def sync_deliver_units(request, org_slug, opp_id): message = "Failed to retrieve updates. No available build at the moment." return HttpResponse(content=message, status=status) - diff --git a/commcare_connect/templates/tailwind/base_table.html b/commcare_connect/templates/tailwind/base_table.html index 28926457c..c47766d82 100644 --- a/commcare_connect/templates/tailwind/base_table.html +++ b/commcare_connect/templates/tailwind/base_table.html @@ -1,7 +1,7 @@ {% load django_tables2 %} {% load i18n %} {% block table-wrapper %} -
+
{% block table %}
@@ -64,5 +64,53 @@
{% endblock table %} -
+ {% block pagination %} + {% if table.page and table.paginator.num_pages > 1 %} + + {% endif %} + + {% endblock pagination %} +
{% endblock table-wrapper %} diff --git a/commcare_connect/templates/tailwind/pages/invoice_base.html b/commcare_connect/templates/tailwind/pages/invoice_base.html new file mode 100644 index 000000000..85a37b871 --- /dev/null +++ b/commcare_connect/templates/tailwind/pages/invoice_base.html @@ -0,0 +1,42 @@ +{% extends "tailwind/base.html" %} +{% load django_tables2 %} + +{% block content %} +

{{ opportunity.name }}

+
+ +
+ {% with list_view_name='opportunity:tw_invoice_list' report_view_name='opportunity:tw_payment_report' current_view_name=request.resolver_match.view_name %} +
    +
  • + {# Invisible link overlay for click #} + All Invoices + {% if current_view_name == list_view_name %}
    {% endif %} +
  • + +
  • + {# Invisible link overlay for click #} + Payment Report + {% if current_view_name == report_view_name %}
    {% endif %} +
  • +
+ +
+ + {% block top_buttons %} + {% endblock top_buttons %} +
+ {% endwith %} +
+ + {% block report_cards %}{% endblock report_cards %} + +
+ {% block table_content %} + {% render_table table %} + {% endblock table_content %} +
+
+{% endblock content %} diff --git a/commcare_connect/templates/tailwind/pages/invoice_list.html b/commcare_connect/templates/tailwind/pages/invoice_list.html index f4a8c32ae..c0824a89e 100644 --- a/commcare_connect/templates/tailwind/pages/invoice_list.html +++ b/commcare_connect/templates/tailwind/pages/invoice_list.html @@ -1,105 +1,8 @@ -{% extends "tailwind/base.html" %} {% block content %} -

Opportunity Name

-
- -
-
    -
  • - All Invoices -
    -
  • -
  • - Payment Report -
    -
  • -
- - -
- - -
- - -
- -
-
- - -
- - -
-

All Invoices

-
- - -
- -
-{% endblock content %} +{% extends "tailwind/pages/invoice_base.html" %} + +{% block top_buttons %} + {{ block.super }} + {% if not request.org_membership.is_program_manager %} + + {% endif %} +{% endblock top_buttons %} diff --git a/commcare_connect/templates/tailwind/pages/invoice_payment_report.html b/commcare_connect/templates/tailwind/pages/invoice_payment_report.html new file mode 100644 index 000000000..d2a254f66 --- /dev/null +++ b/commcare_connect/templates/tailwind/pages/invoice_payment_report.html @@ -0,0 +1,30 @@ +{% extends "tailwind/pages/invoice_base.html" %} +{% load django_tables2 %} {# Load needed if render_table is used within overridden blocks #} + + +{% block report_cards %} +
+
+ {% for card in cards %} +
+
+
{{ card.amount }}
+ +
+
+
+ {{ card.label }} | {{ card.subtext }} +
+ +
+
+ {% endfor %} +
+
+{% endblock report_cards %} + +{% block table_section_header %} +
+

All Invoices

+
+{% endblock table_section_header %} From cf746e2cf657502d7e55a482c1a63d5abcab84c5 Mon Sep 17 00:00:00 2001 From: Sravan Reddy Date: Wed, 16 Apr 2025 10:11:08 +0530 Subject: [PATCH 2/7] Fix forms --- commcare_connect/opportunity/tw_form_views.py | 11 +- commcare_connect/opportunity/tw_forms.py | 18 +- commcare_connect/opportunity/urls.py | 2 +- .../templates/account/account_inactive.html | 11 + commcare_connect/templates/account/base.html | 10 + commcare_connect/templates/account/email.html | 86 +++ .../templates/account/email_confirm.html | 31 + commcare_connect/templates/account/login.html | 63 ++ .../templates/account/logout.html | 19 + .../templates/account/password_change.html | 16 + .../templates/account/password_reset.html | 26 + .../account/password_reset_done.html | 16 + .../account/password_reset_from_key.html | 24 + .../account/password_reset_from_key_done.html | 9 + .../templates/account/password_set.html | 18 + .../templates/account/signup.html | 26 + .../templates/account/signup_closed.html | 11 + .../templates/account/verification_sent.html | 12 + .../account/verified_email_required.html | 21 + .../opportunity/add_payment_units.html | 27 + .../add_visits_existing_users.html | 147 ++++ .../templates/opportunity/base.html | 22 + .../templates/opportunity/invoice_list.html | 110 +++ .../opportunity/opportunity_create.html | 22 + .../opportunity/opportunity_detail.html | 662 ++++++++++++++++++ .../opportunity/opportunity_edit.html | 24 + .../opportunity/opportunity_finalize.html | 10 + .../opportunity/opportunity_init.html | 10 + .../opportunity/opportunity_list.html | 117 ++++ .../opportunity_user_payments_list.html | 51 ++ .../templates/opportunity/payment_report.html | 57 ++ .../templates/opportunity/send_message.html | 74 ++ .../opportunity/suspended_users.html | 26 + .../opportunity/upload_progress.html | 23 + .../opportunity/user_learn_progress.html | 39 ++ .../templates/opportunity/user_profile.html | 228 ++++++ .../opportunity/user_visit_review.html | 52 ++ .../opportunity/user_visits_list.html | 23 + .../verification_flags_config.html | 121 ++++ .../opportunity/visit_verification.html | 355 ++++++++++ .../organization/organization_create.html | 23 + .../organization/organization_home.html | 62 ++ commcare_connect/templates/pages/about.html | 1 + commcare_connect/templates/pages/home.html | 1 + .../templates/program/application_list.html | 69 ++ commcare_connect/templates/program/base.html | 22 + .../templates/program/dashboard.html | 78 +++ .../templates/program/program_add.html | 9 + .../templates/program/program_edit.html | 24 + .../templates/program/program_list.html | 26 + commcare_connect/templates/reports/admin.html | 13 + .../templates/reports/dashboard.html | 428 +++++++++++ .../templates/reports/htmx_table.html | 87 +++ .../templates/reports/report_table.html | 57 ++ commcare_connect/templates/steps.html | 21 +- .../templates/tables/single_table.html | 4 + .../templates/tables/tabbed_table.html | 45 ++ .../templates/tables/table_manage_action.html | 23 + .../templates/tables/table_placeholder.html | 16 + .../tailwind/pages/add_payment_units.html | 2 +- .../tailwind/pages/opportunity_finalize.html | 2 +- .../tailwind/pages/opportunity_init.html | 2 +- .../templates/tailwind/pages/steps.html | 19 + .../pages/verification_flags_config.html | 5 +- 64 files changed, 3639 insertions(+), 30 deletions(-) create mode 100644 commcare_connect/templates/account/account_inactive.html create mode 100644 commcare_connect/templates/account/base.html create mode 100644 commcare_connect/templates/account/email.html create mode 100644 commcare_connect/templates/account/email_confirm.html create mode 100644 commcare_connect/templates/account/login.html create mode 100644 commcare_connect/templates/account/logout.html create mode 100644 commcare_connect/templates/account/password_change.html create mode 100644 commcare_connect/templates/account/password_reset.html create mode 100644 commcare_connect/templates/account/password_reset_done.html create mode 100644 commcare_connect/templates/account/password_reset_from_key.html create mode 100644 commcare_connect/templates/account/password_reset_from_key_done.html create mode 100644 commcare_connect/templates/account/password_set.html create mode 100644 commcare_connect/templates/account/signup.html create mode 100644 commcare_connect/templates/account/signup_closed.html create mode 100644 commcare_connect/templates/account/verification_sent.html create mode 100644 commcare_connect/templates/account/verified_email_required.html create mode 100644 commcare_connect/templates/opportunity/add_payment_units.html create mode 100644 commcare_connect/templates/opportunity/add_visits_existing_users.html create mode 100644 commcare_connect/templates/opportunity/base.html create mode 100644 commcare_connect/templates/opportunity/invoice_list.html create mode 100644 commcare_connect/templates/opportunity/opportunity_create.html create mode 100644 commcare_connect/templates/opportunity/opportunity_detail.html create mode 100644 commcare_connect/templates/opportunity/opportunity_edit.html create mode 100644 commcare_connect/templates/opportunity/opportunity_finalize.html create mode 100644 commcare_connect/templates/opportunity/opportunity_init.html create mode 100644 commcare_connect/templates/opportunity/opportunity_list.html create mode 100644 commcare_connect/templates/opportunity/opportunity_user_payments_list.html create mode 100644 commcare_connect/templates/opportunity/payment_report.html create mode 100644 commcare_connect/templates/opportunity/send_message.html create mode 100644 commcare_connect/templates/opportunity/suspended_users.html create mode 100644 commcare_connect/templates/opportunity/upload_progress.html create mode 100644 commcare_connect/templates/opportunity/user_learn_progress.html create mode 100644 commcare_connect/templates/opportunity/user_profile.html create mode 100644 commcare_connect/templates/opportunity/user_visit_review.html create mode 100644 commcare_connect/templates/opportunity/user_visits_list.html create mode 100644 commcare_connect/templates/opportunity/verification_flags_config.html create mode 100644 commcare_connect/templates/opportunity/visit_verification.html create mode 100644 commcare_connect/templates/organization/organization_create.html create mode 100644 commcare_connect/templates/organization/organization_home.html create mode 100644 commcare_connect/templates/pages/about.html create mode 100644 commcare_connect/templates/pages/home.html create mode 100644 commcare_connect/templates/program/application_list.html create mode 100644 commcare_connect/templates/program/base.html create mode 100644 commcare_connect/templates/program/dashboard.html create mode 100644 commcare_connect/templates/program/program_add.html create mode 100644 commcare_connect/templates/program/program_edit.html create mode 100644 commcare_connect/templates/program/program_list.html create mode 100644 commcare_connect/templates/reports/admin.html create mode 100644 commcare_connect/templates/reports/dashboard.html create mode 100644 commcare_connect/templates/reports/htmx_table.html create mode 100644 commcare_connect/templates/reports/report_table.html create mode 100644 commcare_connect/templates/tables/single_table.html create mode 100644 commcare_connect/templates/tables/tabbed_table.html create mode 100644 commcare_connect/templates/tables/table_manage_action.html create mode 100644 commcare_connect/templates/tables/table_placeholder.html create mode 100644 commcare_connect/templates/tailwind/pages/steps.html diff --git a/commcare_connect/opportunity/tw_form_views.py b/commcare_connect/opportunity/tw_form_views.py index 663f29f5f..b3c518533 100644 --- a/commcare_connect/opportunity/tw_form_views.py +++ b/commcare_connect/opportunity/tw_form_views.py @@ -111,6 +111,10 @@ class OpportunityEdit(views.OpportunityEdit): template_name = "tailwind/pages/opportunity_edit.html" form_class = OpportunityChangeForm + def get_success_url(self): + return reverse("opportunity:tw_opportunity_detail", args=(self.request.org.slug, self.object.id)) + + @override_settings(CRISPY_TEMPLATE_PACK="tailwind") @org_member_required @@ -226,7 +230,7 @@ def send_message_mobile_users(request, org_slug=None, pk=None): send_push_notification_task.delay(selected_user_ids, title, body) if "sms" in message_type: send_sms_task.delay(selected_user_ids, body) - return redirect("opportunity:detail", org_slug=request.org.slug, pk=pk) + return redirect("opportunity:tw_opportunity_detail", request.org.slug, pk) return render( request, @@ -317,6 +321,11 @@ def get_template_names(self): template = f"tailwind/pages/program_{view}.html" return template + def get_success_url(self): + # Todo; update with tailwind URL + return reverse("program:list", kwargs={"org_slug": self.request.org.slug}) + + def invite_organization(request, org_slug=None, pk=None): return render(request, "tailwind/pages/invite_organization.html") diff --git a/commcare_connect/opportunity/tw_forms.py b/commcare_connect/opportunity/tw_forms.py index 36b1e5b2e..25a6800db 100644 --- a/commcare_connect/opportunity/tw_forms.py +++ b/commcare_connect/opportunity/tw_forms.py @@ -7,8 +7,9 @@ from django.core.exceptions import ValidationError from django.db.models import Q, Sum from django.utils.timezone import now +from django.urls import reverse -from commcare_connect.connect_id_client.models import Credential +from commcare_connect import connect_id_client from commcare_connect.opportunity.forms import FILTER_COUNTRIES, DateRanges from commcare_connect.organization.models import Organization from commcare_connect.program.models import ManagedOpportunity, Program @@ -213,7 +214,7 @@ def __init__(self, *args, **kwargs): class OpportunityUserInviteForm(forms.Form): def __init__(self, *args, **kwargs): org_slug = kwargs.pop("org_slug", None) - credentials = [Credential(org_slug, org_slug)] # connect_id_client.fetch_credentials(org_slug) + credentials = connect_id_client.fetch_credentials(org_slug) super().__init__(*args, **kwargs) self.helper = TailwindFormHelper(self) @@ -586,7 +587,6 @@ def __init__(self, *args, **kwargs): Row( HTML("
Apps
Add required apps to the opportunity. All fields are mandatory.
"), Column( - "Learn App", Field("learn_app_domain", css_class=SELECT_CLASS), Field("learn_app", css_class=SELECT_CLASS), Field("learn_app_description", css_class=TEXTAREA_CLASS), @@ -594,7 +594,6 @@ def __init__(self, *args, **kwargs): data_loading_states=True, ), Column( - "Deliver App", Field("deliver_app_domain", css_class=SELECT_CLASS), Field("deliver_app", css_class=SELECT_CLASS), data_loading_states=True, @@ -607,10 +606,19 @@ def __init__(self, *args, **kwargs): ) ) - domain_choices = [(i, f"Domain {i}") for i in range(1, 11)] + domain_choices = [(domain, domain) for domain in self.domains] self.fields["description"] = forms.CharField(widget=forms.Textarea(attrs={"rows": 3})) self.fields["learn_app_domain"] = forms.ChoiceField( choices=domain_choices, + widget=forms.Select( + attrs={ + "hx-get": reverse("opportunity:get_applications_by_domain", args=(self.org_slug,)), + "hx-include": "#id_learn_app_domain", + "hx-trigger": "load delay:0.3s, change", + "hx-target": "#id_learn_app", + "data-loading-disable": True, + } + ), ) learn_app_choices = [(i, f"Learn App {i}") for i in range(1, 11)] self.fields["learn_app"] = forms.Field(widget=forms.Select(choices=learn_app_choices)) diff --git a/commcare_connect/opportunity/urls.py b/commcare_connect/opportunity/urls.py index 2edfbb719..13264812c 100644 --- a/commcare_connect/opportunity/urls.py +++ b/commcare_connect/opportunity/urls.py @@ -131,7 +131,7 @@ path("/tw/dashboard/", tw_views.dashboard, name="tw_worker"), path("/tw/pm_dashboard/", tw_views.pm_dashboard, name="pm_dashboard"), path("/tw/worker/", tw_views.worker, name="tw_worker"), - path("/tw/opportunities/", tw_views.opportunities, name="tw_opportunities"), + path("/tw/opportunities/", tw_views.opportunities, name="tw_opportunity_detail"), path("/tw/opportunities_list/", tw_views.opportunities_list, name="tw_opportunities"), path("/tw/pm_opportunities_list/", tw_views.pm_opportunities_list, name="tw_pm_opportunities"), path("/tw/worker_learn/", tw_views.worker_learn, name="tw_worker_learn"), # TABLE diff --git a/commcare_connect/templates/account/account_inactive.html b/commcare_connect/templates/account/account_inactive.html new file mode 100644 index 000000000..4f29ce3e2 --- /dev/null +++ b/commcare_connect/templates/account/account_inactive.html @@ -0,0 +1,11 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %}{% translate "Account Inactive" %}{% endblock %} + +{% block inner %} +

{% translate "Account Inactive" %}

+ +

{% translate "This account is inactive." %}

+{% endblock %} diff --git a/commcare_connect/templates/account/base.html b/commcare_connect/templates/account/base.html new file mode 100644 index 000000000..2ad74a4ee --- /dev/null +++ b/commcare_connect/templates/account/base.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block title %}{% block head_title %}{% endblock head_title %}{% endblock title %} + +{% block content %} +
+
+ {% block inner %}{% endblock %} +
+
+{% endblock %} diff --git a/commcare_connect/templates/account/email.html b/commcare_connect/templates/account/email.html new file mode 100644 index 000000000..9e66e0033 --- /dev/null +++ b/commcare_connect/templates/account/email.html @@ -0,0 +1,86 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %}{% translate "Account" %}{% endblock %} + +{% block inner %} +

{% translate "E-mail Addresses" %}

+ + {% if user.emailaddress_set.all %} +

{% translate 'The following e-mail addresses are associated with your account:' %}

+ + + + {% else %} +

+ {% translate 'Warning:' %} {% translate "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %} +

+ + {% endif %} + + +
{% translate "Add E-mail Address" %}
+ +
+ {% csrf_token %} + {{ form|crispy }} + +
+ +{% endblock %} + + +{% block inline_javascript %} + {{ block.super }} + +{% endblock %} diff --git a/commcare_connect/templates/account/email_confirm.html b/commcare_connect/templates/account/email_confirm.html new file mode 100644 index 000000000..dcdeedd5e --- /dev/null +++ b/commcare_connect/templates/account/email_confirm.html @@ -0,0 +1,31 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account %} + +{% block head_title %}{% translate "Confirm E-mail Address" %}{% endblock %} + + +{% block inner %} +

{% translate "Confirm E-mail Address" %}

+ +{% if confirmation %} + +{% user_display confirmation.email_address.user as user_display %} + +

{% blocktranslate with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktranslate %}

+ +
+{% csrf_token %} + +
+ +{% else %} + +{% url 'account_email' as email_url %} + +

{% blocktranslate %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktranslate %}

+ +{% endif %} + +{% endblock %} diff --git a/commcare_connect/templates/account/login.html b/commcare_connect/templates/account/login.html new file mode 100644 index 000000000..cd6535f83 --- /dev/null +++ b/commcare_connect/templates/account/login.html @@ -0,0 +1,63 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account socialaccount %} +{% load crispy_forms_tags %} + +{% block head_title %}{% translate "Sign In" %}{% endblock %} + +{% block inner %} + +

{% translate "Sign In" %}

+ +{% get_providers as socialaccount_providers %} + +{% if socialaccount_providers %} +

+ {% translate "Please sign in with one of your existing third party accounts:" %} + {% if ACCOUNT_ALLOW_REGISTRATION %} + {% blocktranslate trimmed %} + Or, sign up + for a {{ site_name }} account and sign in below: + {% endblocktranslate %} + {% endif %} +

+ +
+ +
    + {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
+ + + +
+ + {% include "socialaccount/snippets/login_extra.html" %} + +{% else %} + {% if ACCOUNT_ALLOW_REGISTRATION %} +

+ {% blocktranslate trimmed %} + If you have not created an account yet, then please + sign up first. + {% endblocktranslate %} +

+ {% endif %} +{% endif %} + + + +{% endblock %} diff --git a/commcare_connect/templates/account/logout.html b/commcare_connect/templates/account/logout.html new file mode 100644 index 000000000..be4765598 --- /dev/null +++ b/commcare_connect/templates/account/logout.html @@ -0,0 +1,19 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %}{% translate "Sign Out" %}{% endblock %} + +{% block inner %} +

{% translate "Sign Out" %}

+ +

{% translate 'Are you sure you want to sign out?' %}

+ +
+ {% csrf_token %} + {% if redirect_field_value %} + + {% endif %} + +
+{% endblock %} diff --git a/commcare_connect/templates/account/password_change.html b/commcare_connect/templates/account/password_change.html new file mode 100644 index 000000000..5182a7a18 --- /dev/null +++ b/commcare_connect/templates/account/password_change.html @@ -0,0 +1,16 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %}{% translate "Change Password" %}{% endblock %} + +{% block inner %} +

{% translate "Change Password" %}

+ +
+ {% csrf_token %} + {{ form|crispy }} + +
+{% endblock %} diff --git a/commcare_connect/templates/account/password_reset.html b/commcare_connect/templates/account/password_reset.html new file mode 100644 index 000000000..ddb331415 --- /dev/null +++ b/commcare_connect/templates/account/password_reset.html @@ -0,0 +1,26 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account %} +{% load crispy_forms_tags %} + +{% block head_title %}{% translate "Password Reset" %}{% endblock %} + +{% block inner %} + +

{% translate "Password Reset" %}

+ {% if user.is_authenticated %} + {% include "account/snippets/already_logged_in.html" %} + {% endif %} + +

{% translate "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

+ +
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+
+

{% blocktranslate %}Please contact us if you have any trouble resetting your password.{% endblocktranslate %}

+{% endblock %} diff --git a/commcare_connect/templates/account/password_reset_done.html b/commcare_connect/templates/account/password_reset_done.html new file mode 100644 index 000000000..3b01f8128 --- /dev/null +++ b/commcare_connect/templates/account/password_reset_done.html @@ -0,0 +1,16 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load account %} + +{% block head_title %}{% translate "Password Reset" %}{% endblock %} + +{% block inner %} +

{% translate "Password Reset" %}

+ + {% if user.is_authenticated %} + {% include "account/snippets/already_logged_in.html" %} + {% endif %} + +

{% blocktranslate %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktranslate %}

+{% endblock %} diff --git a/commcare_connect/templates/account/password_reset_from_key.html b/commcare_connect/templates/account/password_reset_from_key.html new file mode 100644 index 000000000..443c3487a --- /dev/null +++ b/commcare_connect/templates/account/password_reset_from_key.html @@ -0,0 +1,24 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} +{% block head_title %}{% translate "Change Password" %}{% endblock %} + +{% block inner %} +

{% if token_fail %}{% translate "Bad Token" %}{% else %}{% translate "Change Password" %}{% endif %}

+ + {% if token_fail %} + {% url 'account_reset_password' as passwd_reset_url %} +

{% blocktranslate %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktranslate %}

+ {% else %} + {% if form %} +
+ {% csrf_token %} + {{ form|crispy }} + +
+ {% else %} +

{% translate 'Your password is now changed.' %}

+ {% endif %} + {% endif %} +{% endblock %} diff --git a/commcare_connect/templates/account/password_reset_from_key_done.html b/commcare_connect/templates/account/password_reset_from_key_done.html new file mode 100644 index 000000000..07a9396ac --- /dev/null +++ b/commcare_connect/templates/account/password_reset_from_key_done.html @@ -0,0 +1,9 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% block head_title %}{% translate "Change Password" %}{% endblock %} + +{% block inner %} +

{% translate "Change Password" %}

+

{% translate 'Your password is now changed.' %}

+{% endblock %} diff --git a/commcare_connect/templates/account/password_set.html b/commcare_connect/templates/account/password_set.html new file mode 100644 index 000000000..69dec8589 --- /dev/null +++ b/commcare_connect/templates/account/password_set.html @@ -0,0 +1,18 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %}{% translate "Set Password" %}{% endblock %} + +{% block inner %} +

{% translate "Set Password" %}

+ +
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+
+{% endblock %} diff --git a/commcare_connect/templates/account/signup.html b/commcare_connect/templates/account/signup.html new file mode 100644 index 000000000..362039e14 --- /dev/null +++ b/commcare_connect/templates/account/signup.html @@ -0,0 +1,26 @@ +{% extends "account/base.html" %} + +{% load i18n %} +{% load crispy_forms_tags %} + +{% block head_title %}{% translate "Signup" %}{% endblock %} + +{% block inner %} +

{% translate "Sign Up" %}

+ + + +
+ {% blocktranslate %}Already have an account? Then please sign in.{% endblocktranslate %} +
+ +{% endblock %} diff --git a/commcare_connect/templates/account/signup_closed.html b/commcare_connect/templates/account/signup_closed.html new file mode 100644 index 000000000..11da9d779 --- /dev/null +++ b/commcare_connect/templates/account/signup_closed.html @@ -0,0 +1,11 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %}{% translate "Sign Up Closed" %}{% endblock %} + +{% block inner %} +

{% translate "Sign Up Closed" %}

+ +

{% translate "We are sorry, but the sign up is currently closed." %}

+{% endblock %} diff --git a/commcare_connect/templates/account/verification_sent.html b/commcare_connect/templates/account/verification_sent.html new file mode 100644 index 000000000..4c9ee7852 --- /dev/null +++ b/commcare_connect/templates/account/verification_sent.html @@ -0,0 +1,12 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %}{% translate "Verify Your E-mail Address" %}{% endblock %} + +{% block inner %} +

{% translate "Verify Your E-mail Address" %}

+ +

{% blocktranslate %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktranslate %}

+ +{% endblock %} diff --git a/commcare_connect/templates/account/verified_email_required.html b/commcare_connect/templates/account/verified_email_required.html new file mode 100644 index 000000000..5dd064ddb --- /dev/null +++ b/commcare_connect/templates/account/verified_email_required.html @@ -0,0 +1,21 @@ +{% extends "account/base.html" %} + +{% load i18n %} + +{% block head_title %}{% translate "Verify Your E-mail Address" %}{% endblock %} + +{% block inner %} +

{% translate "Verify Your E-mail Address" %}

+ +{% url 'account_email' as email_url %} + +

{% blocktranslate %}This part of the site requires us to verify that +you are who you claim to be. For this purpose, we require that you +verify ownership of your e-mail address. {% endblocktranslate %}

+ +

{% blocktranslate %}We have sent an e-mail to you for +verification. Please click on the link inside this e-mail. Please +contact us if you do not receive it within a few minutes.{% endblocktranslate %}

+ +

{% blocktranslate %}Note: you can still change your e-mail address.{% endblocktranslate %}

+{% endblock %} diff --git a/commcare_connect/templates/opportunity/add_payment_units.html b/commcare_connect/templates/opportunity/add_payment_units.html new file mode 100644 index 000000000..54b90fd98 --- /dev/null +++ b/commcare_connect/templates/opportunity/add_payment_units.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load django_tables2 %} + +{% block title %}{{ request.org }} - Payment Units {% endblock %} + +{% block content %} + {% include "steps.html" with step='step2' org_slug=request.org.slug pk=opportunity.pk %} +
+
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+
+ + + Setup Budget + +
+
+
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/add_visits_existing_users.html b/commcare_connect/templates/opportunity/add_visits_existing_users.html new file mode 100644 index 000000000..d4043a1c5 --- /dev/null +++ b/commcare_connect/templates/opportunity/add_visits_existing_users.html @@ -0,0 +1,147 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load i18n %} + +{% block title %}{{ request.org }} - {{ opportunity.name }}{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

{% translate "Edit Opportunity" %}

+

{% translate "Add Budget" %}

+
+
+
+
+ + +
+ {% if not disable_add_budget_for_new_users %} +
+ + +
+ {% endif %} +
+
+ {% csrf_token %} + + {% if form.selected_users.errors %} + + {% endif %} + + {% if opportunity_claims %} + + + + + + + + + + + + + {% for opp_claim in opportunity_claims %} + + + + + + + + + + {% endfor %} + +
NameUsernameMax VisitsUsed VisitsEnd date
+ + {{ opp_claim.opportunity_access.user.name }}{{ opp_claim.opportunity_access.user.username }}{{ opp_claim.max_payments }}{{ opp_claim.opportunity_access.visit_count }}{{ opp_claim.end_date }}
+
+
+ {{ form.additional_visits|as_crispy_field }} +
+
+ {{ form.end_date|as_crispy_field }} +
+
+ + {% else %} +
+ No Opportunity Claims yet.
+ All + Opportunities +
+ {% endif %} + + + + + +
+
+
Loading...
+
+
+
+ +
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/base.html b/commcare_connect/templates/opportunity/base.html new file mode 100644 index 000000000..372bae3ce --- /dev/null +++ b/commcare_connect/templates/opportunity/base.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +
+
+ {% block inner %}{% endblock %} +
+
+{% endblock %} diff --git a/commcare_connect/templates/opportunity/invoice_list.html b/commcare_connect/templates/opportunity/invoice_list.html new file mode 100644 index 000000000..c9612f62e --- /dev/null +++ b/commcare_connect/templates/opportunity/invoice_list.html @@ -0,0 +1,110 @@ +{% extends "opportunity/base.html" %} +{% load django_tables2 %} +{% load static %} +{% load crispy_forms_tags %} +{% load i18n %} + +{% block title %}{{ request.org }} - Invoices{% endblock title %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

Invoices

+
+ {% if not request.org_membership.is_program_manager %} + + {% endif %} + +
+
+ + +
+
+ + {% if request.org_membership.is_program_manager %} +
+ {% endif %} + +
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+ + {% if request.org_membership.is_program_manager %} + +
+ {% endif %} +{% endblock content %} + +{% if not request.org_membership.is_program_manager %} +{% block modal %} + +{% endblock modal %} +{% endif %} + +{% block inline_javascript %} + +{% endblock inline_javascript %} diff --git a/commcare_connect/templates/opportunity/opportunity_create.html b/commcare_connect/templates/opportunity/opportunity_create.html new file mode 100644 index 000000000..4895cef40 --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_create.html @@ -0,0 +1,22 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - Create Opportunity{% endblock %} + +{% block content %} +
+

Create Opportunity

+
+ {% csrf_token %} + {% crispy form %} +
+
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/opportunity_detail.html b/commcare_connect/templates/opportunity/opportunity_detail.html new file mode 100644 index 000000000..7af9815b3 --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_detail.html @@ -0,0 +1,662 @@ +{% extends "opportunity/base.html" %} +{% load i18n %} +{% load django_tables2 %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - {{ opportunity.name }}{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + +{% endblock %} + +{% block content %} +
+
+

{{ object.name }}

+
+ +
+
+
+ Ends on {{ object.end_date }} +
+
+ {% if export_task_id %} +
+
+
+ Loading... +
+
+
+ {% endif %} +
+
+
+ Description +

+ {{ object.description }} +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Visits
Total Visits Allotted{{ object.allotted_visits }}
Total Visits Claimed{{ object.claimed_visits }}
Total Visits Approved{{ object.approved_visits }}
Allotted Visits per user{{ object.max_visits_per_user_new }}
Daily Max Visits per user{{ object.daily_max_visits_per_user_new }}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Budget
Total Budget{{ object.total_budget }} {{ object.currency|default_if_none:"" }}
Budget per user{{ object.budget_per_user }} {{ object.currency|default_if_none:"" }}
Budget Claimed{{ object.claimed_budget }} {{ object.currency|default_if_none:"" }}
Budget Utilised{{ object.utilised_budget }} {{ object.currency|default_if_none:"" }}
Budget per visit{{ object.budget_per_visit_new }} {{ object.currency|default_if_none:"" }}
+
+
+
+
+

+ +

+
+
+ + + + + + + + + + {% for module in object.learn_app.learn_modules.all %} + + + + + + {% endfor %} + +
NameDescriptionTime Estimate
{{ module.name }}{{ module.description }}{{ module.time_estimate }} hour{{ module.time_estimate|pluralize:",s" }}
+
+
+
+
+

+ +

+
+
+ + + + + + + + + {% for unit in object.deliver_app.deliver_units.all %} + + + + + {% endfor %} + +
Deliver Unit NameDeliver Unit ID
{{ unit.name }}{{ unit.slug }}
+
+
+
+
+

+ +

+
+
+
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+
+
+
+
+ +
+ +
+
+
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+
+
+
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+
+
+
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+
+
+
+ {% include "tables/table_placeholder.html" with num_cols=4 %} +
+
+
+
+ + +{% endblock content %} + +{% block modal %} + + + + + + + + + + + + + + {% url "opportunity:payment_export" org_slug=request.org.slug pk=opportunity.pk as payment_export_url %} + {% include "export_modal.html" with modal_id="exportPaymentModal" modal_title=_("Export Users for Payment") export_url=payment_export_url export_form=export_form %} + + + + + {% url 'opportunity:user_status_export' org_slug=request.org.slug pk=opportunity.pk as user_status_export_url %} + {% include "export_modal.html" with modal_id="exportUserStatusModal" modal_title=_("Export User Status") export_url=user_status_export_url export_form=export_form %} + + + {% url 'opportunity:deliver_status_export' org_slug=request.org.slug pk=opportunity.pk as deliver_status_export_url %} + {% include "export_modal.html" with modal_id="exportDeliverStatusModal" modal_title=_("Export Deliver Status") export_url=deliver_status_export_url export_form=export_form %} + + + {% url 'opportunity:completed_work_export' org_slug=request.org.slug pk=opportunity.pk as completed_work_export_url %} + {% include "export_modal.html" with modal_id="exportCompletedWorkModal" modal_title=_("Export Payment Verification") export_url=completed_work_export_url export_form=export_form %} + + + {% url 'opportunity:catchment_area_export' org_slug=request.org.slug pk=opportunity.pk as catchment_area_export_url %} + {% include "export_modal.html" with modal_id="exportCatchmentModal" modal_title=_("Export Catchment Area") export_url=catchment_area_export_url export_form=export_form %} + + + + + + + + + + + + +{% endblock modal %} diff --git a/commcare_connect/templates/opportunity/opportunity_edit.html b/commcare_connect/templates/opportunity/opportunity_edit.html new file mode 100644 index 000000000..bd220e183 --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_edit.html @@ -0,0 +1,24 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - {{ opportunity.name }}{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

Edit Opportunity

+
+
+ {% csrf_token %} + {% crispy form %} +
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/opportunity_finalize.html b/commcare_connect/templates/opportunity/opportunity_finalize.html new file mode 100644 index 000000000..1ed612b2b --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_finalize.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - Opportunity Budget{% endblock %} + +{% block content %} + {% include "steps.html" with step='step3' org_slug=request.org.slug pk=opportunity.pk %} +{% include "partial_form.html" %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/opportunity_init.html b/commcare_connect/templates/opportunity/opportunity_init.html new file mode 100644 index 000000000..03a8e9a13 --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_init.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - Create Opportunity{% endblock %} + +{% block content %} + {% include "steps.html" with step='step1' %} +{% include "partial_form.html" %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/opportunity_list.html b/commcare_connect/templates/opportunity/opportunity_list.html new file mode 100644 index 000000000..04be5a476 --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_list.html @@ -0,0 +1,117 @@ +{% extends base_template %} +{% load static %} +{% load sort_link %} +{% load i18n %} +{% load django_tables2 %} +{% block title %}{{ request.org }} - Opportunities{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + {% if program %} + + {% endif %} +{% endblock %} + + + +{% block content %} +
+
+

Opportunities + + {% if request.org_membership.is_viewer %} + + {% else %} + Add new + + {% endif %} + +

+
+
+ + + + + + + + + + + + + {% for opportunity in page_obj %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% sort_link 'name' 'Name' %}{% sort_link 'start_date' 'Start Date' %}{% sort_link 'end_date' 'End Date' %}StatusProgramManage
{{ opportunity.name }}{{ opportunity.start_date|default:"Not Set" }}{{ opportunity.end_date|default:"Not Set" }} + {% if opportunity.is_setup_complete %} + {% if opportunity.is_active %} + Active + {% else %} + Inactive + {% endif %} + {% else %} + Pending Setup + {% endif %} + {% if opportunity.managed %} {{ opportunity.managedopportunity.program.name }} {% else %} - {% endif %} +
+ +  View + + {% if request.org_membership.is_viewer %} + + {% if not opportunity.managed %} + + {% endif %} + {% else %} +  Edit + {% if not opportunity.managed %} +  Add Budget + + {% endif %} + {% endif %} +
+
{% translate "No opportunities yet." %}
+
+ {% include 'pagination.html' %} +
+ +{% if program_invitation_table and program_invitation_table.data %} +
+
+

{% translate "Program Invitations" %}

+
+
+ {% render_table program_invitation_table %} +
+
+{% endif %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/opportunity_user_payments_list.html b/commcare_connect/templates/opportunity/opportunity_user_payments_list.html new file mode 100644 index 000000000..54ece15f6 --- /dev/null +++ b/commcare_connect/templates/opportunity/opportunity_user_payments_list.html @@ -0,0 +1,51 @@ +{% extends "opportunity/base.html" %} +{% load static %} + +{% block title %}{{ request.org }} - Opportunities{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

Payments for User {{ access.display_name }}

+
+ {% if latest_payment and not request.org_membership.is_viewer %} + + {% endif %} + {% load django_tables2 %} + {% render_table table %} + {% if latest_payment %} + + {% endif %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/payment_report.html b/commcare_connect/templates/opportunity/payment_report.html new file mode 100644 index 000000000..25d539fa9 --- /dev/null +++ b/commcare_connect/templates/opportunity/payment_report.html @@ -0,0 +1,57 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load django_tables2 %} + +{% block title %}Payment Report{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

Payment Report

+
+
+
+ + + + + + + + + + + + + +
Workers
Total Payment Accrued{{ total_user_payment_accrued }}
Total Paid{{ total_paid_users }}
+
+
+ + + + + + + + + + + + + +
Organization
Total Payment Accrued{{ total_nm_payment_accrued }}
Total Paid{{ total_paid_nm }}
+
+
+
+ {% render_table table %} +
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/send_message.html b/commcare_connect/templates/opportunity/send_message.html new file mode 100644 index 000000000..7bd049ed1 --- /dev/null +++ b/commcare_connect/templates/opportunity/send_message.html @@ -0,0 +1,74 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% if title %}{% block title %}{{ title }}{% endblock %} {% endif %} + +{% block content %} +
+ {% if form_title %}

{{ form_title }}

{% endif %} +
+ {% csrf_token %} + {% if form.selected_users.errors %} + + {% endif %} + {% if users %} +
+
+

Select Users*

+ + + + + + + + + + {% for user in users %} + + + + + + {% endfor %} + +
NameUsername
+ + {{ user.name }}{{ user.username }}
+
+
+ {{ form.title|as_crispy_field }} + {{ form.body|as_crispy_field }} + {{ form.message_type|as_crispy_field }} + +
+
+ {% else %} +
+ No Users yet.
+ All + Opportunities +
+ {% endif %} +
+ {{ user_ids|json_script:"user_ids" }} +
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/suspended_users.html b/commcare_connect/templates/opportunity/suspended_users.html new file mode 100644 index 000000000..fbebc149c --- /dev/null +++ b/commcare_connect/templates/opportunity/suspended_users.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}{{ request.org }} - Suspended Users{% endblock %} + +{% block content %} + + +

Suspended Users

+
+ {% include "tables/tabbed_table.html" with table=table %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/upload_progress.html b/commcare_connect/templates/opportunity/upload_progress.html new file mode 100644 index 000000000..3b7c4cf88 --- /dev/null +++ b/commcare_connect/templates/opportunity/upload_progress.html @@ -0,0 +1,23 @@ +
+ {% if progress.error %} + + {% elif not progress.complete %} +
+
+
+ Preparing export, hang tight... {{ progress.progress.description }} + {% else %} + All done! + Download your + export + {% endif %} +
diff --git a/commcare_connect/templates/opportunity/user_learn_progress.html b/commcare_connect/templates/opportunity/user_learn_progress.html new file mode 100644 index 000000000..ad6a8b435 --- /dev/null +++ b/commcare_connect/templates/opportunity/user_learn_progress.html @@ -0,0 +1,39 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load duration_minutes %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

Learn Progress for {{ object.user.name }}

+
+ + + + + + + + + + {% for mod in object.completedmodule_set.all %} + + + + + + {% empty %} + No learn progress for user. + {% endfor %} + +
Module NameDate CompletedDuration
{{ mod.module.name }}{{ mod.date }}{{ mod.duration|duration_minutes }}
+{% endblock content %} diff --git a/commcare_connect/templates/opportunity/user_profile.html b/commcare_connect/templates/opportunity/user_profile.html new file mode 100644 index 000000000..714a1c681 --- /dev/null +++ b/commcare_connect/templates/opportunity/user_profile.html @@ -0,0 +1,228 @@ +{% extends "base.html" %} +{% load i18n %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - {{access.display_name}}{% endblock %} + +{% block content %} + + +
+
+
+ +
+
+ {{access.display_name|slice:":1"}} +
+

{{access.display_name}}

+
{{access.user.username}}
+
+ + +
+
+
+
+ +
{% translate "Phone" %}
+
{{access.user.phone_number}}
+
+
+
+
+
+
+ +
{% translate "Learn Progress" %}
+
+
+
+
+ {{access.learn_progress}}% +
+
+
+
+
+
+
+ +
{% translate "Total Visits" %}
+
{{access.visit_count}}
+
+
+
+
+
+
+ +
{% translate "Last Visit" %}
+
{{access.last_visit_date}}
+
+
+
+
+ + +
+
+
Visit Locations
+
+ + +
+
+
+
+
+
+
Catchment Areas
+
+ + Active +
+
+ + Inactive +
+
+
+
+
+ + +
+ {% if access.suspended %} + + {% translate "Revoke Suspension" %} + + {% else %} + + {% endif %} +
+
+
+
+{% endblock content %} + +{% block inline_javascript %} +{{ block.super }} + {{ user_visits|json_script:"userVisits" }} + {{ user_catchments|json_script:"userCatchments" }} + +{% endblock %} + +{% block modal %} + +{% endblock %} diff --git a/commcare_connect/templates/opportunity/user_visit_review.html b/commcare_connect/templates/opportunity/user_visit_review.html new file mode 100644 index 000000000..208c6274e --- /dev/null +++ b/commcare_connect/templates/opportunity/user_visit_review.html @@ -0,0 +1,52 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load django_tables2 %} + +{% block title %}{{ request.org }} - User Visit Review{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

User Visit Review

+
+
+ {% crispy review_filter.form %} +
+{% if request.org_membership.is_admin and request.org.program_manager %} +
+ {% csrf_token %} +
+ {% render_table table %} +
+ + +
+{% else %} +
+ {% render_table table %} +
+{% endif %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/user_visits_list.html b/commcare_connect/templates/opportunity/user_visits_list.html new file mode 100644 index 000000000..c68ed7ddc --- /dev/null +++ b/commcare_connect/templates/opportunity/user_visits_list.html @@ -0,0 +1,23 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load django_tables2 %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

User Visits for {{ user_name }}

+
+
+ {% crispy visit_filter.form %} +
+ {% render_table table %} +{% endblock content %} diff --git a/commcare_connect/templates/opportunity/verification_flags_config.html b/commcare_connect/templates/opportunity/verification_flags_config.html new file mode 100644 index 000000000..3f4899905 --- /dev/null +++ b/commcare_connect/templates/opportunity/verification_flags_config.html @@ -0,0 +1,121 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load duration_minutes %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +

Verification Flags Configuration

+
+
+ {% csrf_token %} +
+ {% crispy form %} +
+
+

Deliver Unit Flags

+ {% crispy deliver_unit_formset deliver_unit_formset.empty_form.helper %} +
+
+

+ Form Validation Rules + +

+ {{ form_json_formset.management_form|crispy }} + +
+ {% for form in form_json_formset %} +
+
+ {% crispy form form.helper %} +
+ +
+ {% endfor %} +
+
+ + +
+{% endblock content %} + +{% block inline_javascript %} + +{% endblock inline_javascript %} diff --git a/commcare_connect/templates/opportunity/visit_verification.html b/commcare_connect/templates/opportunity/visit_verification.html new file mode 100644 index 000000000..0f10d75f9 --- /dev/null +++ b/commcare_connect/templates/opportunity/visit_verification.html @@ -0,0 +1,355 @@ +{% extends "opportunity/base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load django_tables2 %} +{% load duration_minutes %} + +{% block css %} +{{ block.super }} + +{% endblock %} + +{% block breadcrumbs_inner %} + {{ block.super }} + + + +{% endblock %} + +{% block content %} +
+

Visit of {{ visit.entity_name }}

+
+
+
+ + + + + + + + + + + + + + + +
Start Time{{ xform.metadata.timeStart }}
End Time{{ xform.metadata.timeEnd }}
Duration{{ xform.metadata.duration|duration_minutes }}
+
+
+ + + + + + + + + + + + + + + {% if visit.opportunity.managed %} + + + + + {% endif %} + +
Name{{ visit.entity_name }}
Identifier{{ visit.entity_id }}
Current Status + + {{ visit.get_status_display }} + +
Review Status + + {{ visit.get_review_status_display }} + +
+
+
+ {% if visit.flag_reason %} +

Flags

+ + + {% for flags in visit.flag_reason.flags %} + + + + {% endfor %} + +
{{ flags.1 }}
+ {% endif %} + {% if visit.reason %} +

Rejected Reason

+
{{ visit.reason }}
+ {% endif %} + {% if visit.location %} +

Map

+
+
+ +
+

Service Records

+
Under review
+
Closest from this user
+
Closest from others
+
+
+ {% endif %} + {% if visit.images %} +

Images

+
+ {% for image in visit.images %} +
+ {% endfor %} +
+ {% endif %} +
+ {% if request.org_membership.is_admin and request.org.program_manager or request.org_membership.is_viewer %} + + + {% else %} + {% if visit.flagged and visit.opportunity.managed %} + + {% else %} +
+ {% csrf_token %} + +
+ {% endif %} + + {% endif %} +
+
+{% endblock content %} + +{% block modal %} + + + + +{% endblock modal %} + + +{% block inline_javascript %} +{{ block.super }} + +{% endblock %} diff --git a/commcare_connect/templates/organization/organization_create.html b/commcare_connect/templates/organization/organization_create.html new file mode 100644 index 000000000..1817b30f0 --- /dev/null +++ b/commcare_connect/templates/organization/organization_create.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} +{% load i18n %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}New Organization - CommCare Connect{% endblock title %} + +{% block content %} +
+
+
+
Create a New Organization
+
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+
+
+{% endblock content %} diff --git a/commcare_connect/templates/organization/organization_home.html b/commcare_connect/templates/organization/organization_home.html new file mode 100644 index 000000000..99e14927d --- /dev/null +++ b/commcare_connect/templates/organization/organization_home.html @@ -0,0 +1,62 @@ +{% extends "base.html" %} +{% load i18n %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }}{% endblock title %} + +{% block content %} +
+
+
+

{{ organization.name }}

+

{{ organization.slug }}

+
+ +
+
+ {% csrf_token %} + {% crispy form %} +
+
+ +
+

{% translate "Members" %}

+
+ {% csrf_token %} + {% crispy membership_form %} +
+ +
+ + + + + + + + + {% for membership in organization.memberships.all %} + + + + + {% endfor %} + +
{% translate "Member" %}{% translate "Role" %}
+ {{ membership.user }} + {{ membership.get_role_display }}
+
+
+ +
+

{% translate "Add Credential" %}

+
+ {% csrf_token %} + {% crispy add_credential_form %} +
+
+ +
+
+{% endblock content %} diff --git a/commcare_connect/templates/pages/about.html b/commcare_connect/templates/pages/about.html new file mode 100644 index 000000000..94d9808cc --- /dev/null +++ b/commcare_connect/templates/pages/about.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/commcare_connect/templates/pages/home.html b/commcare_connect/templates/pages/home.html new file mode 100644 index 000000000..94d9808cc --- /dev/null +++ b/commcare_connect/templates/pages/home.html @@ -0,0 +1 @@ +{% extends "base.html" %} diff --git a/commcare_connect/templates/program/application_list.html b/commcare_connect/templates/program/application_list.html new file mode 100644 index 000000000..80d916d46 --- /dev/null +++ b/commcare_connect/templates/program/application_list.html @@ -0,0 +1,69 @@ +{% extends "program/base.html" %} +{% load static %} +{% load i18n %} +{% load sort_link %} +{% load django_tables2 %} +{% block title %}{{ request.org }} - Programs{% endblock %} + +{% block breadcrumbs_inner %} +{{ block.super }} + + +{% endblock %} +{% block content %} +
+
+

Applications + + + +

+
+
+ {% render_table table %} +
+
+ + + + + +{% endblock content %} diff --git a/commcare_connect/templates/program/base.html b/commcare_connect/templates/program/base.html new file mode 100644 index 000000000..02eeb1697 --- /dev/null +++ b/commcare_connect/templates/program/base.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +
+
+ {% block inner %}{% endblock %} +
+
+{% endblock %} diff --git a/commcare_connect/templates/program/dashboard.html b/commcare_connect/templates/program/dashboard.html new file mode 100644 index 000000000..57f3b461b --- /dev/null +++ b/commcare_connect/templates/program/dashboard.html @@ -0,0 +1,78 @@ +{% extends "program/base.html" %} +{% load static %} +{% load i18n %} +{% load django_tables2 %} +{% block title %}{{ request.org }} - Programs{% endblock %} + +{% block breadcrumbs_inner %} +{{ block.super }} + + +{% endblock %} +{% block content %} +
+
+

{% trans "Dashboard" %}

+
+
+ +
+
+
+ {% include "tables/table_placeholder.html" with num_cols=6 %} +
+
+ +
+
+
+ + +
+
+ + +
+ +
+
+
+ {% include "tables/table_placeholder.html" with num_cols=7 %} +
+
+
+
+
+{% endblock content %} diff --git a/commcare_connect/templates/program/program_add.html b/commcare_connect/templates/program/program_add.html new file mode 100644 index 000000000..8dd68b94b --- /dev/null +++ b/commcare_connect/templates/program/program_add.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% block content %} +

Create Program

+
+{% include "partial_form.html" %} +{% endblock content %} diff --git a/commcare_connect/templates/program/program_edit.html b/commcare_connect/templates/program/program_edit.html new file mode 100644 index 000000000..1f3517ce5 --- /dev/null +++ b/commcare_connect/templates/program/program_edit.html @@ -0,0 +1,24 @@ +{% extends "program/base.html" %} +{% load static %} +{% load crispy_forms_tags %} + +{% block title %}{{ request.org }} - {{ program.name }}{% endblock %} + +{% block breadcrumbs_inner %} +{{ block.super }} + + +{% endblock %} + +{% block content %} +

Edit Program

+
+
+ {% csrf_token %} + {% crispy form %} +
+{% endblock content %} diff --git a/commcare_connect/templates/program/program_list.html b/commcare_connect/templates/program/program_list.html new file mode 100644 index 000000000..1ff439af2 --- /dev/null +++ b/commcare_connect/templates/program/program_list.html @@ -0,0 +1,26 @@ +{% extends "program/base.html" %} +{% load static %} +{% load sort_link %} +{% load django_tables2 %} + +{% block title %}{{ request.org }} - Program{% endblock %} + +{% block content %} +
+
+

Programs + + Add new + + +

+
+ +
+ {% render_table table %} +
+ {% include 'pagination.html' %} +
+{% endblock content %} diff --git a/commcare_connect/templates/reports/admin.html b/commcare_connect/templates/reports/admin.html new file mode 100644 index 000000000..6361420bc --- /dev/null +++ b/commcare_connect/templates/reports/admin.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load django_tables2 %} + + +{% block title %}Admin Data{% endblock %} + +{% block content %} +

Admin Data Report by Quarter

+
+ {% render_table table %} +{% endblock content %} diff --git a/commcare_connect/templates/reports/dashboard.html b/commcare_connect/templates/reports/dashboard.html new file mode 100644 index 000000000..4150798de --- /dev/null +++ b/commcare_connect/templates/reports/dashboard.html @@ -0,0 +1,428 @@ +{% extends "base.html" %} +{% load static %} +{% load crispy_forms_tags %} +{% load django_tables2 %} +{% block title %}Admin Dashboard{% endblock %} +{% block javascript %} + {{ block.super }} + + +{% endblock %} +{% block content %} +

Program Dashboard

+
+
+
+ {% crispy filter.form %} +
+
+
+
+

Front-Line Worker Earnings

+
+
+
+
+
Amount Earned by FLWs (USD)
+
+ $0 + + +   + +
+
+
+
+
+
+
+
Amount Paid to FLWs (USD)
+
+ $0 + + +   + +
+
+
+
+
+
+
+

Organization Earnings

+
+
+
+
+
Amount Earned by Organizations (USD)
+
+ $0 + + +   + +
+
+
+
+
+
+
+
Amount Paid to Organizations (USD)
+
+ $0 + + +   + +
+
+
+
+
+
+
+

Program Metrics

+
+
+
+
+
+ 0 + + +   + +
+
Active FLWs
+
+
+
+
+
+
+
+ 0 + + +   + +
+
Total Visits
+
+
+
+
+
+
+
+ 0 + + +   + +
+
Verified Visits
+
+
+
+
+
+
+
+ 0 + + +   + +
+
Percent Verified
+
+
+
+
+
+
+
+
+

Service Delivery Map

+
+
+
+
+ Loading map... +
+
+
+
+
+
+
+

Visit Breakdown

+
+
+
By Program
+
+ +
+
+
+
By Status
+
+ +
+
+
+
Over time
+
+ +
+ +
+
+
+{% endblock content %} +{% block inline_javascript %} +{{ block.super }} + + +{% endblock %} diff --git a/commcare_connect/templates/reports/htmx_table.html b/commcare_connect/templates/reports/htmx_table.html new file mode 100644 index 000000000..d896eec5b --- /dev/null +++ b/commcare_connect/templates/reports/htmx_table.html @@ -0,0 +1,87 @@ +{% extends "django_tables2/bootstrap5.html" %} + +{% load django_tables2 %} +{% load i18n %} + +{% block table.thead %} + {% if table.show_header %} + + + {% for column in table.columns %} + + {{ column.header }} + {% if column.orderable %} + {% if column.order_by_alias == column.order_by_alias.next %} + + {% elif column.order_by_alias|slice:":1" == "-" %} + + {% else %} + + {% endif %} + {% endif%} + + {% endfor %} + + + {% endif %} +{% endblock table.thead %} + +{% block pagination %} + {% if table.page and table.paginator.num_pages > 1 %} + + {% endif %} +{% endblock pagination %} diff --git a/commcare_connect/templates/reports/report_table.html b/commcare_connect/templates/reports/report_table.html new file mode 100644 index 000000000..0c5775373 --- /dev/null +++ b/commcare_connect/templates/reports/report_table.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} + +{% load render_table from django_tables2 %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block content %} +

Delivery Stats Report

+ +
+ {% crispy filter.form %} +
+ + {% render_table table %} +{% endblock %} + +{% block javascript %} +{{ block.super }} + + + + + +{% endblock %} + +{% block css %} +{{ block.super }} + +{% endblock %} diff --git a/commcare_connect/templates/steps.html b/commcare_connect/templates/steps.html index acee3e927..77fa1240a 100644 --- a/commcare_connect/templates/steps.html +++ b/commcare_connect/templates/steps.html @@ -1,19 +1,8 @@ {% load static %} {% load crispy_forms_tags %} - + \ No newline at end of file diff --git a/commcare_connect/templates/tables/single_table.html b/commcare_connect/templates/tables/single_table.html new file mode 100644 index 000000000..8fe0be67c --- /dev/null +++ b/commcare_connect/templates/tables/single_table.html @@ -0,0 +1,4 @@ +{% load django_tables2 %} +
+ {% render_table table %} +
diff --git a/commcare_connect/templates/tables/tabbed_table.html b/commcare_connect/templates/tables/tabbed_table.html new file mode 100644 index 000000000..3df6ceb73 --- /dev/null +++ b/commcare_connect/templates/tables/tabbed_table.html @@ -0,0 +1,45 @@ +{% extends "django_tables2/bootstrap5.html" %} +{% load django_tables2 %} +{% load i18n %} + +{% block pagination %} +{% if table.page and table.paginator.num_pages > 1 %} + +{% endif %} +{% endblock pagination %} diff --git a/commcare_connect/templates/tables/table_manage_action.html b/commcare_connect/templates/tables/table_manage_action.html new file mode 100644 index 000000000..ca55184b8 --- /dev/null +++ b/commcare_connect/templates/tables/table_manage_action.html @@ -0,0 +1,23 @@ +{% load i18n %} +{% for button in buttons %} + {% if button.post %} + +
+ {% csrf_token %} + +
+ {% else %} + + + {% if button.icon %}{% endif %} + {% if button.icon %} {% endif %}{% translate button.text %} + + {% endif %} +{% endfor %} diff --git a/commcare_connect/templates/tables/table_placeholder.html b/commcare_connect/templates/tables/table_placeholder.html new file mode 100644 index 000000000..cb838455a --- /dev/null +++ b/commcare_connect/templates/tables/table_placeholder.html @@ -0,0 +1,16 @@ + + + + {% for i in "x"|rjust:num_cols %} + + {% endfor %} + + + + + {% for i in "x"|rjust:num_cols %} + + {% endfor %} + + +
diff --git a/commcare_connect/templates/tailwind/pages/add_payment_units.html b/commcare_connect/templates/tailwind/pages/add_payment_units.html index 678f792a6..3a44c3051 100644 --- a/commcare_connect/templates/tailwind/pages/add_payment_units.html +++ b/commcare_connect/templates/tailwind/pages/add_payment_units.html @@ -8,7 +8,7 @@ {% block content %}
- {% include "steps.html" with step='step2' org_slug=request.org.slug pk=opportunity.pk %} + {% include "tailwind/pages/steps.html" with step='step2' org_slug=request.org.slug pk=opportunity.pk %}
diff --git a/commcare_connect/templates/tailwind/pages/opportunity_finalize.html b/commcare_connect/templates/tailwind/pages/opportunity_finalize.html index 771d09805..894f9c32f 100644 --- a/commcare_connect/templates/tailwind/pages/opportunity_finalize.html +++ b/commcare_connect/templates/tailwind/pages/opportunity_finalize.html @@ -6,7 +6,7 @@ {% block content %}
- {% include "steps.html" with step='step3' org_slug=request.org.slug pk=opportunity.pk %} + {% include "tailwind/pages/steps.html" with step='step3' org_slug=request.org.slug pk=opportunity.pk %}
{% include "partial_form.html" %}
diff --git a/commcare_connect/templates/tailwind/pages/opportunity_init.html b/commcare_connect/templates/tailwind/pages/opportunity_init.html index d50c00750..dd0e7c7c7 100644 --- a/commcare_connect/templates/tailwind/pages/opportunity_init.html +++ b/commcare_connect/templates/tailwind/pages/opportunity_init.html @@ -8,7 +8,7 @@ {% block content %}
- {% include "steps.html" with step='step1' %} + {% include "tailwind/pages/steps.html" with step='step1' %}
Create Details
{% include "partial_form.html" %}
diff --git a/commcare_connect/templates/tailwind/pages/steps.html b/commcare_connect/templates/tailwind/pages/steps.html new file mode 100644 index 000000000..923eea1a1 --- /dev/null +++ b/commcare_connect/templates/tailwind/pages/steps.html @@ -0,0 +1,19 @@ +{% load static %} +{% load crispy_forms_tags %} + + diff --git a/commcare_connect/templates/tailwind/pages/verification_flags_config.html b/commcare_connect/templates/tailwind/pages/verification_flags_config.html index 8ea169394..0283bb3b5 100644 --- a/commcare_connect/templates/tailwind/pages/verification_flags_config.html +++ b/commcare_connect/templates/tailwind/pages/verification_flags_config.html @@ -76,11 +76,8 @@

Form Validation Rules

-
From 791ca16ea4a8da014d0e1634957338505ea8e6a8 Mon Sep 17 00:00:00 2001 From: Sravan Reddy Date: Wed, 16 Apr 2025 12:54:50 +0530 Subject: [PATCH 3/7] Fix modal popup in add budget form --- commcare_connect/opportunity/tw_form_views.py | 15 ++- commcare_connect/opportunity/tw_forms.py | 5 - commcare_connect/opportunity/views.py | 5 +- .../pages/add_visits_existing_users.html | 120 ++++++++++-------- .../tailwind/pages/invoice_list.html | 71 ++++++++++- 5 files changed, 146 insertions(+), 70 deletions(-) diff --git a/commcare_connect/opportunity/tw_form_views.py b/commcare_connect/opportunity/tw_form_views.py index b3c518533..8b093cc12 100644 --- a/commcare_connect/opportunity/tw_form_views.py +++ b/commcare_connect/opportunity/tw_form_views.py @@ -1,10 +1,12 @@ from django.contrib import messages from django.db.models import Q from django.forms import modelformset_factory +from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.test.utils import override_settings from django.urls import reverse from django.views.generic import CreateView +from crispy_forms.utils import render_crispy_form from commcare_connect.opportunity import views from commcare_connect.opportunity.models import ( @@ -249,16 +251,15 @@ def send_message_mobile_users(request, org_slug=None, pk=None): @org_member_required def invoice_create(request, org_slug=None, pk=None): opportunity = get_opportunity_or_404(pk, org_slug) - # if not opportunity.managed or request.org_membership.is_program_manager: - # return redirect("opportunity:detail", org_slug, pk) + if not opportunity.managed or request.org_membership.is_program_manager: + return redirect("opportunity:detail", org_slug, pk) form = PaymentInvoiceForm(data=request.POST or None, opportunity=opportunity) if request.POST and form.is_valid(): form.save() - return render( - request, - "tailwind/pages/form.html", - dict(title=f"{request.org.slug} - {opportunity.name}", form=form), - ) + form = PaymentInvoiceForm(opportunity=opportunity) + return redirect("opportunity:tw_invoice_list", org_slug, pk) + return HttpResponse(render_crispy_form(form)) + @override_settings(CRISPY_TEMPLATE_PACK="tailwind") diff --git a/commcare_connect/opportunity/tw_forms.py b/commcare_connect/opportunity/tw_forms.py index 25a6800db..563308220 100644 --- a/commcare_connect/opportunity/tw_forms.py +++ b/commcare_connect/opportunity/tw_forms.py @@ -524,11 +524,6 @@ def __init__(self, *args, **kwargs): ), css_class="flex flex-col gap-2", ), - Row( - Submit("cancel", "Cancel", css_class="button button-md outline-style"), - Submit("submit", "Submit", css_class="button button-md primary-dark"), - css_class="flex justify-end gap-4", - ), css_class="flex flex-col gap-4", ) ) diff --git a/commcare_connect/opportunity/views.py b/commcare_connect/opportunity/views.py index 02571aa09..ae8757a09 100644 --- a/commcare_connect/opportunity/views.py +++ b/commcare_connect/opportunity/views.py @@ -1355,6 +1355,7 @@ def invoice_list(request, org_slug, pk): @org_member_required def tw_invoice_list(request, org_slug=None, pk=None): + from commcare_connect.opportunity.tw_forms import PaymentInvoiceForm opportunity = get_opportunity_or_404(pk, org_slug) if not opportunity.managed: return redirect("opportunity:detail", org_slug, pk) @@ -1364,11 +1365,13 @@ def tw_invoice_list(request, org_slug=None, pk=None): queryset = PaymentInvoice.objects.filter(**filter_kwargs).order_by("date") table = TWPaymentInvoiceTable(queryset) + form = PaymentInvoiceForm(opportunity=opportunity) + RequestConfig(request, paginate={"per_page": 2}).configure(table) return render( request, "tailwind/pages/invoice_list.html", - {"header_title": "Invoices", "opportunity": opportunity, "table": table}, + {"header_title": "Invoices", "opportunity": opportunity, "table": table, "form": form}, ) diff --git a/commcare_connect/templates/tailwind/pages/add_visits_existing_users.html b/commcare_connect/templates/tailwind/pages/add_visits_existing_users.html index 0c9c6e23e..51e9b8a92 100644 --- a/commcare_connect/templates/tailwind/pages/add_visits_existing_users.html +++ b/commcare_connect/templates/tailwind/pages/add_visits_existing_users.html @@ -17,18 +17,21 @@ {% endblock %} {% block content %} -
-

Edit Opportunity

-
+
+

Edit Opportunity

+ {% csrf_token %} {% if form.selected_users.errors %} {% endif %} - {% if opportunity_claims %} -

Adding Visits for Existing Users

- + {% if opportunity_claims %} +

Adding Visits for Existing Users

+
- + @@ -51,60 +56,63 @@

Adding Visits for Existing Users

- {% for opp_claim in opportunity_claims %} - - - - - - - - - - {% endfor %} + {% for opp_claim in opportunity_claims %} + + + + + + + + + + {% endfor %}
+ + Name Username Max Visits
- - {{ opp_claim.opportunity_access.user.name }}{{ opp_claim.opportunity_access.user.username }}{{ opp_claim.max_payments }}{{ opp_claim.opportunity_access.visit_count }}{{ opp_claim.end_date }}
+ + {{ opp_claim.opportunity_access.user.name }}{{ opp_claim.opportunity_access.user.username }}{{ opp_claim.max_payments }}{{ opp_claim.opportunity_access.visit_count }}{{ opp_claim.end_date }}
{{ form.additional_visits|as_crispy_field }}
-
{{ form.end_date|as_crispy_field }}
-
-
+
{% else %}
No Opportunity Claims yet.
- All - Opportunities + All Opportunities
{% endif %} -
- - - {% endif %} +
+

{% translate "Add E-mail Address" %}

-
{% translate "Add E-mail Address" %}
+
+
+ {% csrf_token %} - - {% csrf_token %} - {{ form|crispy }} - -
+ {% crispy form %} -{% endblock %} +
+ +
+ +
+
+ +
+{% endblock content %} {% block inline_javascript %} @@ -80,7 +125,5 @@
{% translate "Add E-mail Address" %}
} }); } - Array.from(document.getElementsByClassName('form-group')).forEach(x => x.classList.remove('row')); - }); {% endblock %} diff --git a/commcare_connect/templates/account/email_confirm.html b/commcare_connect/templates/account/email_confirm.html index dcdeedd5e..6741bdde7 100644 --- a/commcare_connect/templates/account/email_confirm.html +++ b/commcare_connect/templates/account/email_confirm.html @@ -1,31 +1,50 @@ -{% extends "account/base.html" %} +{% extends "tailwind/base.html" %} {# Changed base template #} {% load i18n %} -{% load account %} +{% load account %} {# Keep necessary template tags #} {% block head_title %}{% translate "Confirm E-mail Address" %}{% endblock %} +{% block content %} {# Changed block name #} +
{# Added container, padding, centered text #} -{% block inner %} -

{% translate "Confirm E-mail Address" %}

+

{% translate "Confirm E-mail Address" %}

{# Tailwind heading styles #} -{% if confirmation %} + {% if confirmation %} -{% user_display confirmation.email_address.user as user_display %} + {% user_display confirmation.email_address.user as user_display %} -

{% blocktranslate with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktranslate %}

+ {# Original paragraph structure, Tailwind text style applied #} +

+ {% blocktranslate with confirmation.email_address.email as email %} + Please confirm that {{ email }} is an e-mail address for user {{ user_display }}. + {% endblocktranslate %} + {# Note: Link styling (text-brand-indigo etc.) applied directly to the tag above #} +

-
-{% csrf_token %} - -
+ {# Original form structure #} +
+ {% csrf_token %} {# Keep CSRF token #} -{% else %} + {# Original button structure, replace Bootstrap classes with Tailwind component #} + +
-{% url 'account_email' as email_url %} + {% else %} {# Original else block structure #} -

{% blocktranslate %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktranslate %}

+ {% url 'account_email' as email_url %} -{% endif %} + {# Original paragraph structure, Tailwind text style applied #} +

+ {% blocktranslate %} + This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request. + {% endblocktranslate %} + {# Note: Link styling (text-brand-indigo etc.) applied directly to the tag above #} +

+ {% endif %} + +
{# End overall container #} {% endblock %} diff --git a/commcare_connect/users/views.py b/commcare_connect/users/views.py index f680fe956..de810fd98 100644 --- a/commcare_connect/users/views.py +++ b/commcare_connect/users/views.py @@ -1,12 +1,15 @@ from allauth.account.models import transaction +from crispy_forms.utils import render_crispy_form from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.http import HttpResponse +from django.middleware.csrf import get_token from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.utils.decorators import method_decorator +from django.utils.safestring import mark_safe from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import csrf_exempt @@ -41,14 +44,29 @@ class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = User fields = ["name"] success_message = _("Information successfully updated") + template_name = "account/user_update.html" def get_success_url(self): assert self.request.user.is_authenticated # for mypy to know that the user is authenticated - return self.request.user.get_absolute_url() + return reverse("account_email") def get_object(self): return self.request.user + def get(self, request, *args, **kwargs): + self.object = self.get_object() + form = self.get_form() + csrf_token = get_token(self.request) + form_html = f""" +
+ + {render_crispy_form(form)} +
+ """ + return HttpResponse(mark_safe(form_html)) + user_update_view = UserUpdateView.as_view()