timgremore's picture
fix: Rename directories and main context files
6488653
raw
history blame
5.96 kB
defmodule MedicodeWeb.Components do
@moduledoc """
Functional UI components for the main transcription and coding view.
"""
use Phoenix.Component
use MedicodeWeb, :verified_routes
import MedicodeWeb.CoreComponents
attr(:audio_upload, Phoenix.LiveView.UploadConfig, required: true)
@doc """
Displays a form containing a button for listening to live audio and a file upload for recorded audio files.
"""
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">
<button
type="button"
disabled
phx-click="toggle_recording"
title="Not available"
class="cursor-not-allowed mr-2 h-full px-3 py-2.5 rounded-lg border border-emerald-300 bg-emerald-200"
>
<.icon name="hero-microphone" class="w-6 h-6" />
</button>
<label
for={@audio_upload.ref}
class="cursor-pointer mr-4 px-[16.5px] py-[9.941667px] rounded-lg bg-[#444444]/10"
>
<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
attr(:visible, :boolean, required: true)
@doc """
A loading icon and message displayed while the audio is being processed.
"""
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
attr(:id, :string, required: true)
attr(:target, :any, required: true)
attr(:transcription, Medicode.Transcriptions.Transcription, required: true)
attr(:summary_keywords, :list, default: [])
@doc """
Shows the status and keywords for the current session.
"""
def result_heading(assigns) do
~H"""
<div class="flex justify-between">
<div class="flex items-center">
<img
:if={@transcription.status == :waiting}
src={~p"/images/loading.svg"}
width="36"
class="mr-6 animate-spin"
/>
<img
:if={@transcription.status == :finished}
src={~p"/images/checkmark.svg"}
width="46"
class="mr-[17px]"
/>
<.record_button_in_heading status={@transcription.status} />
<div class="px-[14px] py-3 flex items-center gap-3 bg-brand rounded-lg text-white mr-4 overflow-hidden">
<img src={~p"/images/document.svg"} width="20" />
<p
id={@id}
contenteditable
phx-hook="ContentEditor"
phx-event-name="rename_transcription"
role="textbox"
class="font-secondary"
>
<%= @transcription.filename %>
</p>
</div>
</div>
</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>
<div
class="flex flex-row items-center divide-x divide-black/15 text-sm leading-normal text-type-black-tertiary"
id="keyword_list"
>
<!-- coronary, artery, disease, unstable, angina, admitted -->
<%= for keyword <- format_keywords(@summary_keywords) do %>
<span class="px-2" title={keyword.score}><%= keyword.keyword %></span>
<% end %>
</div>
</div>
</div>
"""
end
# Renders the record button within the result_heading component
defp record_button_in_heading(assigns) when assigns.status == :streaming_audio do
~H"""
<button
disabled
phx-click="toggle_recording"
title="Not available"
class="cursor-not-allowed mr-6 px-4 py-3 bg-red-200 rounded-lg"
>
<.icon name="hero-microphone" class="animate-pulse" />
</button>
"""
end
defp record_button_in_heading(assigns) do
~H"""
<button
disabled
phx-click="toggle_recording"
title="Not available"
class="cursor-not-allowed mr-6 px-4 py-3 bg-emerald-200 rounded-lg"
>
<.icon name="hero-microphone" />
</button>
"""
end
# Formats keywords for display in the result_heading component
defp format_keywords(keyword_predictions) do
keyword_predictions
|> List.flatten()
|> Enum.sort_by(& &1.score, :desc)
|> Enum.take(7)
end
attr(:code, :string, required: true)
attr(:label, :string, required: true)
@doc """
Displays a single code and its description.
"""
def code_display(assigns) do
~H"""
<div class="py-4 text-sm flex flex-col gap-1 font-secondary text-type-black-primary rounded">
<p class="text-lg font-bold leading-[22.97px]"><%= @code %></p>
<p class="text-base leading-[20.42px]"><%= @label %></p>
</div>
"""
end
end