From 68ebcf88f59157f80f973962563d61fc9f2c3489 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 6 Oct 2025 14:12:46 -0700 Subject: [PATCH 01/10] set up org dashboard --- .../views/organizations/organization/index.html | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/templates/views/organizations/organization/index.html b/app/templates/views/organizations/organization/index.html index 6014333d7..e0520a968 100644 --- a/app/templates/views/organizations/organization/index.html +++ b/app/templates/views/organizations/organization/index.html @@ -2,20 +2,23 @@ {% from "components/big-number.html" import big_number %} {% from "components/live-search.html" import live_search %} {% from "components/pill.html" import pill %} +{% from "components/table.html" import list_table, field, row_heading %} {% extends "withnav_template.html" %} {% block org_page_title %} - Usage + Organization Dashboard {% endblock %} {% block maincolumn_content %} - {{ page_header('Usage', size='medium') }} + {{ page_header('Organization Dashboard', size='large') }} + +

Overall {{ selected_year }} Total Message Usage

+

Combined totals across all services in {{ current_org.name }}

{{ pill(years, selected_year, big_number_args={'smallest': True}) }}
-

Emails

From 0168331390a4409f1ac7f277d9acc46023b62435 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Mon, 6 Oct 2025 18:12:31 -0700 Subject: [PATCH 02/10] adding total chart --- app/main/views/organizations.py | 20 +++++++++- .../organizations/organization/index.html | 37 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/app/main/views/organizations.py b/app/main/views/organizations.py index 3c1e447ff..e9e757630 100644 --- a/app/main/views/organizations.py +++ b/app/main/views/organizations.py @@ -5,7 +5,12 @@ from functools import partial from flask import flash, redirect, render_template, request, url_for from flask_login import current_user -from app import current_organization, org_invite_api_client, organizations_client +from app import ( + current_organization, + org_invite_api_client, + organizations_client, + service_api_client, +) from app.main import main from app.main.forms import ( AdminBillingDetailsForm, @@ -67,6 +72,17 @@ def add_organization(): def organization_dashboard(org_id): year, current_financial_year = requested_and_current_financial_year(request) services = current_organization.services_and_usage(financial_year=year)["services"] + + total_messages_sent = 0 + total_messages_remaining = 0 + + for service in services: + service_message_ratio = service_api_client.get_service_message_ratio( + service["service_id"] + ) + total_messages_sent += service_message_ratio.get("messages_sent", 0) + total_messages_remaining += service_message_ratio.get("messages_remaining", 0) + return render_template( "views/organizations/organization/index.html", services=services, @@ -84,6 +100,8 @@ def organization_dashboard(org_id): download_link=url_for( ".download_organization_usage_report", org_id=org_id, selected_year=year ), + messages_sent=total_messages_sent, + messages_remaining=total_messages_remaining, ) diff --git a/app/templates/views/organizations/organization/index.html b/app/templates/views/organizations/organization/index.html index e0520a968..ff2f8f69c 100644 --- a/app/templates/views/organizations/organization/index.html +++ b/app/templates/views/organizations/organization/index.html @@ -17,8 +17,43 @@

Combined totals across all services in {{ current_org.name }}

- {{ pill(years, selected_year, big_number_args={'smallest': True}) }} +
+ +
+
+

Total messages

+ +
+ +
+
+
+

Emails

From 08d39d7bad30fc6b3fe97a1ca1bac8260ef5d11d Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 14 Oct 2025 12:27:15 -0700 Subject: [PATCH 03/10] Moved usage to it's own route to not confuse with dashboard --- app/main/views/organizations.py | 23 +++- app/navigation.py | 3 + app/templates/components/org_nav.html | 3 +- .../organizations/organization/index.html | 87 --------------- .../organizations/organization/usage.html | 103 ++++++++++++++++++ 5 files changed, 128 insertions(+), 91 deletions(-) create mode 100644 app/templates/views/organizations/organization/usage.html diff --git a/app/main/views/organizations.py b/app/main/views/organizations.py index e9e757630..d283af103 100644 --- a/app/main/views/organizations.py +++ b/app/main/views/organizations.py @@ -85,13 +85,32 @@ def organization_dashboard(org_id): return render_template( "views/organizations/organization/index.html", - services=services, years=get_tuples_of_financial_years( partial(url_for, ".organization_dashboard", org_id=current_organization.id), start=current_financial_year - 2, end=current_financial_year, ), selected_year=year, + messages_sent=total_messages_sent, + messages_remaining=total_messages_remaining, + ) + + +@main.route("/organizations//usage", methods=["GET"]) +@user_has_permissions() +def organization_usage(org_id): + year, current_financial_year = requested_and_current_financial_year(request) + services = current_organization.services_and_usage(financial_year=year)["services"] + + return render_template( + "views/organizations/organization/usage.html", + services=services, + years=get_tuples_of_financial_years( + partial(url_for, ".organization_usage", org_id=current_organization.id), + start=current_financial_year - 2, + end=current_financial_year, + ), + selected_year=year, search_form=SearchByNameForm() if len(services) > 7 else None, **{ f"total_{key}": sum(service[key] for service in services) @@ -100,8 +119,6 @@ def organization_dashboard(org_id): download_link=url_for( ".download_organization_usage_report", org_id=org_id, selected_year=year ), - messages_sent=total_messages_sent, - messages_remaining=total_messages_remaining, ) diff --git a/app/navigation.py b/app/navigation.py index daa29d90a..30945d547 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -339,6 +339,9 @@ class OrgNavigation(Navigation): "dashboard": { "organization_dashboard", }, + "usage": { + "organization_usage", + }, "settings": { "edit_organization_billing_details", "edit_organization_domains", diff --git a/app/templates/components/org_nav.html b/app/templates/components/org_nav.html index c29b41be2..a7a41832b 100644 --- a/app/templates/components/org_nav.html +++ b/app/templates/components/org_nav.html @@ -1,6 +1,7 @@
-
-
-

Emails

-
- {{ big_number( - total_emails_sent, - label='sent', - smaller=True - ) }} -
-
-
-

Text messages

-
- {{ big_number( - total_sms_cost, - 'spent', - currency="$", - smaller=True - ) }} -
-
-
- - {% if search_form %} -
- {{ live_search( - target_selector='.organization-service', - show=True, - form=search_form, - label='Search by service' - ) }} -
- {% endif %} - -

By service

- - {% for service in services %} -
-

- {{ service.service_name }} -

-
-
- {{ big_number( - service.emails_sent, - label=service.emails_sent|message_count_label('email'), - smallest=True - ) }} -
-
- {% if service.sms_cost %} - {{ big_number( - service.sms_cost, - 'spent on text messages', - currency="$", - smallest=True - ) }} - {% else %} - {{ big_number( - service.sms_billable_units, - 'free {}'.format(service.sms_billable_units|message_count_label('sms')), - smallest=True - ) }} - {% endif %} -
-
-
- {% endfor %} -
- {% if not services %} -

- {{ current_org.name }} has no live services on Notify.gov -

-
- {% else %} - - {% endif %} - {% endblock %} diff --git a/app/templates/views/organizations/organization/usage.html b/app/templates/views/organizations/organization/usage.html new file mode 100644 index 000000000..8c7912968 --- /dev/null +++ b/app/templates/views/organizations/organization/usage.html @@ -0,0 +1,103 @@ +{% from "components/page-header.html" import page_header %} +{% from "components/big-number.html" import big_number %} +{% from "components/live-search.html" import live_search %} +{% from "components/pill.html" import pill %} +{% from "components/table.html" import list_table, field, row_heading %} +{% extends "withnav_template.html" %} + +{% block org_page_title %} + Usage +{% endblock %} + +{% block maincolumn_content %} + + {{ page_header('Usage', size='medium') }} + +
+ {{ pill(years, selected_year, big_number_args={'smallest': True}) }} +
+ +
+
+

Emails

+
+ {{ big_number( + total_emails_sent, + label='sent', + smaller=True + ) }} +
+
+
+

Text messages

+
+ {{ big_number( + total_sms_cost, + 'spent', + currency="$", + smaller=True + ) }} +
+
+
+ + {% if search_form %} +
+ {{ live_search( + target_selector='.organization-service', + show=True, + form=search_form, + label='Search by service' + ) }} +
+ {% endif %} + +

By service

+ + {% for service in services %} +
+

+ {{ service.service_name }} +

+
+
+ {{ big_number( + service.emails_sent, + label=service.emails_sent|message_count_label('email'), + smallest=True + ) }} +
+
+ {% if service.sms_cost %} + {{ big_number( + service.sms_cost, + 'spent on text messages', + currency="$", + smallest=True + ) }} + {% else %} + {{ big_number( + service.sms_billable_units, + 'free {}'.format(service.sms_billable_units|message_count_label('sms')), + smallest=True + ) }} + {% endif %} +
+
+
+ {% endfor %} +
+ {% if not services %} +

+ {{ current_org.name }} has no live services on Notify.gov +

+
+ {% else %} + + {% endif %} + +{% endblock %} From f4d326738c249ec926dc0a6329d8f0585393dc06 Mon Sep 17 00:00:00 2001 From: Beverly Nguyen Date: Tue, 14 Oct 2025 12:37:12 -0700 Subject: [PATCH 04/10] added feature flag --- .ds.baseline | 4 ++-- app/config.py | 5 +++++ app/main/views/organizations.py | 5 ++++- app/templates/components/org_nav.html | 2 ++ deploy-config/demo.yml | 1 + deploy-config/production.yml | 1 + deploy-config/sandbox.yml | 1 + deploy-config/staging.yml | 1 + manifest.yml | 2 ++ 9 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 9c1dcfeb6..3a60a1545 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -161,7 +161,7 @@ "filename": "app/config.py", "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", "is_verified": false, - "line_number": 118, + "line_number": 123, "is_secret": false } ], @@ -634,5 +634,5 @@ } ] }, - "generated_at": "2025-10-07T17:43:26Z" + "generated_at": "2025-10-14T19:37:07Z" } diff --git a/app/config.py b/app/config.py index 92aee1670..f691f7a7a 100644 --- a/app/config.py +++ b/app/config.py @@ -78,6 +78,8 @@ class Config(object): # TODO: reassign this NOTIFY_SERVICE_ID = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" + ORGANIZATION_DASHBOARD_ENABLED = getenv("ORGANIZATION_DASHBOARD_ENABLED", "False") == "True" + NOTIFY_BILLING_DETAILS = json.loads(getenv("NOTIFY_BILLING_DETAILS") or "null") or { "account_number": "98765432", "sort_code": "01-23-45", @@ -109,6 +111,9 @@ class Development(Config): ASSET_PATH = "/static/" NOTIFY_LOG_LEVEL = "DEBUG" + # Feature Flags - Enable in development for testing + ORGANIZATION_DASHBOARD_ENABLED = getenv("ORGANIZATION_DASHBOARD_ENABLED", "True") == "True" + # Buckets CSV_UPLOAD_BUCKET = _s3_credentials_from_env("CSV") LOGO_UPLOAD_BUCKET = _s3_credentials_from_env("LOGO") diff --git a/app/main/views/organizations.py b/app/main/views/organizations.py index d283af103..ac332f765 100644 --- a/app/main/views/organizations.py +++ b/app/main/views/organizations.py @@ -2,7 +2,7 @@ from collections import OrderedDict from datetime import datetime from functools import partial -from flask import flash, redirect, render_template, request, url_for +from flask import current_app, flash, redirect, render_template, request, url_for from flask_login import current_user from app import ( @@ -70,6 +70,9 @@ def add_organization(): @main.route("/organizations/", methods=["GET"]) @user_has_permissions() def organization_dashboard(org_id): + if not current_app.config.get("ORGANIZATION_DASHBOARD_ENABLED", False): + return redirect(url_for(".organization_usage", org_id=org_id)) + year, current_financial_year = requested_and_current_financial_year(request) services = current_organization.services_and_usage(financial_year=year)["services"] diff --git a/app/templates/components/org_nav.html b/app/templates/components/org_nav.html index a7a41832b..7b54f2ffb 100644 --- a/app/templates/components/org_nav.html +++ b/app/templates/components/org_nav.html @@ -1,6 +1,8 @@