Skip to content

Commit

Permalink
Merge pull request #505 from DFE-Digital/edit-contacts-ui
Browse files Browse the repository at this point in the history
Create Edit contacts UI and hide behind feature flag
  • Loading branch information
nwarms authored Sep 20, 2024
2 parents d22709b + 01c6164 commit e993381
Show file tree
Hide file tree
Showing 25 changed files with 627 additions and 98 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased][unreleased]

### Added

- Created Edit contacts UI and feature flag to hide it on production environment

### Changed

- Ofsted logic can now handle null dates
- Updated contacts page UI to split each contact into it own section

## [Release-6][release-6] (production-2024-09-17.3129)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public static class FeatureFlags
{
public const string TestFlag = "TestFlag";
public const string EditContactsUI = "EditContactsUI";
}
2 changes: 1 addition & 1 deletion DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public async Task<IActionResult> OnGetAsync()
Trusts = await GetTrustsForKeywords();

PageStatus = Trusts.PageStatus;
PaginationRouteData = new Dictionary<string, string> { { "Keywords", KeyWords } };
PaginationRouteData = new Dictionary<string, string> { { "Keywords", KeyWords ?? string.Empty } };
return new PageResult();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace DfE.FindInformationAcademiesTrusts.Pages.Shared;
public abstract class BasePageModel : PageModel
{
public bool ShowHeaderSearch { get; init; } = true;
[BindProperty(SupportsGet = true)] public string KeyWords { get; set; } = string.Empty;
[BindProperty(SupportsGet = true)] public string? KeyWords { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ namespace DfE.FindInformationAcademiesTrusts.Pages.Shared;

public interface IPageSearchFormModel
{
string KeyWords { get; set; }
string? KeyWords { get; set; }
string PageSearchFormInputId { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@model Microsoft.AspNetCore.Mvc.RazorPages.PageModel

@if (!Model.ModelState.IsValid)
{
<div class="govuk-error-summary" aria-labelledby="error-summary-title" tabindex="-1" data-module="govuk-error-summary">
<div role="alert">
<h2 class="govuk-error-summary__title" id="error-summary-title">
There is a problem
</h2>
<div class="govuk-error-summary__body">
<ul class="govuk-list govuk-error-summary__list" data-testid="error-summary">
@foreach (var (key, modelState) in Model.ModelState)
{
@foreach (var error in modelState.Errors)
{
<li>
<a href="#@key" data-testid="error-link-@key">@error.ErrorMessage</a>
</li>
}
}
</ul>
</div>
</div>
</div>
}
158 changes: 93 additions & 65 deletions DfE.FindInformationAcademiesTrusts/Pages/Trusts/Contacts.cshtml
Original file line number Diff line number Diff line change
@@ -1,99 +1,127 @@
@page
@using DfE.FindInformationAcademiesTrusts.Configuration
@using DfE.FindInformationAcademiesTrusts.Data
@model ContactsModel

@{
Layout = "_TrustLayout";
}

<section class="govuk-!-margin-top-8 govuk-!-margin-bottom-9">
<div class="govuk-summary-card">
<section class="govuk-!-margin-bottom-9">
<h2 class="govuk-heading-m">
Contacts at DfE
</h2>
<feature name="@FeatureFlags.EditContactsUI">
@if (!string.IsNullOrWhiteSpace(TempData["ContactUpdatedMessage"] as string))
{
<div class="govuk-notification-banner govuk-notification-banner--success" role="alert" aria-labelledby="govuk-notification-banner-title" data-module="govuk-notification-banner">
<div class="govuk-notification-banner__header">
<h2 class="govuk-notification-banner__title" id="govuk-notification-banner-title">
Success
</h2>
</div>
<div class="govuk-notification-banner__content">
<h3 class="govuk-notification-banner__heading">
@TempData["ContactUpdatedMessage"]
</h3>
</div>
</div>
}
</feature>
<div class="govuk-summary-card" data-testid="trust-relationship-manager">
<div class="govuk-summary-card__title-wrapper">
<h2 class="govuk-summary-card__title">DfE contacts</h2>
</div>
<div class="govuk-summary-card__content">
<dl class="govuk-summary-list">
<div class="govuk-summary-list__row" data-testid="trust-relationship-manager">
<dt class="govuk-summary-list__key">
Trust relationship manager
</dt>
@{ DisplayContact(Model.TrustRelationshipManager); }
<h3 class="govuk-summary-card__title">
Trust relationship manager
</h3>
<feature name="@FeatureFlags.EditContactsUI">
<div class="govuk-summary-card__actions">
<a class="govuk-link" asp-page="/Trusts/Contacts/EditTrustRelationshipManager" asp-route-uid="@Model.Uid">Change<span class="govuk-visually-hidden"> details of this contact (Trust relationship manager)</span></a>
</div>
<div class="govuk-summary-list__row" data-testid="sfso-lead">
<dt class="govuk-summary-list__key">
SFSO (Schools financial support and oversight) lead
</dt>
@{ DisplayContact(Model.SfsoLead); }
</feature>
</div>
@{ DisplayContact(Model.TrustRelationshipManager); }
</div>
<div class="govuk-summary-card" data-testid="sfso-lead">
<div class="govuk-summary-card__title-wrapper">
<h3 class="govuk-summary-card__title">
SFSO (Schools financial support and oversight) lead
</h3>
<feature name="@FeatureFlags.EditContactsUI">
<div class="govuk-summary-card__actions">
<a class="govuk-link" asp-page="/Trusts/Contacts/EditSfsoLead" asp-route-uid="@Model.Uid">Change<span class="govuk-visually-hidden"> details of this contact (SFSO Lead)</span></a>
</div>
</dl>
</feature>
</div>
@{ DisplayContact(Model.SfsoLead); }
</div>
</section>

<section class="govuk-!-margin-top-8 govuk-!-margin-bottom-9">
<div class="govuk-summary-card">
<h2 class="govuk-heading-m">
Contacts at the trust
</h2>
<div class="govuk-summary-card" data-testid="accounting-officer">
<div class="govuk-summary-card__title-wrapper">
<h2 class="govuk-summary-card__title">Trust contacts</h2>
<h3 class="govuk-summary-card__title">
Accounting officer
</h3>
</div>
<div class="govuk-summary-card__content">
<dl class="govuk-summary-list">
<div class="govuk-summary-list__row" data-testid="accounting-officer">
<dt class="govuk-summary-list__key">
Accounting officer
</dt>
@{ DisplayContact(Model.AccountingOfficer); }
</div>
<div class="govuk-summary-list__row" data-testid="chair-of-trustees">
<dt class="govuk-summary-list__key">
Chair of trustees
</dt>
@{ DisplayContact(Model.ChairOfTrustees); }
</div>
<div class="govuk-summary-list__row" data-testid="chief-financial-officer">
<dt class="govuk-summary-list__key">
Chief financial officer
</dt>
@{ DisplayContact(Model.ChiefFinancialOfficer); }
</div>

</dl>
@{ DisplayContact(Model.AccountingOfficer); }
</div>
<div class="govuk-summary-card" data-testid="chair-of-trustees">
<div class="govuk-summary-card__title-wrapper">
<h3 class="govuk-summary-card__title">
Chair of trustees
</h3>
</div>
@{ DisplayContact(Model.ChairOfTrustees); }
</div>
<div class="govuk-summary-card" data-testid="chief-financial-officer">
<div class="govuk-summary-card__title-wrapper">
<h3 class="govuk-summary-card__title">
Chief financial officer
</h3>
</div>
@{ DisplayContact(Model.ChiefFinancialOfficer); }
</div>
</section>

@functions {

private void DisplayContact(Person? contactToDisplay)
{
<dd class="govuk-summary-list__value">

@if (contactToDisplay is null)
{
<p class="govuk-body">@ContactsModel.ContactInformationNotAvailableMessage</p>
}
else
{
@if (!string.IsNullOrWhiteSpace(contactToDisplay.FullName))
{
<p class="govuk-body" data-testid="contact-name">@contactToDisplay.FullName</p>
}
else
{
<p class="govuk-body">@ContactsModel.ContactNameNotAvailableMessage</p>
}

<p class="govuk-body">
@if (contactToDisplay.Email is not null)
<div class="govuk-summary-card__content">
<dl class="govuk-summary-list">
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Name
</dt>
@if (!string.IsNullOrWhiteSpace(contactToDisplay?.FullName))
{
<dd class="govuk-summary-list__value" data-testid="contact-name">@contactToDisplay.FullName</dd>
}
else
{
<dd class="govuk-summary-list__value" data-testid="contact-name">@ContactsModel.ContactNameNotAvailableMessage</dd>
}
</div>
<div class="govuk-summary-list__row">
<dt class="govuk-summary-list__key">
Email address
</dt>
@if (contactToDisplay?.Email is not null)
{
<a class="govuk-link" href="mailto:@contactToDisplay.Email" rel="noopener" target="_blank" data-testid="contact-email">@contactToDisplay.Email</a>
<dd class="govuk-summary-list__value">
<a class="govuk-link" href="mailto:@contactToDisplay.Email" rel="noopener" target="_blank" data-testid="contact-email">@contactToDisplay.Email</a>
</dd>
}
else
{
<a class="govuk-body">@ContactsModel.ContactEmailNotAvailableMessage</a>
<dd class="govuk-summary-list__value" data-testid="contact-email">@ContactsModel.ContactEmailNotAvailableMessage</dd>
}
</p>
}
</dd>
</div>
</dl>
</div>
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ public class ContactsModel(

public const string ContactEmailNotAvailableMessage = "No contact email available";

public const string ContactInformationNotAvailableMessage = "No contact information available";

public override async Task<IActionResult> OnGetAsync()
{
var pageResult = await base.OnGetAsync();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.ComponentModel.DataAnnotations;
using DfE.FindInformationAcademiesTrusts.Data;
using DfE.FindInformationAcademiesTrusts.Services.DataSource;
using DfE.FindInformationAcademiesTrusts.Services.Trust;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts;

public abstract class EditContactModel(
ITrustProvider trustProvider,
IDataSourceService dataSourceService,
ITrustService trustService,
ILogger<EditContactModel> logger,
string roleText)
: TrustsAreaModel(trustProvider, dataSourceService, trustService,
logger, $"Edit {roleText}")
{
public const string NameField = "Name";
public const string EmailField = "Email";

[BindProperty] [BindRequired] public string? Name { get; set; }

[BindProperty]
[BindRequired]
[EmailAddress(ErrorMessage = "Enter an email address in the correct format, like [email protected]")]
[RegularExpression(".*@education.gov.uk$", ErrorMessage = "Enter a DfE email address ending in @education.gov.uk")]
public string? Email { get; set; }

[TempData] public string ContactUpdatedMessage { get; set; } = string.Empty;

private string RoleText { get; init; } = roleText;

public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return await base.OnGetAsync();
}

ContactUpdatedMessage = $"Changes made to the {RoleText} were successfully updated.";
return RedirectToPage("/Trusts/Contacts", new { Uid });
}

public string GetErrorClass(string key)
{
return ModelState.ContainsKey(key) && ModelState[key]!.Errors.Any() ? "govuk-form-group--error" : string.Empty;
}

public string GenerateErrorAriaDescribedBy(string key)
{
return string.Join(" ", GetErrorList(key).Select(value => $"error-{key}-{value.index}"));
}

public (int index, string errorMessage)[] GetErrorList(string key)
{
if (ModelState.ContainsKey(key))
{
return ModelState[key]!.Errors.Select((error, index) => (index, error.ErrorMessage)).ToArray();
}

return [];
}

public string GeneratePageTitle()
{
return $"{(ModelState.IsValid ? string.Empty : "Error: ")}{PageTitle ?? PageName} - {TrustSummary.Name}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@page
@model EditSfsoLeadModel

@{
Layout = "_Layout";
ViewData["Title"] = Model.GeneratePageTitle();
}

<partial name="_EditContactsForm" model="@Model"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using DfE.FindInformationAcademiesTrusts.Configuration;
using DfE.FindInformationAcademiesTrusts.Data;
using DfE.FindInformationAcademiesTrusts.Services.DataSource;
using DfE.FindInformationAcademiesTrusts.Services.Trust;
using Microsoft.AspNetCore.Mvc;
using Microsoft.FeatureManagement.Mvc;

namespace DfE.FindInformationAcademiesTrusts.Pages.Trusts.Contacts;

[FeatureGate(FeatureFlags.EditContactsUI)]
public class EditSfsoLeadModel(
ITrustProvider trustProvider,
IDataSourceService dataSourceService,
ILogger<EditSfsoLeadModel> logger,
ITrustService trustService)
: EditContactModel(trustProvider, dataSourceService, trustService,
logger, "SFSO (Schools financial support and oversight) lead")
{
public override async Task<IActionResult> OnGetAsync()
{
var pageResult = await base.OnGetAsync();

if (pageResult.GetType() == typeof(NotFoundResult)) return pageResult;

var contacts = await TrustService.GetTrustContactsAsync(Uid);

Email = contacts.SfsoLead?.Email;
Name = contacts.SfsoLead?.FullName;
return pageResult;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@page
@model EditTrustRelationshipManagerModel

@{
Layout = "_Layout";
ViewData["Title"] = Model.GeneratePageTitle();
}

<partial name="_EditContactsForm" model="@Model"/>
Loading

0 comments on commit e993381

Please sign in to comment.