defmodule VaccinsWeb.LocationComponent do
use VaccinsWeb, :live_component
alias Vaccins.{LocationStore, Search}
@impl true
def mount(socket),
do:
{:ok,
socket
|> assign(
slots_after: [],
slots_before: [],
loading: false,
last_refresh_date: nil,
last_early_slot_seen: nil,
render_as: :description_list,
is_local?: false
)}
@impl true
def update(assigns, socket) do
{force_refresh, assigns} = assigns |> Map.pop(:force_refresh)
assigns = assigns |> integrate_availabilities
if is_nil(force_refresh) do
{:ok, socket |> assign(assigns) |> assign(loading: false) |> signal_availabilities}
else
trigger_and_signal_query(
assigns |> Map.get(:location, socket.assigns.location),
assigns.id
)
{:ok,
socket |> assign(assigns) |> assign(loading: true, last_refresh_date: get_refresh_time())}
end
end
@impl true
def handle_event("trigger_query", _, socket = %{assigns: %{id: id, location: location}}) do
trigger_and_signal_query(location, id)
{:noreply, socket |> assign(loading: true, last_refresh_date: get_refresh_time())}
end
def handle_event("delete", _, socket = %{assigns: %{id: id}}) do
if :ok == LocationStore.delete_location(id), do: send(self(), {:location_deleted, id})
{:noreply, socket}
end
@impl true
def render(assigns = %{render_as: :description_list}) do
~L"""
- name
- <%= @location.name %>
- location
- <%= @location.location %>
- Status (<%= if @last_refresh_date, do: @last_refresh_date |> Time.to_string() %>)
- <%= render_status(assigns) %>
- booking page
- <%= link @location.booking_page, to: @location.booking_page %>
<%= if has_slots?(assigns) do %>
- Avant 24h
- <%= render_slots_before(assigns) %>
- Après 24h
- <%= render_slots_after(assigns) %>
<% end %>
- actions
- <%= render_action_list(assigns) %>
"""
end
@impl true
def render(assigns = %{render_as: :table_row}) do
~L"""
<%= @location.name %> |
<%= @location.location %> |
<%= if @last_refresh_date, do: @last_refresh_date |> Time.to_string() %> |
<%= render_status(assigns) %> |
<%= link "Résa.", to: @location.booking_page %> |
<%= render_slots_before(assigns) %> |
<%= render_slots_after(assigns) %> |
<%= if @is_local? do %><%= render_action_list(assigns) %> | <% end %>
"""
end
def render_table_header(is_local?),
do: ~E"""
Nom |
Lieu |
Dernier refresh |
Status |
Lien résa |
Slots avant 24h |
Slots après 24h |
<%= if is_local? do %>Actions | <% end %>
"""
defp render_status(assigns),
do: ~L"""
<%= cond do %>
<%= @loading -> %>...
<%= not has_slots?(assigns) -> %>Pas de créneau <%= if @last_early_slot_seen do %>(<%= @last_early_slot_seen |> DateTime.to_time() |> Time.truncate(:second) |> Time.to_string %>)<% end %>
<%= has_early_slots?(assigns) -> %>Des dispos sous 24h !
<%= has_slots?(assigns) -> %>Des dispos !
<% end %>
"""
defp render_slots_before(assigns),
do: ~L"""
<%= for d <- @slots_before do %>- <%= d |> Calendar.strftime("%d/%m/%Y %H:%M") %>
<% end %>
"""
defp render_slots_after(assigns),
do: ~L"""
<%= for d <- @slots_after do %>- <%= d |> Calendar.strftime("%d/%m/%Y %H:%M") %>
<% end %>
"""
defp render_action_list(assigns),
do: ~L"""
"""
defp integrate_availabilities(assigns = %{availabilities: {:error, reason}}),
do:
assigns
|> Map.put(:slots_after, [])
|> Map.put(:slots_before, [])
defp integrate_availabilities(assigns = %{availabilities: {:ok, after_slots}})
when is_list(after_slots),
do:
assigns
|> Map.put(:slots_after, after_slots |> Enum.take(2))
|> Map.put(:slots_before, [])
defp integrate_availabilities(assigns = %{availabilities: {:ok, before_slots, after_slots}})
when is_list(before_slots),
do:
assigns
|> Map.put(:slots_after, after_slots |> Enum.take(2))
|> Map.put(:slots_before, before_slots |> Enum.take(2))
|> Map.put(:last_early_slot_seen, DateTime.utc_now())
defp integrate_availabilities(assigns), do: assigns
defp has_slots?(assigns = %{slots_before: before, slots_after: after_}),
do: not (before |> Enum.empty?() and after_ |> Enum.empty?())
defp has_early_slots?(assigns = %{slots_before: before}),
do: not (before |> Enum.empty?())
defp trigger_and_signal_query(location, id) do
ref = Search.async_trigger_query(location)
send(self(), {:query_sent, id, ref})
end
defp get_refresh_time() do
with {:ok, now} <-
DateTime.utc_now() |> DateTime.shift_zone("Europe/Paris", Tzdata.TimeZoneDatabase),
do: now |> DateTime.to_time() |> Time.truncate(:second)
end
defp signal_availabilities(socket = %{assigns: %{loading: false}}) do
cond do
socket.assigns |> has_slots? ->
send(self(), {:location_has_slots, socket.assigns.id, socket.assigns |> has_early_slots?})
true ->
send(self(), {:location_no_more_slots, socket.assigns.id})
end
socket
end
defp signal_availabilities(socket), do: socket
defp to_json_query(l = %{availability_query: q, provider: provider}),
do: q |> provider.to_url() |> URI.to_string()
end