Skip to content

Commit 783f001

Browse files
authored
Pagination (#170)
* Pagination * update the project version and changelog version
1 parent d9d9f24 commit 783f001

12 files changed

Lines changed: 100 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,7 @@ All notable changes to this service will be documented in this file.
139139
## [1.3.17]
140140
### Notes
141141
- Use a feature flag to disable submitting an application
142+
143+
## [1.3.18]
144+
### Notes
145+
- Added pagination to the dashboard

src/DfE.ExternalApplications.Application/DfE.ExternalApplications.Application.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="GovUK.Dfe.ExternalApplications.Api.Client" Version="0.1.44" />
10+
<PackageReference Include="GovUK.Dfe.ExternalApplications.Api.Client" Version="0.1.45-prerelease-2" />
1111
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="2.3.0" />
1212
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
1313
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace DfE.ExternalApplications.Application.Options;
2+
3+
public class DashboardOptions
4+
{
5+
public int PageSize { get; set; } = 10;
6+
}

src/DfE.ExternalApplications.Web/DfE.ExternalApplications.Web.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<UserSecretsId>8051c984-585b-4a5e-b6d7-833e5dd4afe7</UserSecretsId>
8-
<Version>1.3.17</Version>
9-
<InformationalVersion>1.3.17</InformationalVersion>
8+
<Version>1.3.18</Version>
9+
<InformationalVersion>1.3.18</InformationalVersion>
1010
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
1111
</PropertyGroup>
1212

src/DfE.ExternalApplications.Web/Pages/Applications/Dashboard.cshtml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,55 @@
8787
</table>
8888
}
8989

90+
@if (Model.TotalPages > 1)
91+
{
92+
<nav class="govuk-pagination" aria-label="Pagination">
93+
@if (Model.CurrentPage > 1)
94+
{
95+
<div class="govuk-pagination__prev">
96+
<a class="govuk-link govuk-pagination__link" href="?currentPage=@(Model.CurrentPage - 1)" rel="prev">
97+
<svg class="govuk-pagination__icon govuk-pagination__icon--prev" xmlns="http://www.w3.org/2000/svg" height="13" width="15" focusable="false" aria-hidden="true" viewBox="0 0 15 13">
98+
<path d="m6.5938-0.0078125-6.7266 6.7266 6.7441 6.4062 1.377-1.449-4.1856-3.9768h12.896v-2h-12.984l4.2931-4.293-1.414-1.414z" />
99+
</svg>
100+
<span class="govuk-pagination__link-title">Previous<span class="govuk-visually-hidden"> page</span></span>
101+
</a>
102+
</div>
103+
}
104+
<ul class="govuk-pagination__list">
105+
@for (var i = 1; i <= Model.TotalPages; i++)
106+
{
107+
if (i == Model.CurrentPage)
108+
{
109+
<li class="govuk-pagination__item govuk-pagination__item--current">
110+
<a class="govuk-link govuk-pagination__link" href="?currentPage=@i" aria-label="Page @i" aria-current="page">@i</a>
111+
</li>
112+
}
113+
else if (i == 1 || i == Model.TotalPages || Math.Abs(i - Model.CurrentPage) <= 1)
114+
{
115+
<li class="govuk-pagination__item">
116+
<a class="govuk-link govuk-pagination__link" href="?currentPage=@i" aria-label="Page @i">@i</a>
117+
</li>
118+
}
119+
else if (Math.Abs(i - Model.CurrentPage) == 2)
120+
{
121+
<li class="govuk-pagination__item govuk-pagination__item--ellipses">&ctdot;</li>
122+
}
123+
}
124+
</ul>
125+
@if (Model.CurrentPage < Model.TotalPages)
126+
{
127+
<div class="govuk-pagination__next">
128+
<a class="govuk-link govuk-pagination__link" href="?currentPage=@(Model.CurrentPage + 1)" rel="next">
129+
<span class="govuk-pagination__link-title">Next<span class="govuk-visually-hidden"> page</span></span>
130+
<svg class="govuk-pagination__icon govuk-pagination__icon--next" xmlns="http://www.w3.org/2000/svg" height="13" width="15" focusable="false" aria-hidden="true" viewBox="0 0 15 13">
131+
<path d="m8.107-0.0078125-1.4136 1.414 4.2926 4.293h-12.986v2h12.896l-4.1855 3.9766 1.377 1.4492 6.7441-6.4062-6.7246-6.7246z" />
132+
</svg>
133+
</a>
134+
</div>
135+
}
136+
</nav>
137+
}
138+
90139
<h2 class="govuk-heading-l govuk-!-margin-top-9">
91140
Start a new @AppTerminology.Singular
92141
</h2>

src/DfE.ExternalApplications.Web/Pages/Applications/Dashboard.cshtml.cs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
using GovUK.Dfe.CoreLibs.Contracts.ExternalApplications.Models.Request;
66
using GovUK.Dfe.CoreLibs.Contracts.ExternalApplications.Models.Response;
77
using DfE.ExternalApplications.Application.Interfaces;
8+
using DfE.ExternalApplications.Application.Options;
89
using GovUK.Dfe.ExternalApplications.Api.Client.Contracts;
910
using GovUK.Dfe.ExternalApplications.Api.Client.Security;
1011
using Microsoft.AspNetCore.Authorization;
1112
using Microsoft.AspNetCore.Mvc;
1213
using Microsoft.AspNetCore.Mvc.RazorPages;
14+
using Microsoft.Extensions.Options;
1315
using SystemTask = System.Threading.Tasks.Task;
1416
using Microsoft.Extensions.Configuration;
1517

@@ -22,7 +24,8 @@ public class DashboardModel(
2224
IApplicationsClient applicationsClient,
2325
IHttpContextAccessor httpContextAccessor,
2426
IApplicationResponseService applicationResponseService,
25-
IFormTemplateProvider templateProvider)
27+
IFormTemplateProvider templateProvider,
28+
IOptions<DashboardOptions> dashboardOptions)
2629
: PageModel
2730
{
2831
public string? Email { get; private set; }
@@ -33,6 +36,12 @@ public class DashboardModel(
3336
public bool HasError { get; private set; }
3437
public string? ErrorMessage { get; private set; }
3538

39+
[BindProperty(SupportsGet = true)]
40+
public int CurrentPage { get; set; } = 1;
41+
42+
public int TotalPages { get; private set; }
43+
public int PageSize => dashboardOptions.Value.PageSize;
44+
3645
public class ApplicationWithCalculatedStatus
3746
{
3847
public ApplicationDto Application { get; set; } = null!;
@@ -157,20 +166,23 @@ private async SystemTask LoadApplicationsAsync()
157166
return;
158167
}
159168

160-
var applications = await applicationsClient.GetMyApplicationsAsync(templateId: templateGuid.Value);
169+
var pageSize = dashboardOptions.Value.PageSize;
170+
var result = await applicationsClient.GetMyApplicationsAsync(
171+
templateId: templateGuid.Value,
172+
pageNumber: CurrentPage,
173+
pageSize: pageSize);
161174

162-
// Calculate status for each application
163-
var applicationTasks = applications.Select(async app => new ApplicationWithCalculatedStatus
175+
TotalPages = result.TotalPages;
176+
CurrentPage = Math.Clamp(CurrentPage, 1, Math.Max(1, TotalPages));
177+
178+
var applicationTasks = result.Items.AsEnumerable().Select(async app => new ApplicationWithCalculatedStatus
164179
{
165180
Application = app,
166181
CalculatedStatus = await GetCalculatedApplicationStatusAsync(app)
167182
});
168183

169-
var applicationsWithStatus = await SystemTask.WhenAll(applicationTasks);
170-
171-
Applications = applicationsWithStatus
172-
.OrderByDescending(a => a.DateCreated)
173-
.ToList();
184+
Applications = [..(await SystemTask.WhenAll(applicationTasks))
185+
.OrderByDescending(a => a.DateCreated)];
174186
}
175187

176188
private Guid? ResolveTemplateId()

src/DfE.ExternalApplications.Web/Pages/Feedback/SupportRequest.cshtml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ protected override SupportRequest BuildUserFeedbackRequest() =>
2424

2525
protected override async Task FetchFormDataAsync()
2626
{
27-
var applications = await applicationsClient.GetMyApplicationsAsync(templateId: TemplateId);
28-
ApplicationReferences = applications.Select(a => a.ApplicationReference).ToList();
27+
var result = await applicationsClient.GetMyApplicationsAsync(templateId: TemplateId);
28+
ApplicationReferences = result.Items.AsEnumerable().Select(a => a.ApplicationReference).ToList();
2929

3030
await base.FetchFormDataAsync();
3131
}

src/DfE.ExternalApplications.Web/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,9 @@ static void BindNestedConfiguration(ConfigurationManager config, string parentKe
442442

443443
// Site-wide notification banner (feature flag driven from appsettings)
444444
builder.Services.Configure<NotificationBannerOptions>(configuration.GetSection("NotificationBanner"));
445+
446+
// Dashboard configuration (page size for application list pagination)
447+
builder.Services.Configure<DashboardOptions>(configuration.GetSection("Dashboard"));
445448
builder.Services.AddSingleton<IApplicationTerminologyProvider, ApplicationTerminologyProvider>();
446449

447450
// Application submission configuration (mapper key and handlers per application)

src/DfE.ExternalApplications.Web/appsettings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@
143143
"sigchange": "form-sigchange-guid"
144144
}
145145
},
146+
"Dashboard": {
147+
"PageSize": 10
148+
},
146149
"CacheSettings": {
147150
"Memory": {
148151
"DefaultDurationInSeconds": 60,

src/DfE.ExternalApplications.Web/configurations/Lsrp/appsettings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
"Audience": "external-applications-api",
3131
"TokenLifetimeMinutes": 10
3232
},
33+
"Dashboard": {
34+
"PageSize": 10
35+
},
3336
"AllowedHosts": "*",
3437
"DfESignIn": {
3538
"Authority": "https://test-oidc.signin.education.gov.uk",

0 commit comments

Comments
 (0)