|
defmodule MedicodeWeb.Components.CodeSelect do |
|
@moduledoc """ |
|
An auto-complete select field that allows users to search for codes to add by either `code` or `description`. |
|
""" |
|
|
|
use MedicodeWeb, :live_component |
|
|
|
import MedicodeWeb.Components, only: [code_display: 1] |
|
|
|
alias Medicode.Feedback |
|
alias Medicode.Transcriptions |
|
|
|
@impl Phoenix.LiveComponent |
|
def mount(socket) do |
|
socket = |
|
socket |
|
|> assign(:codes, []) |
|
|> assign(:selected_code, nil) |
|
|> assign(:submitted_code_feedback, nil) |
|
|
|
{:ok, socket} |
|
end |
|
|
|
@impl Phoenix.LiveComponent |
|
def update( |
|
%{id: id, text: text, current_user: current_user, chunk: chunk} = _assigns, |
|
socket |
|
) do |
|
form = to_form(%{"search_term" => ""}, as: id) |
|
|
|
socket = |
|
socket |
|
|> assign(:form, form) |
|
|> assign(:text, text) |
|
|> assign(:current_user, current_user) |
|
|> assign(:chunk, chunk) |
|
|
|
{:ok, socket} |
|
end |
|
|
|
@impl Phoenix.LiveComponent |
|
def render(assigns) do |
|
~H""" |
|
<div class="w-full" phx-click-away="clear-codes" phx-target={@myself}> |
|
<%= if is_nil(@selected_code) do %> |
|
<.form for={@form} phx-change="suggest-code" phx-target={@myself} class="relative"> |
|
<.input |
|
type="text" |
|
field={@form[:search_term]} |
|
class="w-full rounded" |
|
style={if length(@codes) > 0, do: "border-radius: 0.25rem 0.25rem 0 0 !important;"} |
|
placeholder="Search for another code..." |
|
/> |
|
|
|
<div |
|
:if={length(@codes) > 0} |
|
class="absolute z-10 w-full bg-white border-l border-r border-b border-zinc-400 rounded-b overflow-hidden" |
|
> |
|
<button |
|
:for={code <- @codes} |
|
type="button" |
|
class="w-full text-left px-2 py-1 text-sm border-b border-zinc-100 last:border-b-0 hover:bg-blue-200" |
|
phx-click="choose-code" |
|
phx-value-code={code.code} |
|
phx-target={@myself} |
|
> |
|
<%= code.code %>: <%= code.description %> |
|
</button> |
|
</div> |
|
</.form> |
|
<% else %> |
|
<div class="px-[14px]"> |
|
<div class="flex justify-between pb-1 border-b border-zinc-300"> |
|
<p class="leading-normal font-bold text-type-black-primary uppercase"> |
|
User-selected code |
|
</p> |
|
<button type="button" phx-click="clear-code" phx-target={@myself}> |
|
<.icon name="hero-x-circle" /> |
|
</button> |
|
</div> |
|
|
|
<.code_display code={@selected_code.code} label={@selected_code.description} /> |
|
</div> |
|
<% end %> |
|
</div> |
|
""" |
|
end |
|
|
|
@impl Phoenix.LiveComponent |
|
def handle_event("suggest-code", params, socket) do |
|
form_id = socket.assigns.form.id |
|
%{^form_id => %{"search_term" => value}} = params |
|
suggested_codes = Medicode.Coding.search_for_code_vector(value) |
|
|
|
{:noreply, assign(socket, :codes, suggested_codes)} |
|
end |
|
|
|
def handle_event("clear-codes", _params, socket) do |
|
{:noreply, assign(socket, :codes, [])} |
|
end |
|
|
|
def handle_event("choose-code", %{"code" => code}, socket) do |
|
selected_code = Enum.find(socket.assigns.codes, &(&1.code == code)) |
|
text_vector = Medicode.Coding.compute_vector_as_list(socket.assigns.text) |
|
|
|
code_feedback = |
|
Feedback.insert_and_return(%{ |
|
text: socket.assigns.text, |
|
text_vector: text_vector, |
|
response: true, |
|
code_vector_id: selected_code.id |
|
}) |
|
|
|
Transcriptions.upsert_code_vector_for_transcription_chunk(%{ |
|
assigned_by_user_id: socket.assigns.current_user.id, |
|
code_vector_id: selected_code.id, |
|
transcription_chunk_id: socket.assigns.chunk.id, |
|
cosine_similarity: |
|
Medicode.Coding.get_cosine_similarity(text_vector, selected_code.description_vector) |
|
}) |
|
|
|
send_update(MedicodeWeb.Components.TranscriptionTextComponent, |
|
id: "transcription-chunk-#{socket.assigns.chunk.id}", |
|
chunk: socket.assigns.chunk, |
|
chunk_id: socket.assigns.chunk.id, |
|
current_user: socket.assigns.current_user, |
|
on_feedback: &MedicodeWeb.TranscriptionsLive.Show.on_feedback/1, |
|
on_remove_code: &MedicodeWeb.TranscriptionsLive.Show.on_remove_code/1 |
|
) |
|
|
|
Phoenix.PubSub.broadcast( |
|
:medicode_pubsub, |
|
"transcriptions:#{socket.assigns.chunk.transcription_id}", |
|
{:transcription_updated, socket.assigns.chunk} |
|
) |
|
|
|
socket = |
|
if is_nil(code_feedback) do |
|
put_flash(socket, :error, "Failed to record feedback") |
|
else |
|
socket |
|
|> assign(:selected_code, selected_code) |
|
|> assign(:submitted_code_feedback, code_feedback) |
|
|> assign(:codes, []) |
|
end |
|
|
|
{:noreply, socket} |
|
end |
|
|
|
@impl Phoenix.LiveComponent |
|
def handle_event( |
|
"clear-code", |
|
_params, |
|
%{assigns: %{submitted_code_feedback: submitted_code_feedback}} = socket |
|
) do |
|
|
|
Feedback.delete(submitted_code_feedback) |
|
|
|
socket = |
|
socket |
|
|> assign(:codes, []) |
|
|> assign(:selected_code, nil) |
|
|> assign(:submitted_code_feedback, nil) |
|
|
|
{:noreply, socket} |
|
end |
|
end |
|
|