Skip to content

Commit 0e798ee

Browse files
FrostyApeOneFrostyApeOne
authored andcommitted
Merged
2 parents 4e90dc1 + 8d345bf commit 0e798ee

16 files changed

Lines changed: 1706 additions & 406 deletions

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,5 @@
2525
<ProjectReference Include="..\DfE.ExternalApplications.Infrastructure\DfE.ExternalApplications.Infrastructure.csproj" />
2626
</ItemGroup>
2727

28-
<ItemGroup>
29-
<Content Update="templates\form-transfers.json">
30-
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
31-
</Content>
32-
</ItemGroup>
33-
3428

3529
</Project>

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

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,34 +48,63 @@
4848
var fieldValue = Model.GetFieldValue(field.FieldId);
4949
var hasValue = Model.HasFieldValue(field.FieldId);
5050

51-
<div class="govuk-summary-list__row">
52-
<dt class="govuk-summary-list__key">
53-
@field.Label.Value
54-
</dt>
55-
<dd class="govuk-summary-list__value">
56-
@if (hasValue)
57-
{
58-
@if (field.Type == "radios" && field.Options != null)
51+
if (field.Type == "autocomplete" && hasValue)
52+
{
53+
// Handle multiple autocomplete values as separate rows
54+
var formattedValues = Model.GetFormattedFieldValues(field.FieldId);
55+
var itemLabel = Model.GetFieldItemLabel(field.FieldId);
56+
var allowMultiple = Model.IsFieldAllowMultiple(field.FieldId);
57+
58+
for (int i = 0; i < formattedValues.Count; i++)
59+
{
60+
<div class="govuk-summary-list__row">
61+
<dt class="govuk-summary-list__key">
62+
@if (allowMultiple)
63+
{
64+
@($"{itemLabel} {i + 1}")
65+
}
66+
else
67+
{
68+
@field.Label.Value
69+
}
70+
</dt>
71+
<dd class="govuk-summary-list__value">
72+
@Html.Raw(formattedValues[i])
73+
</dd>
74+
</div>
75+
}
76+
}
77+
else
78+
{
79+
<div class="govuk-summary-list__row">
80+
<dt class="govuk-summary-list__key">
81+
@field.Label.Value
82+
</dt>
83+
<dd class="govuk-summary-list__value">
84+
@if (hasValue)
5985
{
60-
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
61-
@(selectedOption?.Label ?? fieldValue)
62-
}
63-
else if (field.Type == "select" && field.Options != null)
64-
{
65-
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
66-
@(selectedOption?.Label ?? fieldValue)
86+
@if (field.Type == "radios" && field.Options != null)
87+
{
88+
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
89+
@(selectedOption?.Label ?? fieldValue)
90+
}
91+
else if (field.Type == "select" && field.Options != null)
92+
{
93+
var selectedOption = field.Options.FirstOrDefault(o => o.Value == fieldValue);
94+
@(selectedOption?.Label ?? fieldValue)
95+
}
96+
else
97+
{
98+
@fieldValue
99+
}
67100
}
68101
else
69102
{
70-
@fieldValue
103+
<span class="govuk-hint">Not answered</span>
71104
}
72-
}
73-
else
74-
{
75-
<span class="govuk-hint">Not answered</span>
76-
}
77-
</dd>
78-
</div>
105+
</dd>
106+
</div>
107+
}
79108
}
80109
}
81110
</dl>

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

Lines changed: 230 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,240 @@ public string GetFieldValue(string fieldId)
154154
{
155155
if (FormData.TryGetValue(fieldId, out var value))
156156
{
157-
return value?.ToString() ?? string.Empty;
157+
if (value == null)
158+
{
159+
return string.Empty;
160+
}
161+
162+
// If it's already a string, return it
163+
if (value is string stringValue)
164+
{
165+
return stringValue;
166+
}
167+
168+
// If it's an object (like from autocomplete), serialize it to JSON
169+
try
170+
{
171+
return JsonSerializer.Serialize(value);
172+
}
173+
catch
174+
{
175+
return value.ToString() ?? string.Empty;
176+
}
158177
}
159178
return string.Empty;
160179
}
161180

181+
public string GetFormattedFieldValue(string fieldId)
182+
{
183+
var fieldValue = GetFieldValue(fieldId);
184+
185+
if (string.IsNullOrEmpty(fieldValue))
186+
{
187+
return string.Empty;
188+
}
189+
190+
// Try to format as autocomplete data if it looks like JSON
191+
if (fieldValue.StartsWith("{") || fieldValue.StartsWith("["))
192+
{
193+
return FormatAutocompleteValue(fieldValue);
194+
}
195+
196+
return fieldValue;
197+
}
198+
199+
public List<string> GetFormattedFieldValues(string fieldId)
200+
{
201+
var fieldValue = GetFieldValue(fieldId);
202+
203+
if (string.IsNullOrEmpty(fieldValue))
204+
{
205+
return new List<string>();
206+
}
207+
208+
// Try to format as autocomplete data if it looks like JSON
209+
if (fieldValue.StartsWith("{") || fieldValue.StartsWith("["))
210+
{
211+
return FormatAutocompleteValuesList(fieldValue);
212+
}
213+
214+
return new List<string> { fieldValue };
215+
}
216+
217+
public string GetFieldItemLabel(string fieldId)
218+
{
219+
// Find the field in the template
220+
var field = Template?.TaskGroups?
221+
.SelectMany(g => g.Tasks)
222+
.SelectMany(t => t.Pages)
223+
.SelectMany(p => p.Fields)
224+
.FirstOrDefault(f => f.FieldId == fieldId);
225+
226+
if (field?.ComplexField != null)
227+
{
228+
try
229+
{
230+
var complexField = JsonSerializer.Deserialize<Dictionary<string, object>>(field.ComplexField);
231+
if (complexField?.ContainsKey("properties") == true)
232+
{
233+
var properties = JsonSerializer.Deserialize<Dictionary<string, object>>(complexField["properties"].ToString());
234+
if (properties?.ContainsKey("label") == true)
235+
{
236+
return properties["label"].ToString();
237+
}
238+
}
239+
}
240+
catch
241+
{
242+
// If parsing fails, return default
243+
}
244+
}
245+
246+
// Default label if not found in properties
247+
return "Item";
248+
}
249+
250+
public bool IsFieldAllowMultiple(string fieldId)
251+
{
252+
// Find the field in the template
253+
var field = Template?.TaskGroups?
254+
.SelectMany(g => g.Tasks)
255+
.SelectMany(t => t.Pages)
256+
.SelectMany(p => p.Fields)
257+
.FirstOrDefault(f => f.FieldId == fieldId);
258+
259+
if (field?.ComplexField != null)
260+
{
261+
try
262+
{
263+
var complexField = JsonSerializer.Deserialize<Dictionary<string, object>>(field.ComplexField);
264+
if (complexField?.ContainsKey("properties") == true)
265+
{
266+
var properties = JsonSerializer.Deserialize<Dictionary<string, object>>(complexField["properties"].ToString());
267+
if (properties?.ContainsKey("allowMultiple") == true)
268+
{
269+
return bool.Parse(properties["allowMultiple"].ToString());
270+
}
271+
}
272+
}
273+
catch
274+
{
275+
// If parsing fails, return default
276+
}
277+
}
278+
279+
return false; // Default to single selection
280+
}
281+
282+
private string FormatAutocompleteValue(string value)
283+
{
284+
if (string.IsNullOrEmpty(value))
285+
{
286+
return string.Empty;
287+
}
288+
289+
try
290+
{
291+
using (var doc = JsonDocument.Parse(value))
292+
{
293+
if (doc.RootElement.ValueKind == JsonValueKind.Array)
294+
{
295+
var displayValues = new List<string>();
296+
foreach (var element in doc.RootElement.EnumerateArray())
297+
{
298+
displayValues.Add(FormatSingleAutocompleteValue(element));
299+
}
300+
return string.Join("<br />", displayValues);
301+
}
302+
else if (doc.RootElement.ValueKind == JsonValueKind.Object)
303+
{
304+
return FormatSingleAutocompleteValue(doc.RootElement);
305+
}
306+
}
307+
}
308+
catch
309+
{
310+
// If not JSON, return as is
311+
}
312+
313+
return value;
314+
}
315+
316+
private List<string> FormatAutocompleteValuesList(string value)
317+
{
318+
if (string.IsNullOrEmpty(value))
319+
{
320+
return new List<string>();
321+
}
322+
323+
try
324+
{
325+
using (var doc = JsonDocument.Parse(value))
326+
{
327+
if (doc.RootElement.ValueKind == JsonValueKind.Array)
328+
{
329+
var displayValues = new List<string>();
330+
foreach (var element in doc.RootElement.EnumerateArray())
331+
{
332+
displayValues.Add(FormatSingleAutocompleteValue(element));
333+
}
334+
return displayValues;
335+
}
336+
else if (doc.RootElement.ValueKind == JsonValueKind.Object)
337+
{
338+
return new List<string> { FormatSingleAutocompleteValue(doc.RootElement) };
339+
}
340+
}
341+
}
342+
catch
343+
{
344+
// If not JSON, return as single item
345+
}
346+
347+
return new List<string> { value };
348+
}
349+
350+
private string FormatSingleAutocompleteValue(JsonElement element)
351+
{
352+
if (element.ValueKind == JsonValueKind.Object)
353+
{
354+
string name = "";
355+
string ukprn = "";
356+
357+
if (element.TryGetProperty("name", out var nameProperty) && nameProperty.ValueKind == JsonValueKind.String)
358+
{
359+
name = nameProperty.GetString() ?? "";
360+
}
361+
362+
if (element.TryGetProperty("ukprn", out var ukprnProperty))
363+
{
364+
if (ukprnProperty.ValueKind == JsonValueKind.String)
365+
{
366+
ukprn = ukprnProperty.GetString() ?? "";
367+
}
368+
else if (ukprnProperty.ValueKind == JsonValueKind.Number)
369+
{
370+
ukprn = ukprnProperty.GetInt64().ToString();
371+
}
372+
}
373+
374+
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(ukprn))
375+
{
376+
return $"{System.Web.HttpUtility.HtmlEncode(name)} (UKPRN: {System.Web.HttpUtility.HtmlEncode(ukprn)})";
377+
}
378+
else if (!string.IsNullOrEmpty(name))
379+
{
380+
return System.Web.HttpUtility.HtmlEncode(name);
381+
}
382+
}
383+
else if (element.ValueKind == JsonValueKind.String)
384+
{
385+
return System.Web.HttpUtility.HtmlEncode(element.GetString() ?? "");
386+
}
387+
388+
return System.Web.HttpUtility.HtmlEncode(element.ToString());
389+
}
390+
162391
public bool HasFieldValue(string fieldId)
163392
{
164393
var value = GetFieldValue(fieldId);

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,22 @@ public class RenderFormModel : PageModel
3232
private readonly IFormTemplateProvider _templateProvider;
3333
private readonly IApplicationResponseService _applicationResponseService;
3434
private readonly IApplicationsClient _applicationsClient;
35+
private readonly IAutocompleteService _autocompleteService;
3536
private readonly ILogger<RenderFormModel> _logger;
3637

3738
public RenderFormModel(
3839
IFieldRendererService renderer,
3940
IFormTemplateProvider templateProvider,
4041
IApplicationResponseService applicationResponseService,
4142
IApplicationsClient applicationsClient,
43+
IAutocompleteService autocompleteService,
4244
ILogger<RenderFormModel> logger)
4345
{
4446
_renderer = renderer;
4547
_templateProvider = templateProvider;
4648
_applicationResponseService = applicationResponseService;
4749
_applicationsClient = applicationsClient;
50+
_autocompleteService = autocompleteService;
4851
_logger = logger;
4952
}
5053

@@ -142,6 +145,29 @@ public async Task<IActionResult> OnPostPageAsync()
142145
return RedirectToPage("/TaskSummary", new { referenceNumber = ReferenceNumber, taskId = CurrentTask.TaskId });
143146
}
144147

148+
public async Task<IActionResult> OnGetAutocompleteAsync(string endpoint, string query)
149+
{
150+
_logger.LogInformation("Autocomplete search called with endpoint: {Endpoint}, query: {Query}", endpoint, query);
151+
152+
if (string.IsNullOrWhiteSpace(endpoint))
153+
{
154+
_logger.LogWarning("Autocomplete search called without endpoint");
155+
return new JsonResult(new List<object>());
156+
}
157+
158+
try
159+
{
160+
var results = await _autocompleteService.SearchAsync(endpoint, query);
161+
_logger.LogInformation("Autocomplete search returned {Count} results", results.Count);
162+
return new JsonResult(results);
163+
}
164+
catch (Exception ex)
165+
{
166+
_logger.LogError(ex, "Error in autocomplete search endpoint: {Endpoint}, query: {Query}", endpoint, query);
167+
return new JsonResult(new List<object>());
168+
}
169+
}
170+
145171
private async Task LoadTemplateAsync()
146172
{
147173
Template = await _templateProvider.GetTemplateAsync(TemplateId);

0 commit comments

Comments
 (0)