Download lib/medicode_web/components/components.ex from headway/medicode: direct link, hf CLI and curl.
- Browser
- Download file 6.27 kB
-
https://huggingface.co/spaces/headway/medicode/resolve/main/lib/medicode_web/components/components.ex
- Command line
-
hf download hf://spaces/headway/medicode/lib/medicode_web/components/components.ex
-
curl -L -o components.ex https://huggingface.co/spaces/headway/medicode/resolve/main/lib/medicode_web/components/components.ex
6.27 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: []) | |
| 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 | |