File size: 6,272 Bytes
3f219b5 cb8dc08 bade74c 3f219b5 a19d36e 3f219b5 7fa3fa6 572e5c5 76513af 8aae8fe 6fe951d 2719169 dc67aef 2719169 dc67aef 2719169 d952874 6fe951d 7a61f41 6fe951d 8aae8fe 7a61f41 8aae8fe 6fe951d 8aae8fe 6fe951d 8aae8fe 572e5c5 2893bb5 8aae8fe 0fc3ad2 3f219b5 572e5c5 8ba1194 e415915 7fc32c8 6fe951d c066ce6 4b2c1da cf5a59c 4b2c1da 3f219b5 4b2c1da 0fc3ad2 6fe951d 4b2c1da c066ce6 4b2c1da 6fe951d 5c502c9 4b2c1da 5c502c9 4b2c1da 5c502c9 6fe951d 8ba1194 7182c88 8ba1194 4d43dfd 8ba1194 4b2c1da 6fe951d 7fc32c8 e7e9eaf 572e5c5 410cb59 bade74c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | 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: [])
attr(:finalized_codes, :list, default: [])
attr(:timezone, :string, default: "Etc/UTC")
@doc """
Shows the status and keywords for the current session.
"""
def result_heading(assigns) do
assigns =
assign_new(assigns, :transcript_inserted_at, fn _ ->
assigns.transcription.inserted_at
|> DateTime.shift_zone!(assigns.timezone)
|> Calendar.strftime("%a, %b %d, %Y, %I:%M %p")
end)
~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]"
/>
<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">
<%= @transcript_inserted_at %>
</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 :if={!Enum.empty?(@finalized_codes)} class="px-4 pt-2 pb-10 flex flex-col gap-2">
<p class="leading-normal font-bold text-type-black-primary uppercase">Finalized Codes</p>
<div
class="flex flex-row items-center divide-x divide-black/15 text-sm leading-normal text-type-black-tertiary"
id="finalized_vector_code_list"
>
<span :for={code_vector <- @finalized_codes} class="px-2" title={code_vector.code}>
<%= code_vector.code %>
</span>
</div>
<.link
href={~p"/transcription/reports/#{@transcription.id}"}
class="font-semibold text-brand hover:underline"
download={"transcription-report-#{@transcription.id}.pdf"}
>
Download Report
</.link>
</div>
</div>
"""
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
|