medicode / lib /medicode_web /components /sidebar_component.ex
timgremore's picture
feat: Center MediCode logo in sidebar
aef8413
defmodule MedicodeWeb.Components.SidebarComponent do
@moduledoc """
Main layout header component
"""
use MedicodeWeb, :live_component
use MedicodeWeb, :verified_routes
import MedicodeWeb.CoreComponents
alias Medicode.Transcriptions
@impl Phoenix.LiveComponent
def mount(socket) do
socket =
socket
|> assign(:status, nil)
|> assign(:upload_progress, 0)
|> allow_upload(:audio,
accept: ~w(.mp3),
max_entries: 1,
progress: &handle_upload_progress/3,
auto_upload: true
)
{:ok, socket}
end
@impl Phoenix.LiveComponent
def update(assigns, socket) do
if connected?(socket) do
Phoenix.PubSub.subscribe(:medicode_pubsub, "medicode:#{assigns.current_user.id}")
end
socket =
socket
|> assign(:current_user, assigns.current_user)
|> assign(:transcriptions, list_transcriptions(assigns.current_user))
{:ok, socket}
end
@impl Phoenix.LiveComponent
def render(assigns) do
~H"""
<header class="hidden w-[335px] min-w-[335px] h-screen sticky top-8 pb-16 border-r border-gray-200 lg:flex flex-col gap-12 overflow-hidden overflow-y-auto">
<div>
<.link navigate={~p"/"} class="flex justify-center items-center gap-2">
<img src={~p"/images/logo.svg"} width="48" />
<h1 class="text-lg leading-normal px-2 font-semibold">
MediCode
</h1>
</.link>
</div>
<%= if @current_user do %>
<div class="flex-1 flex flex-col px-6 gap-4">
<form
id="audio-form"
phx-submit="save"
phx-change="validate"
phx-target={@myself}
class="relative w-full flex overflow-hidden"
>
<div
phx-drop-target={@uploads.audio.ref}
class="cursor-pointer text-[0.8125rem] flex justify-center gap-2 w-full text-white font-semibold hover:text-slate-300 px-3 py-2 bg-emerald-600 rounded-lg"
>
<.icon name="hero-document-plus" />
<label for={@uploads.audio.ref} class="cursor-pointer">
Upload Audio File (.mp3)
</label>
<.live_file_input class="hidden" upload={@uploads.audio} />
</div>
<button
phx-click="save"
phx-target={@myself}
disabled={@upload_progress < 100}
type="submit"
data-audio={Enum.count(@uploads.audio.entries) > 0}
class="flex justify-center items-center border-box text-[0.8125rem] text-white hover:text-slate-300 px-0 py-2 bg-emerald-600 rounded-lg transition-all relative left-8 w-0 ml-0 data-[audio]:px-3 data-[audio]:left-0 data-[audio]:w-fit data-[audio]:ml-2"
>
<.icon name="hero-paper-airplane" />
</button>
</form>
<button
id="audio-recorder-button"
phx-hook="AudioRecorder"
phx-click="record_transcription"
phx-target={@myself}
navigate={~p"/transcriptions/new"}
class="text-[0.8125rem] flex justify-center gap-2 w-full text-white font-semibold hover:text-slate-300 px-3 py-2 bg-emerald-600 rounded-lg"
>
<.icon :if={@status == :streaming_audio} name="hero-speaker-wave" />
<.icon :if={@status != :streaming_audio} name="hero-microphone" />
<span>Record Patient Notes</span>
</button>
<div class="flex flex-col gap-4">
<.link
:for={transcription <- @transcriptions}
navigate={~p"/transcriptions/#{transcription.id}"}
class="font-semibold text-lg leading-normal"
>
<%= transcription.filename %>
</.link>
</div>
</div>
<% end %>
<ul class="flex flex-col items-center gap-4 px-4">
<%= if @current_user do %>
<li class="text-[0.8125rem] leading-6 text-zinc-900">
<%= @current_user.email %>
</li>
<li class="w-full">
<.link
navigate={~p"/users/settings"}
class="text-[0.8125rem] text-left w-full block leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
>
Settings
</.link>
</li>
<li class="w-full">
<.link
href={~p"/users/log_out"}
method="delete"
class="text-[0.8125rem] text-left w-full block leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
>
Log out
</.link>
</li>
<% end %>
</ul>
<%= if @current_user do %>
<div class="px-6 flex flex-col items-center">
<%= if Medicode.Coding.icd9_present?() do %>
<div
class="w-full px-3 py-2 bg-emerald-600 text-white text-center rounded-lg"
title="Precalculated vector embeddings for classification labels were found."
>
<.icon name="hero-check-circle" /> Vector embeddings found!
</div>
<% else %>
<div
class="w-full px-3 py-2 bg-red-300 text-slate-900 text-center rounded-lg"
title="Precalculated vector embeddings for classification labels were found."
>
<.icon name="hero-x-circle" /> Vector embeddings need to be built. Run
<code class="p-1 text-sm bg-slate-300 rounded-lg">mix build_code_vectors</code>
on the server.
</div>
<% end %>
</div>
<% end %>
</header>
"""
end
@impl Phoenix.LiveComponent
def handle_event("record_transcription", _params, socket) do
socket =
if socket.assigns.status == :streaming_audio do
socket
|> push_event("stop_audio_recording", %{})
|> assign(:status, nil)
else
{:ok, new_transcription} =
Transcriptions.create_transcription(%{
user_id: socket.assigns.current_user.id,
filename: "Untitled",
status: :recording
})
Medicode.TranscriptionSupervisor.start_transcription(new_transcription)
transcriptions = list_transcriptions(socket.assigns.current_user)
socket
|> assign(:status, :streaming_audio)
|> assign(:transcriptions, transcriptions)
|> push_event("start_audio_recording", %{})
|> push_patch(to: ~p"/transcriptions/#{new_transcription.id}")
end
{:noreply, socket}
end
def handle_event("save", _params, socket) do
if socket.assigns.uploads.audio.entries == [] do
{:noreply, put_flash(socket, :error, "Please select an audio file")}
else
uploaded_files =
consume_uploaded_entries(socket, :audio, fn %{path: path}, _entry ->
dest = Path.join(System.tmp_dir(), Path.basename(path))
File.cp!(path, dest)
{:ok, dest}
end)
{:ok, transcription} =
Transcriptions.create_transcription(%{
user_id: socket.assigns.current_user.id,
filename: Enum.at(uploaded_files, 0)
})
Phoenix.PubSub.broadcast(
:medicode_pubsub,
"medicode:#{socket.assigns.current_user.id}",
{:transcription_created, transcription.id}
)
Transcriptions.transcribe_audio(transcription)
{:noreply, push_navigate(socket, to: ~p"/transcriptions/#{transcription.id}")}
end
end
def handle_event("validate", _params, socket) do
{:noreply, socket}
end
defp list_transcriptions(user) do
Transcriptions.list_transcriptions(user)
end
defp handle_upload_progress(:audio, entry, socket) do
{:noreply, assign(socket, :upload_progress, entry.progress)}
end
end