diff --git a/app/assets/javascripts/activityChart.js b/app/assets/javascripts/activityChart.js index 62c1e6e3e6..a3f615c655 100644 --- a/app/assets/javascripts/activityChart.js +++ b/app/assets/javascripts/activityChart.js @@ -1,7 +1,8 @@ (function (window) { if (document.getElementById('activityChartContainer')) { - + const tableContainer = document.getElementById('activityContainer'); + const currentUserName = tableContainer.getAttribute('data-currentUserName'); const COLORS = { delivered: '#0076d6', failed: '#fa9441', @@ -270,27 +271,58 @@ subTitle.textContent = `${selectedText} - last 7 days`; fetchData(selectedValue); - // Update ARIA live region const liveRegion = document.getElementById('aria-live-account'); liveRegion.textContent = `Data updated for ${selectedText} - last 7 days`; - // Switch tables based on dropdown selection - const selectedTable = selectedValue === "individual" ? "table1" : "table2"; - const tables = document.querySelectorAll('.table-overflow-x-auto'); - tables.forEach(function(table) { - table.classList.add('hidden'); // Hide all tables by adding the hidden class - table.classList.remove('visible'); // Ensure they are not visible - }); - const tableToShow = document.getElementById(selectedTable); - tableToShow.classList.remove('hidden'); // Remove hidden class - tableToShow.classList.add('visible'); // Add visible class + const tableHeading = document.querySelector('#tableActivity h2'); + const senderColumns = document.querySelectorAll('.sender-column'); + const allRows = document.querySelectorAll('#activity-table tbody tr'); + const caption = document.querySelector('#activity-table caption'); + + if (selectedValue === 'individual') { + + tableHeading.textContent = 'My activity'; + caption.textContent = `Table showing the sent jobs for ${currentUserName}`; + + senderColumns.forEach(col => { + col.style.display = 'none'; + }); + + allRows.forEach(row => row.style.display = 'none'); + + const userRows = Array.from(allRows).filter(row => { + const senderCell = row.querySelector('.sender-column'); + const rowSender = senderCell ? senderCell.textContent.trim() : ''; + return rowSender === currentUserName; + }); + + userRows.slice(0, 5).forEach(row => { + row.style.display = ''; + }); + } else { + + tableHeading.textContent = 'Service activity'; + caption.textContent = `Table showing the sent jobs for service`; + + senderColumns.forEach(col => { + col.style.display = ''; + }); + + allRows.forEach((row, index) => { + row.style.display = (index < 5) ? '' : 'none'; + }); + } }; document.addEventListener('DOMContentLoaded', function() { // Initialize activityChart chart and table with service data by default fetchData('service'); - // Add event listener to the dropdown + const allRows = Array.from(document.querySelectorAll('#activity-table tbody tr')); + allRows.forEach((row, index) => { + row.style.display = (index < 5) ? '' : 'none'; + }); + const dropdown = document.getElementById('options'); dropdown.addEventListener('change', handleDropdownChange); }); diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 8013acb9d2..1f8cac5730 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -14,7 +14,7 @@ service_api_client, template_statistics_client, ) -from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left +from app.formatters import format_date_numeric, format_datetime_numeric from app.main import main from app.main.views.user_profile import set_timezone from app.statistics_utils import get_formatted_percentage @@ -62,32 +62,14 @@ def service_dashboard(service_id): job_response = job_api_client.get_jobs(service_id)["data"] service_data_retention_days = 7 - jobs = [ - { - "job_id": job["id"], - "time_left": get_time_left(job["created_at"]), - "download_link": url_for( - ".view_job_csv", service_id=current_service.id, job_id=job["id"] - ), - "view_job_link": url_for( - ".view_job", service_id=current_service.id, job_id=job["id"] - ), - "created_at": job["created_at"], - "processing_finished": job.get("processing_finished"), - "processing_started": job.get("processing_started"), - "notification_count": job["notification_count"], - "created_by": job["created_by"], - "template_name": job["template_name"], - "original_file_name": job["original_file_name"], - } - for job in job_response - if job["job_status"] != "cancelled" - ] + filtered_jobs = [job for job in job_response if job["job_status"] != "cancelled"] + sorted_jobs = sorted(filtered_jobs, key=lambda job: job["created_at"], reverse=True) + return render_template( "views/dashboard/dashboard.html", updates_url=url_for(".service_dashboard_updates", service_id=service_id), partials=get_dashboard_partials(service_id), - jobs=jobs, + jobs=sorted_jobs, service_data_retention_days=service_data_retention_days, sms_sent=sms_sent, sms_allowance_remaining=sms_allowance_remaining, diff --git a/app/templates/views/dashboard/activity-table.html b/app/templates/views/dashboard/activity-table.html new file mode 100644 index 0000000000..a40840033d --- /dev/null +++ b/app/templates/views/dashboard/activity-table.html @@ -0,0 +1,72 @@ +

Recent activity

+
+
+ + +
+
+
+
{{ current_service.name }} - last 7 days
+
+
+
+
+
+
+
+
+
+

Service activity

+ + + + + + + + + + + + + + + {% if jobs %} + {% for job in jobs %} + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
Table showing the sent jobs for {{current_service.name}}
Job ID#TemplateJob statusSender + # of Recipients
+ + {{ job.id[:8] if job.id else 'Manually entered number' }} + + {{ job.template_name }} + {% if job.scheduled_for and not job.processing_finished %} + Scheduled for {{ job.scheduled_for|format_datetime_table }} + {% elif job.processing_finished and not job.statistics|selectattr('status', 'equalto', 'sending')|list %} + Sent on {{ job.processing_finished|format_datetime_table }} + {% elif job.processing_started %} + Sending since {{ job.processing_started|format_datetime_table }} + {% else %} + Pending since {{ job.created_at|format_datetime_table }} + {% endif %} + {{ job.created_by.name }}{{ job.notification_count }}
No batched job messages found  (messages are + kept for {{ service_data_retention_days }} days).
+
+
diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html index 04abe1afcc..bd19585c31 100644 --- a/app/templates/views/dashboard/dashboard.html +++ b/app/templates/views/dashboard/dashboard.html @@ -6,140 +6,38 @@ {% from "components/ajax-block.html" import ajax_block %} {% block service_page_title %} - Dashboard + Dashboard {% endblock %} {% block maincolumn_content %} - - - -
- -

Dashboard

- {% if current_user.has_permissions('manage_templates') and not current_service.all_templates %} - {% include 'views/dashboard/write-first-messages.html' %} - {% endif %} - - {{ ajax_block(partials, updates_url, 'upcoming') }} - -

{{ current_service.name }} Dashboard

- - {{ ajax_block(partials, updates_url, 'inbox') }} - -
-

Total messages

- -
-
-
- -

Recent activity

-
-
- - -
-
-
-
{{ current_service.name }} - last 7 days
-
-
-
-
-
-
-
- - {% if current_user.has_permissions('manage_service') %}{% endif %} - -
- - -
-

Service activity

- - - - - - - - - - - - - {% if jobs %} - {% for job in jobs[:5] %} - {% set notification = job.notifications[0] %} - - - - - - - - {% endfor %} - {% else %} - - - - {% endif %} - -
Table showing the sent jobs for this service
Job ID#TemplateJob statusSender# of Recipients
- - {{ job.job_id[:8] if job.job_id else 'Manually entered number' }} - - {{ job.template_name }}Sent on - {{ (job.processing_finished if job.processing_finished else job.processing_started - if job.processing_started else job.created_at)|format_datetime_table }} - {{ job.created_by.name }}{{ job.notification_count }}
No batched job messages found  (messages are kept for {{ service_data_retention_days }} days).
-
-
- {{ ajax_block(partials, updates_url, 'template-statistics') }} -
+ + + +
+ +

Dashboard

+{% if current_user.has_permissions('manage_templates') and not current_service.all_templates %} + {% include 'views/dashboard/write-first-messages.html' %} +{% endif %} + +{{ ajax_block(partials, updates_url, 'upcoming') }} + +

{{ current_service.name }} Dashboard

+ +{{ ajax_block(partials, updates_url, 'inbox') }} + +
+

Total messages

+ +
+
+
+ +{% include 'views/dashboard/activity-table.html' %} + +{% if current_user.has_permissions('manage_service') %}{% endif %} + +{{ ajax_block(partials, updates_url, 'template-statistics') }} +
{% endblock %} diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 97cf42d688..461888c5c4 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -657,9 +657,8 @@ def test_should_show_recent_templates_on_dashboard( ] assert "Total messages" in headers - table_rows = page.find_all("tbody")[0].find_all("tr") - - assert len(table_rows) == 0 + table_rows = page.find_all("tbody")[1].find_all("tr") + assert len(table_rows) == 2 @pytest.mark.parametrize( @@ -1943,7 +1942,6 @@ def test_service_dashboard_shows_batched_jobs( job_table_body = page.find("table", class_="job-table") rows = job_table_body.find_all("tbody")[0].find_all("tr") - - assert len(rows) == 0 + assert len(rows) == 1 assert job_table_body is not None diff --git a/tests/javascripts/activityChart.test.js b/tests/javascripts/activityChart.test.js index 8e3d406eac..427c93ac24 100644 --- a/tests/javascripts/activityChart.test.js +++ b/tests/javascripts/activityChart.test.js @@ -21,7 +21,7 @@ Object.defineProperty(HTMLElement.prototype, 'clientWidth', { beforeAll(done => { // Set up the DOM with the D3 script included document.body.innerHTML = ` -
+
+ + + + `; + + window.currentUserName = "Test User"; + + jest.spyOn(window, 'fetchData').mockImplementation(() => {}); + + const selectElement = document.getElementById('options'); + selectElement.value = 'individual'; + const event = { target: selectElement }; + + window.handleDropdownChange(event); + + expect(document.getElementById('table-heading').textContent).toBe('My activity'); + expect(document.getElementById('caption').textContent).toContain('Test User'); + + document.querySelectorAll('.sender-column').forEach(col => { + expect(col.style.display).toBe('none'); + }); + + const rows = Array.from(document.querySelectorAll('#activity-table tbody tr')); + const visibleRows = rows.filter(row => row.style.display !== 'none'); + expect(visibleRows.length).toBeLessThanOrEqual(5); + visibleRows.forEach(row => { + const sender = row.querySelector('.sender-column').textContent.trim(); + expect(sender).toBe('Test User'); + }); + + window.fetchData.mockRestore(); +});