|
defmodule MedicalTranscriptionWeb.Components do |
|
@moduledoc """ |
|
Functional UI components for the main transcription and coding view. |
|
""" |
|
|
|
use Phoenix.Component |
|
import MedicalTranscriptionWeb.CoreComponents |
|
alias MedicalTranscriptionWeb.Components.TranscriptionTextComponent |
|
use MedicalTranscriptionWeb, :verified_routes |
|
|
|
def upload_form(assigns) do |
|
~H""" |
|
<form |
|
id="audio-form" |
|
phx-submit="save" |
|
phx-change="validate" |
|
class="w-full flex flex-col items-center gap-[21px]" |
|
> |
|
<div class="w-full px-4 py-[19px] rounded-[10px] flex items-center bg-light-divider"> |
|
<label |
|
for={@audio_upload.ref} |
|
class="cursor-pointer mr-4 px-[16.5px] py-[9.941667px] rounded-lg bg-[ |
|
> |
|
<img src={~p"/images/paperclip.svg"} width="15" height="30" /> |
|
</label> |
|
<.live_file_input |
|
class="flex-1 cursor-pointer file:hidden file:font-secondary file:text-sm file:rounded-full file:px-4 file:py-2 file:border-0 file:bg-brand file:hover:bg-brand-active file:text-white" |
|
upload={@audio_upload} |
|
/> |
|
<button name="submit-btn" title="Upload and process file"> |
|
<img src={~p"/images/upload.svg"} /> |
|
</button> |
|
</div> |
|
|
|
<p class="leading-normal text-type-black-secondary">Audio file can be .mp3</p> |
|
</form> |
|
""" |
|
end |
|
|
|
def loading_message(assigns) do |
|
~H""" |
|
<%= if @visible do %> |
|
<div class="flex gap-2 items-center p-2 rounded-md text-slate-800 text-sm bg-slate-200 border border-slate-300"> |
|
<.icon name="hero-arrow-path" class="w-4 h-4 animate-spin" /> |
|
<p>Transcribing and tagging audio file...</p> |
|
</div> |
|
<% end %> |
|
""" |
|
end |
|
|
|
def result_heading(assigns) do |
|
if Enum.member?([:loading, :success], assigns.status) do |
|
~H""" |
|
<div class="flex justify-between"> |
|
<div class="flex items-center"> |
|
<%= if @status == :loading do %> |
|
<img src={~p"/images/loading.svg"} width="36" class="mr-6 animate-spin" /> |
|
<% end %> |
|
<%= if @status == :success do %> |
|
<img src={~p"/images/checkmark.svg"} width="46" class="mr-[17px]" /> |
|
<% end %> |
|
<div class="px-[14px] py-3 flex items-center gap-3 bg-brand rounded-lg text-white"> |
|
<img src={~p"/images/document.svg"} width="20" /> |
|
<span class="font-secondary"><%= @filename %></span> |
|
</div> |
|
</div> |
|
<button phx-click="reset_to_pending" type="button" class="p-3 rounded-lg bg-[#444444]/10"> |
|
<.icon name="hero-x-circle" class="w-6 h-6" /> |
|
</button> |
|
</div> |
|
|
|
<div class="border-b border-[#444444]/20"> |
|
<div class="px-4 py-2 flex items-center gap-2"> |
|
<img src={~p"/images/calendar.svg"} width="16" /> |
|
|
|
<span class="text-sm leading-normal font-bold text-type-black-tertiary uppercase"> |
|
<%= Calendar.strftime(DateTime.now!("Etc/UTC"), "%a, %b %d, %Y, %I:%M %p") %> |
|
</span> |
|
</div> |
|
<div class="px-4 pt-2 pb-10 flex flex-col gap-2"> |
|
<p class="leading-normal font-bold text-type-black-primary uppercase">Summary Keywords</p> |
|
|
|
<p |
|
class="text-sm leading-normal text-type-black-tertiary" |
|
id="keyword_list" |
|
class="flex flex-col gap-14" |
|
> |
|
<!-- coronary, artery, disease, unstable, angina, admitted --> |
|
<%= for keyword <- format_keywords(@summary_keywords) do %> |
|
<span class="" title={keyword.score}><%= keyword.label %>,</span> |
|
<% end %> |
|
</p> |
|
</div> |
|
</div> |
|
""" |
|
else |
|
~H"" |
|
end |
|
end |
|
|
|
def format_keywords(keyword_predictions) do |
|
keyword_predictions |
|
|> List.flatten() |
|
|> Enum.sort_by(& &1.score, :desc) |
|
|> Enum.take(7) |
|
end |
|
|
|
def result_list(assigns) do |
|
~H""" |
|
<div |
|
id="result_list" |
|
class="flex flex-col gap-14" |
|
phx-update={match?(%Phoenix.LiveView.LiveStream{}, @rows) && "stream"} |
|
> |
|
<%= for {dom_id, row} <- @rows do %> |
|
<.live_component module={TranscriptionTextComponent} id={dom_id} row={row} /> |
|
<% end %> |
|
</div> |
|
""" |
|
end |
|
|
|
def tag_result(assigns) do |
|
~H""" |
|
<div class="flex items-center gap-4 px-[14px] py-3 text-sm"> |
|
<div class="flex gap-3"> |
|
<.feedback_button code={@code} text={@text} label={@label} response="true" /> |
|
<.feedback_button code={@code} text={@text} label={@label} response="false" class="" /> |
|
</div> |
|
|
|
<div |
|
class="flex flex-col gap-1 font-secondary text-type-black-primary border-l border-[#444444]/20 pl-4" |
|
title={"Similarity score: #{trunc(@score * 100) / 100}"} |
|
> |
|
<p class="text-lg font-bold leading-[22.97px]"><%= @code %></p> |
|
<p class="text-base leading-[20.42px]"><%= @label %></p> |
|
</div> |
|
</div> |
|
""" |
|
end |
|
|
|
defp feedback_button(assigns) do |
|
~H""" |
|
<button |
|
phx-click="add_feedback" |
|
phx-value-code={@code} |
|
phx-value-text={@text} |
|
phx-value-long-description={@label} |
|
phx-value-response={@response} |
|
type="button" |
|
class="p-2 border border-button-deactivated-background rounded-lg hover:bg-slate-200" |
|
> |
|
<%= if @response == "true" do %> |
|
<img src={~p"/images/thumbs-up.svg"} width="20" height="20" class="w-5 h-5 min-w-[20px]" /> |
|
<% else %> |
|
<img src={~p"/images/thumbs-down.svg"} width="20" height="20" class="w-5 h-5 min-w-[20px]" /> |
|
<% end %> |
|
</button> |
|
""" |
|
end |
|
end |
|
|