location_component.ex 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. defmodule VaccinsWeb.LocationComponent do
  2. use VaccinsWeb, :live_component
  3. alias Vaccins.{LocationStore, Search}
  4. @impl true
  5. def mount(socket),
  6. do:
  7. {:ok,
  8. socket
  9. |> assign(
  10. slots_after: [],
  11. slots_before: [],
  12. loading: false,
  13. last_refresh_date: nil
  14. )}
  15. @impl true
  16. def update(assigns, socket) do
  17. {force_refresh, assigns} = assigns |> Map.pop(:force_refresh)
  18. assigns = assigns |> integrate_availabilities
  19. if is_nil(force_refresh) do
  20. {:ok, socket |> assign(assigns) |> assign(loading: false) |> signal_availabilities}
  21. else
  22. trigger_and_signal_query(
  23. assigns |> Map.get(:location, socket.assigns.location),
  24. assigns.id
  25. )
  26. {:ok,
  27. socket |> assign(assigns) |> assign(loading: true, last_refresh_date: get_refresh_time())}
  28. end
  29. end
  30. @impl true
  31. def handle_event("trigger_query", _, socket = %{assigns: %{id: id, location: location}}) do
  32. trigger_and_signal_query(location, id)
  33. {:noreply, socket |> assign(loading: true, last_refresh_date: get_refresh_time())}
  34. end
  35. def handle_event("delete", _, socket = %{assigns: %{id: id}}) do
  36. if LocationStore.delete_location(id), do: send(self(), {:location_deleted, id})
  37. {:noreply, socket}
  38. end
  39. @impl true
  40. def render(assigns) do
  41. ~L"""
  42. <dl class="location">
  43. <dt>id</dt>
  44. <dd><%= @location.id %></dd>
  45. <dt>Status (<%= if @last_refresh_date, do: @last_refresh_date |> Time.to_string() %>) </dt>
  46. <dd>
  47. <%= cond do %>
  48. <%= @loading -> %>...
  49. <%= not has_slots?(assigns) -> %>Pas de créneau
  50. <%= has_early_slots?(assigns) -> %><span class="alert-danger">Des dispos sous 24h !</span>
  51. <%= has_slots?(assigns) -> %>Des dispos !
  52. <% end %>
  53. </dd>
  54. <dt>booking page</dt>
  55. <dd><%= link @location.booking_page, to: @location.booking_page %></dd>
  56. <%= if has_slots?(assigns) do %>
  57. <dt>Avant 24h</dt>
  58. <dd>
  59. <ul class="slots-list"><%= for d <- @slots_before do %><li><%= d |> DateTime.to_string %></li><% end %></ul>
  60. </dd>
  61. <dt>Après 24h</dt>
  62. <dd>
  63. <ul class="slots-list"><%= for d <- @slots_after do %><li><%= d |> DateTime.to_string %></li><% end %></ul>
  64. </dd>
  65. <% end %>
  66. <dt>actions</dt>
  67. <dd>
  68. <ul class="actions-list">
  69. <li><button phx-click="trigger_query" phx-target="<%= @myself %>">Trigger</button></li>
  70. <li><a href="<%= @location |> to_json_query %>"><button>Debug</button></a></li>
  71. <li><button class="alert-danger" phx-click="delete" phx-target="<%= @myself %>" data-confirm="Etes-vous sur?">Delete</button></li>
  72. </ul>
  73. </dd>
  74. </dl>
  75. """
  76. end
  77. defp integrate_availabilities(assigns = %{availabilities: {:error, reason}}),
  78. do: assigns
  79. defp integrate_availabilities(assigns = %{availabilities: {:ok, after_slots}})
  80. when is_list(after_slots),
  81. do:
  82. assigns
  83. |> Map.put(:slots_after, after_slots |> Enum.take(5))
  84. defp integrate_availabilities(assigns = %{availabilities: {:ok, before_slots, after_slots}})
  85. when is_list(before_slots),
  86. do:
  87. assigns
  88. |> Map.put(:slots_after, after_slots |> Enum.take(5))
  89. |> Map.put(:slots_before, before_slots)
  90. defp integrate_availabilities(assigns), do: assigns
  91. defp has_slots?(assigns = %{slots_before: before, slots_after: after_}),
  92. do: not (before |> Enum.empty?() and after_ |> Enum.empty?())
  93. defp has_early_slots?(assigns = %{slots_before: before}),
  94. do: not (before |> Enum.empty?())
  95. defp trigger_and_signal_query(location, id) do
  96. ref = Search.async_trigger_query(location)
  97. send(self(), {:query_sent, id, ref})
  98. end
  99. defp get_refresh_time() do
  100. with {:ok, now} <-
  101. DateTime.utc_now() |> DateTime.shift_zone("Europe/Paris", Tzdata.TimeZoneDatabase),
  102. do: now |> DateTime.to_time() |> Time.truncate(:second)
  103. end
  104. defp signal_availabilities(socket = %{assigns: %{loading: false, slots_after: slots_after}}) do
  105. cond do
  106. socket.assigns |> has_slots? ->
  107. send(self(), {:location_has_slots, socket.assigns.id, socket.assigns |> has_early_slots?})
  108. true ->
  109. send(self(), {:location_no_more_slots, socket.assigns.id})
  110. end
  111. socket
  112. end
  113. defp signal_availabilities(socket), do: socket
  114. defp to_json_query(l = %{availability_query: q, provider: provider}), do: q |> provider.to_url() |> URI.to_string()
  115. end