Skip to content

Commit 2bbb6db

Browse files
FrostyApeOneFrostyApeOne
andauthored
Bug/228846 fix academies order (#42)
* Fixed order of the academies and how the change link is displayed in the summary page * Fixed an issue where autocomplete data weren't being saved * Fixed the missing break line in the summary page for the text fields --------- Co-authored-by: FrostyApeOne <Farshad.DASHTI@EDUCATION.GOV.UK>
1 parent 130f5eb commit 2bbb6db

7 files changed

Lines changed: 245 additions & 141 deletions

File tree

src/DfE.ExternalApplications.Infrastructure/Services/ApplicationResponseService.cs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,51 @@ public void AccumulateFormData(Dictionary<string, object> newData, ISession sess
8585
{
8686
var existingData = GetAccumulatedFormData(session);
8787

88-
// Merge new data into existing data
8988
foreach (var kvp in newData)
9089
{
91-
existingData[kvp.Key] = kvp.Value;
90+
var normalizedFieldName = NormalizeFieldName(kvp.Key);
91+
var fieldNameToUse = normalizedFieldName;
92+
93+
logger.LogDebug("Normalizing field name: '{OriginalKey}' -> '{NormalizedKey}'", kvp.Key, fieldNameToUse);
94+
95+
existingData[fieldNameToUse] = kvp.Value;
96+
97+
var alternativeKeys = existingData.Keys
98+
.Where(key => key != fieldNameToUse && AreEquivalentFieldNames(key, fieldNameToUse))
99+
.ToList();
100+
101+
foreach (var altKey in alternativeKeys)
102+
{
103+
logger.LogDebug("Removing duplicate field entry: {OldKey} in favor of {NewKey}", altKey, fieldNameToUse);
104+
existingData.Remove(altKey);
105+
}
92106
}
93107

94-
// Save back to session
95108
var jsonString = JsonSerializer.Serialize(existingData);
96109
session.SetString(SessionKeyFormData, jsonString);
97110
}
98111

112+
private bool AreEquivalentFieldNames(string fieldName1, string fieldName2)
113+
{
114+
var normalized1 = NormalizeFieldName(fieldName1);
115+
var normalized2 = NormalizeFieldName(fieldName2);
116+
117+
return string.Equals(normalized1, normalized2, StringComparison.OrdinalIgnoreCase);
118+
}
119+
120+
private string NormalizeFieldName(string fieldName)
121+
{
122+
if (string.IsNullOrEmpty(fieldName))
123+
return fieldName;
124+
125+
if (fieldName.StartsWith("Data_", StringComparison.OrdinalIgnoreCase))
126+
{
127+
return fieldName.Substring(5);
128+
}
129+
130+
return fieldName;
131+
}
132+
99133
public Dictionary<string, object> GetAccumulatedFormData(ISession session)
100134
{
101135
var jsonString = session.GetString(SessionKeyFormData);

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

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,46 @@
5757

5858
if ((field.Type == "autocomplete" || field.Type == "complexField") && hasValue)
5959
{
60-
// Handle multiple autocomplete/complex field values as separate rows
60+
// Handle multiple autocomplete/complex field values as main header + individual rows
6161
var formattedValues = Model.GetFormattedFieldValues(field.FieldId);
6262
var itemLabel = Model.GetFieldItemLabel(field.FieldId);
6363
var allowMultiple = Model.IsFieldAllowMultiple(field.FieldId);
6464

65-
for (int i = 0; i < formattedValues.Count; i++)
65+
// Show main field header
66+
<div class="govuk-summary-list__row">
67+
<dt class="govuk-summary-list__key">
68+
@field.Label.Value
69+
</dt>
70+
<dd class="govuk-summary-list__value">
71+
@if (formattedValues.Count == 0)
72+
{
73+
<span class="govuk-hint">Not answered</span>
74+
}
75+
else if (!allowMultiple)
76+
{
77+
@Html.Raw(formattedValues.FirstOrDefault())
78+
}
79+
else
80+
{
81+
<span class="govuk-hint">@formattedValues.Count item@(formattedValues.Count == 1 ? "" : "s") selected</span>
82+
}
83+
</dd>
84+
</div>
85+
86+
// Show individual items if multiple selection is enabled
87+
@if (allowMultiple && formattedValues.Count > 0)
6688
{
67-
<div class="govuk-summary-list__row">
68-
<dt class="govuk-summary-list__key">
69-
@if (allowMultiple)
70-
{
89+
for (int i = 0; i < formattedValues.Count; i++)
90+
{
91+
<div class="govuk-summary-list__row">
92+
<dt class="govuk-summary-list__key">
7193
@($"{itemLabel} {i + 1}")
72-
}
73-
else
74-
{
75-
@field.Label.Value
76-
}
77-
</dt>
78-
<dd class="govuk-summary-list__value">
79-
@Html.Raw(formattedValues[i])
80-
</dd>
81-
</div>
94+
</dt>
95+
<dd class="govuk-summary-list__value">
96+
@Html.Raw(formattedValues[i])
97+
</dd>
98+
</div>
99+
}
82100
}
83101
}
84102
else
@@ -90,20 +108,20 @@
90108
<dd class="govuk-summary-list__value">
91109
@if (hasValue)
92110
{
93-
@if (field.Type == "radios" && field.Options != null)
94-
{
95-
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
96-
@(selectedOption?.Label ?? fieldValue)
97-
}
98-
else if (field.Type == "select" && field.Options != null)
99-
{
100-
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
101-
@(selectedOption?.Label ?? fieldValue)
102-
}
103-
else
104-
{
105-
@fieldValue
106-
}
111+
@if (field.Type == "radios" && field.Options != null)
112+
{
113+
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
114+
@(selectedOption?.Label ?? fieldValue)
115+
}
116+
else if (field.Type == "select" && field.Options != null)
117+
{
118+
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
119+
@(selectedOption?.Label ?? fieldValue)
120+
}
121+
else
122+
{
123+
@Html.Raw(fieldValue.Replace("\r\n", "<br/>").Replace("\r", "<br/>").Replace("\n", "<br/>"))
124+
}
107125
}
108126
else
109127
{

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

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,41 +20,63 @@
2020

2121
var fieldIdHyphenated = field.FieldId.Replace(" ", "-").ToLower();
2222

23-
if ((field.Type == "autocomplete" || field.Type == "complexField") && hasValue)
23+
if ((field.Type == "autocomplete" || field.Type == "complexField") && hasValue)
2424
{
25-
// Handle multiple autocomplete/complex field values as separate rows
25+
// Handle multiple autocomplete/complex field values as main header + individual rows
2626
var formattedValues = Model.GetFormattedFieldValues(field.FieldId);
2727
var itemLabel = Model.GetFieldItemLabel(field.FieldId);
2828
var allowMultiple = Model.IsFieldAllowMultiple(field.FieldId);
2929

3030
var itemLabelHyphenated = itemLabel.Replace(" ", "-").ToLower();
31-
32-
for (int i = 0; i < formattedValues.Count; i++)
31+
32+
// Show main field header with change link
33+
<div class="govuk-summary-list__row" id="field-@fieldIdHyphenated" data-testid="field-@fieldIdHyphenated">
34+
<dt class="govuk-summary-list__key">
35+
@field.Label.Value
36+
</dt>
37+
<dd class="govuk-summary-list__value">
38+
@if (formattedValues.Count == 0)
39+
{
40+
<span class="govuk-hint">Not answered</span>
41+
}
42+
else if (!allowMultiple)
43+
{
44+
@Html.Raw(formattedValues.FirstOrDefault())
45+
}
46+
</dd>
47+
<dd class="govuk-summary-list__actions">
48+
@if (Model.IsApplicationEditable())
49+
{
50+
<a class="govuk-link" href="@changeUrl" id="field-@fieldIdHyphenated-change-link" data-testid="field-@fieldIdHyphenated-change-link">
51+
Change<span class="govuk-visually-hidden"> @field.Label.Value</span>
52+
</a>
53+
}
54+
</dd>
55+
</div>
56+
57+
// Show individual items if multiple selection is enabled
58+
@if (allowMultiple && formattedValues.Count > 0)
3359
{
34-
var fieldIndex = i + 1;
35-
<div class="govuk-summary-list__row" id="field-@itemLabelHyphenated-@fieldIndex" data-testid="field-@itemLabelHyphenated-@fieldIndex">
36-
<dt class="govuk-summary-list__key">
37-
@if (allowMultiple)
38-
{
60+
for (int i = 0; i < formattedValues.Count; i++)
61+
{
62+
var fieldIndex = i + 1;
63+
<div class="govuk-summary-list__row" id="field-@itemLabelHyphenated-@fieldIndex" data-testid="field-@itemLabelHyphenated-@fieldIndex">
64+
<dt class="govuk-summary-list__key">
3965
@($"{itemLabel} {fieldIndex}")
40-
}
41-
else
42-
{
43-
@field.Label.Value
44-
}
45-
</dt>
46-
<dd class="govuk-summary-list__value">
47-
@Html.Raw(formattedValues[i])
48-
</dd>
49-
<dd class="govuk-summary-list__actions">
50-
@if (Model.IsApplicationEditable() && i == 0)
51-
{
52-
<a class="govuk-link" href="@changeUrl" id="field-@fieldIdHyphenated-change-link" data-testid="field-@fieldIdHyphenated-change-link">
53-
Change<span class="govuk-visually-hidden"> @field.Label.Value</span>
54-
</a>
55-
}
56-
</dd>
57-
</div>
66+
</dt>
67+
<dd class="govuk-summary-list__value">
68+
@Html.Raw(formattedValues[i])
69+
</dd>
70+
<dd class="govuk-summary-list__actions">
71+
@if (Model.IsApplicationEditable())
72+
{
73+
<a class="govuk-link" href="@changeUrl?removeItem=@i" id="field-@itemLabelHyphenated-@fieldIndex-remove-link" data-testid="field-@itemLabelHyphenated-@fieldIndex-remove-link">
74+
Remove<span class="govuk-visually-hidden"> @($"{itemLabel} {fieldIndex}")</span>
75+
</a>
76+
}
77+
</dd>
78+
</div>
79+
}
5880
}
5981
}
6082
else
@@ -78,7 +100,7 @@
78100
}
79101
else
80102
{
81-
@fieldValue
103+
@Html.Raw(fieldValue.Replace("\r\n", "<br/>").Replace("\r", "<br/>").Replace("\n", "<br/>"))
82104
}
83105
}
84106
else

src/DfE.ExternalApplications.Web/Pages/FormEngine/RenderForm.cshtml.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using DfE.ExternalApplications.Web.Services;
55
using Microsoft.AspNetCore.Mvc;
66
using System.Diagnostics.CodeAnalysis;
7+
using System.Text.Json;
78
using System.Text.RegularExpressions;
89
using Task = System.Threading.Tasks.Task;
910

@@ -56,6 +57,9 @@ public async Task OnGetAsync()
5657

5758
// Load accumulated form data from session to pre-populate fields
5859
LoadAccumulatedDataFromSession();
60+
61+
// Handle remove item request for autocomplete fields
62+
await HandleRemoveItemRequestAsync();
5963
}
6064

6165
public async Task<IActionResult> OnPostPageAsync()
@@ -252,6 +256,63 @@ private void LoadAccumulatedDataFromSession()
252256
}
253257
}
254258

259+
/// <summary>
260+
/// Handles the removal of individual items from autocomplete fields
261+
/// </summary>
262+
private async Task HandleRemoveItemRequestAsync()
263+
{
264+
if (Request.Query.TryGetValue("removeItem", out var removeItemValue) &&
265+
int.TryParse(removeItemValue, out var itemIndex))
266+
{
267+
// Find the autocomplete field in the current page
268+
var autocompleteField = CurrentPage?.Fields?.FirstOrDefault(f =>
269+
f.Type == "autocomplete" || f.Type == "complexField");
270+
271+
if (autocompleteField != null && Data.ContainsKey(autocompleteField.FieldId))
272+
{
273+
var currentValue = Data[autocompleteField.FieldId]?.ToString();
274+
275+
if (!string.IsNullOrEmpty(currentValue))
276+
{
277+
try
278+
{
279+
// Parse the JSON array
280+
var items = JsonSerializer.Deserialize<JsonElement[]>(currentValue);
281+
282+
// Remove the item at the specified index
283+
if (itemIndex >= 0 && itemIndex < items.Length)
284+
{
285+
var updatedItems = items.Where((item, index) => index != itemIndex).ToArray();
286+
287+
// Serialize back to JSON
288+
var updatedValue = JsonSerializer.Serialize(updatedItems);
289+
Data[autocompleteField.FieldId] = updatedValue;
290+
291+
// Save the updated data to session and API
292+
_applicationResponseService.AccumulateFormData(Data, HttpContext.Session);
293+
294+
if (ApplicationId.HasValue)
295+
{
296+
await _applicationResponseService.SaveApplicationResponseAsync(ApplicationId.Value, Data, HttpContext.Session);
297+
}
298+
299+
_logger.LogInformation("Removed item at index {ItemIndex} from field {FieldId}",
300+
itemIndex, autocompleteField.FieldId);
301+
}
302+
}
303+
catch (JsonException ex)
304+
{
305+
_logger.LogWarning(ex, "Failed to parse autocomplete field value for removal: {FieldId}",
306+
autocompleteField.FieldId);
307+
}
308+
}
309+
}
310+
311+
// Redirect back to the summary page to show updated data
312+
Response.Redirect($"/applications/{ReferenceNumber}/{TaskId}/summary");
313+
}
314+
}
315+
255316
/// <summary>
256317
/// Calculate overall application status based on task statuses
257318
/// </summary>

0 commit comments

Comments
 (0)