21 changed files with 331 additions and 40 deletions
@ -0,0 +1,33 @@
|
||||
class AddressOptionsController < ApplicationController |
||||
def index |
||||
query = params[:query] |
||||
service = AddressClient.new(address: query) |
||||
service.call |
||||
|
||||
if service.error.present? |
||||
render json: { error: service.error }, status: :unprocessable_entity |
||||
else |
||||
render json: service.result.map { |result| { address: result["ADDRESS"], uprn: result["UPRN"] } } |
||||
end |
||||
end |
||||
|
||||
def current |
||||
log_id = params[:log_id] |
||||
sales_log = SalesLog.find_by(id: log_id) |
||||
uprn = sales_log&.address_search |
||||
|
||||
if uprn.present? |
||||
service = AddressClient.new(uprn: uprn) |
||||
service.call |
||||
|
||||
if service.error.present? |
||||
render json: { error: service.error }, status: :unprocessable_entity |
||||
else |
||||
address = service.result.find { |result| result["UPRN"] == uprn }&.dig("ADDRESS") |
||||
render json: { stored_value: { uprn:, address: } } |
||||
end |
||||
else |
||||
render json: { stored_value: nil } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,115 @@
|
||||
import {Controller} from '@hotwired/stimulus' |
||||
import accessibleAutocomplete from 'accessible-autocomplete' |
||||
import 'accessible-autocomplete/dist/accessible-autocomplete.min.css' |
||||
import {searchableName} from "../modules/search"; |
||||
|
||||
const options = [] |
||||
|
||||
const fetchOptions = async (query) => { |
||||
const response = await fetch(`/address_options?query=${query}`) |
||||
const data = await response.json() |
||||
console.log(data) |
||||
return data |
||||
} |
||||
|
||||
const fetchAndPopulateSearchResults = async (query, populateResults, populateOptions, selectEl) => { |
||||
if (/\S/.test(query)) { |
||||
const results = await fetchOptions(query) |
||||
console.log(results) // address and uprn keys returned per result
|
||||
populateOptions(results, selectEl) |
||||
populateResults(Object.values(results).map((o) => o.address)) |
||||
// populateResults(results)
|
||||
} |
||||
} |
||||
|
||||
const populateOptions = (results, selectEl) => { |
||||
selectEl.innerHTML = '' |
||||
|
||||
results.forEach((result) => { |
||||
const option = document.createElement('option') |
||||
option.value = result.uprn |
||||
option.innerHTML = result.address |
||||
option.setAttribute('address', result.address) |
||||
selectEl.appendChild(option) |
||||
options.push(option) |
||||
}) |
||||
} |
||||
|
||||
// const populateOptions = (results, selectEl) => {
|
||||
// selectEl.innerHTML = ''
|
||||
//
|
||||
// Object.keys(results).forEach((key) => {
|
||||
// const option = document.createElement('option')
|
||||
// option.value = key
|
||||
// option.innerHTML = results[key].value
|
||||
// if (results[key].hint) { option.setAttribute('data-hint', results[key].hint) }
|
||||
// option.setAttribute('text', searchableName(results[key]))
|
||||
// selectEl.appendChild(option)
|
||||
// options.push(option)
|
||||
// })
|
||||
// }
|
||||
|
||||
export default class extends Controller { |
||||
connect () { |
||||
const selectEl = this.element |
||||
|
||||
const currentValue = this.getCurrentValue() |
||||
console.log(selectEl) |
||||
|
||||
if (currentValue && currentValue.stored_value) { |
||||
console.log(currentValue) |
||||
const option = document.createElement('option') |
||||
option.value = currentValue.stored_value.uprn |
||||
option.innerHTML = currentValue.stored_value.address |
||||
option.selected = true |
||||
selectEl.appendChild(option) |
||||
} |
||||
|
||||
accessibleAutocomplete.enhanceSelectElement({ |
||||
defaultValue: '', |
||||
selectElement: selectEl, |
||||
minLength: 1, |
||||
source: (query, populateResults) => { |
||||
fetchAndPopulateSearchResults(query, populateResults, populateOptions, selectEl) |
||||
}, |
||||
autoselect: true, |
||||
showNoOptionsFound: true, |
||||
placeholder: currentValue?.stored_value?.address || 'Start typing to search', |
||||
templates: { suggestion: (value) => value }, |
||||
onConfirm: (val) => { |
||||
const selectedResult = Array.from(selectEl.options).find(option => option.address === val) |
||||
|
||||
if (selectedResult) { |
||||
selectedResult.selected = true |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
fetchOptions(query, populateResults) { |
||||
fetch(`/address_options?query=${query}`) |
||||
.then(response => response.json()) |
||||
.then(data => { |
||||
console.log(data) |
||||
const results = data.map(result => result.uprn) |
||||
populateResults(results.slice(0, 10)) |
||||
}) |
||||
} |
||||
|
||||
async getCurrentValue() { |
||||
const currentPageUrl = window.location.href; |
||||
console.log(currentPageUrl); |
||||
const match = currentPageUrl.match(/sales-logs\/(\d+)\/address-search/); |
||||
const id = match ? match[1] : null; |
||||
|
||||
if (id) { |
||||
const response = await fetch(`/address_options/current?log_id=${id}`); |
||||
const data = await response.json(); |
||||
console.log(data); |
||||
return data; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,2 @@
|
||||
module AddressOptionsHelper |
||||
end |
@ -0,0 +1,24 @@
|
||||
class Form::Sales::Pages::AddressSearch < ::Form::Page |
||||
def initialize(id, hsh, subsection) |
||||
super |
||||
@id = "address_search" |
||||
@copy_key = "sales.property_information.address_search" |
||||
@depends_on = [ |
||||
{ "uprn_known" => nil }, |
||||
{ "uprn_known" => 0 }, |
||||
{ "uprn_confirmed" => 0 }, |
||||
] |
||||
end |
||||
|
||||
def questions |
||||
@questions ||= [ |
||||
Form::Sales::Questions::AddressSearch.new(nil, nil, self), |
||||
] |
||||
end |
||||
|
||||
def skip_href(log = nil) |
||||
return unless log |
||||
|
||||
"/#{log.model_name.param_key.dasherize}s/#{log.id}/property-number-of-bedrooms" |
||||
end |
||||
end |
@ -0,0 +1,32 @@
|
||||
class Form::Sales::Questions::AddressSearch < ::Form::Question |
||||
def initialize(id, hsh, page) |
||||
super |
||||
@id = "address_search" |
||||
@copy_key = "sales.property_information.address_search" |
||||
@error_label = "Enter search query" |
||||
@type = "address_autocomplete" |
||||
@plain_label = true |
||||
end |
||||
|
||||
def answer_options(log = nil, _user = nil) |
||||
return {} unless ActiveRecord::Base.connected? |
||||
return {} unless log&.address_options |
||||
|
||||
answer_opts = {} |
||||
|
||||
(0...[log.address_options.count, 10].min).each do |i| |
||||
answer_opts[log.address_options[i][:uprn]] = { "value" => log.address_options[i][:address] } |
||||
end |
||||
|
||||
answer_opts["divider"] = { "value" => true } |
||||
answer_opts |
||||
end |
||||
|
||||
def displayed_answer_options(log, user = nil) |
||||
answer_options(log, user).transform_values { |value| value["value"] } || {} |
||||
end |
||||
|
||||
def hidden_in_check_answers?(log, _current_user = nil) |
||||
(log.uprn_known == 1 || log.uprn_confirmed == 1) |
||||
end |
||||
end |
@ -0,0 +1,23 @@
|
||||
<% selected = @log.public_send(question.id) || "" %> |
||||
<% answers = question.displayed_answer_options(@log, current_user).map { |key, value| OpenStruct.new(id: key, name: select_option_name(value), resource: value) } %> |
||||
<%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %> |
||||
|
||||
<%= f.govuk_select(question.id.to_sym, |
||||
label: legend(question, page_header, conditional), |
||||
"data-controller": "address-autocomplete", |
||||
caption: caption(caption_text, page_header, conditional), |
||||
hint: { text: question.hint_text&.html_safe }) do %> |
||||
<% if answers.any? %> |
||||
<% answers.each do |answer| %> |
||||
<option value="<%= answer.id %>" |
||||
data-synonyms="<%= answer_option_synonyms(answer.resource) %>" |
||||
data-append="<%= answer_option_append(answer.resource) %>" |
||||
data-hint="<%= answer_option_hint(answer.resource) %>" |
||||
<%= question.answer_selected?(@log, answer) ? "selected" : "" %>><%= answer.name || answer.resource %></option> |
||||
<% end %> |
||||
<% else %> |
||||
<option value="" disabled></option> |
||||
<% end %> |
||||
<% end %> |
||||
|
||||
<%= render partial: "form/guidance/#{question.bottom_guidance_partial}" if question.bottom_guidance? %> |
@ -1,20 +1,23 @@
|
||||
|
||||
<% selected = @log.public_send(question.id) || "" %> |
||||
<% answers = question.displayed_answer_options(@log, current_user).map { |key, value| OpenStruct.new(id: key, name: select_option_name(value), resource: value) } %> |
||||
<%= render partial: "form/guidance/#{question.top_guidance_partial}" if question.top_guidance? %> |
||||
|
||||
<%= f.govuk_select(question.id.to_sym, |
||||
label: legend(question, page_header, conditional), |
||||
"data-controller": "accessible-autocomplete", |
||||
caption: caption(caption_text, page_header, conditional), |
||||
hint: { text: question.hint_text&.html_safe }) do %> |
||||
label: legend(question, page_header, conditional), |
||||
"data-controller": "accessible-autocomplete", |
||||
caption: caption(caption_text, page_header, conditional), |
||||
hint: { text: question.hint_text&.html_safe }) do %> |
||||
<% if answers.any? %> |
||||
<% answers.each do |answer| %> |
||||
<option value="<%= answer.id %>" |
||||
data-synonyms="<%= answer_option_synonyms(answer.resource) %>" |
||||
data-append="<%= answer_option_append(answer.resource) %>" |
||||
data-hint="<%= answer_option_hint(answer.resource) %>" |
||||
<%= question.answer_selected?(@log, answer) ? "selected" : "" %>><%= answer.name || answer.resource %></option> |
||||
data-synonyms="<%= answer_option_synonyms(answer.resource) %>" |
||||
data-append="<%= answer_option_append(answer.resource) %>" |
||||
data-hint="<%= answer_option_hint(answer.resource) %>" |
||||
<%= question.answer_selected?(@log, answer) ? "selected" : "" %>><%= answer.name || answer.resource %></option> |
||||
<% end %> |
||||
<% else %> |
||||
<option value="" disabled></option> |
||||
<% end %> |
||||
<% end %> |
||||
|
||||
<%= render partial: "form/guidance/#{question.bottom_guidance_partial}" if question.bottom_guidance? %> |
||||
|
@ -0,0 +1,5 @@
|
||||
class AddAddressSearchToSalesLogs < ActiveRecord::Migration[7.0] |
||||
def change |
||||
add_column :sales_logs, :address_search, :string |
||||
end |
||||
end |
Loading…
Reference in new issue