lib/core_web/views/helpers.ex

defmodule Legendary.CoreWeb.Helpers do
  @moduledoc """
  HTML helpers for our styled (Tailwind) forms.
  """
  use Phoenix.HTML

  import Phoenix.Controller, only: [get_flash: 2]
  import Phoenix.LiveView.Helpers, only: [sigil_H: 2]
  import ShorterMaps
  import Legendary.CoreWeb.ErrorHelpers

  defdelegate current_user(a), to: Legendary.AuthWeb.Helpers
  defdelegate has_role?(a, b), to: Legendary.AuthWeb.Helpers

  def changeset_error_block(changeset) do
    assigns = ~M{changeset}

    ~H"""
    <%= if changeset.action do %>
      <div class="flex-auto px-4 lg:px-10 py-6 text-red-500 text-center">
        <p>Oops, something went wrong! Please check the errors below.</p>
      </div>
    <% end %>
    """
  end

  def flash_block(conn) do
    assigns = ~M{conn}
    ~H"""
    <div class="fixed w-full px-4 z-50 mt-20">
      <%= [info: "green", error: "red"] |> Enum.map(fn {level, color} ->  %>
        <%= if get_flash(conn, level) do %>
          <div class={"relative bg-#{color}-100 lg:w-1/2 w-full p-5 object-right rounded shadow-xl m-auto mb-5 js-flash"}>
            <div class={"flex justify-between text-#{color}-700"}>
              <div class="flex space-x-3">
                <div class="flex-1 leading-tight text-sm font-medium">
                  <%= get_flash(conn, level) %>
                </div>
              </div>
              <div class="flex-none js-flash-closer">
                &times;
              </div>
            </div>
          </div>
        <% end %>
      <% end) %>
    </div>
    """
  end

  def styled_input(f, field, opts \\ [], options \\ nil, block_list \\ []) do
    {content, _} = Keyword.pop(block_list, :do, "")
    {type, rest_opts} = Keyword.pop(opts, :type, input_type(f, field))
    {classes, rest_opts} = Keyword.pop(rest_opts, :class, default_classes_for_type(type))
    {label_text, rest_opts} = Keyword.pop(rest_opts, :label)
    {input_helper, _rest_opts} = Keyword.pop(rest_opts, :input_helper, input_type(f, field))

    error_classes =
      if Keyword.get_values(f.errors, field) |> Enum.any?() do
        " border border-red-500"
      else
        ""
      end

    assigns = ~M{classes, content, error_classes, f, field, input_helper, label_text, type, options, opts}

    ~H"""
    <div class={"relative w-full mb-3 #{error_class(f, field)}"}>
      <%= if label_text do %>
        <%= label f, field, label_text, class: "block uppercase text-gray-700 text-xs font-bold mb-2" %>
      <% else %>
        <%= label f, field, class: "block uppercase text-gray-700 text-xs font-bold mb-2" %>
      <% end %>

      <%= do_styled_input_tag(type, input_helper, f, field, options, opts, classes, error_classes) %>
      <%= content %>

      <%= error_tag f, field, class: "text-red-500 italic" %>
    </div>
    """
  end

  defp do_styled_input_tag(type, _input_helper, f, field, nil, opts, classes, error_classes) when type in [:date_select, :time_select, :datetime_select] do
    default_child_opts = [
      month: [
        class: "appearance-none border-b-2 border-dashed",
        options: [
          {("Jan"), "1"},
          {("Feb"), "2"},
          {("Mar"), "3"},
          {("Apr"), "4"},
          {("May"), "5"},
          {("Jun"), "6"},
          {("Jul"), "7"},
          {("Aug"), "8"},
          {("Sep"), "9"},
          {("Oct"), "10"},
          {("Nov"), "11"},
          {("Dec"), "12"},
        ]
      ],
      day: [class: "appearance-none border-b-2 border-dashed"],
      year: [class: "appearance-none border-b-2 border-dashed"],
      hour: [class: "appearance-none border-b-2 border-dashed"],
      minute: [class: "appearance-none border-b-2 border-dashed"],
      second: [class: "appearance-none border-b-2 border-dashed"],
    ]

    {child_opts, rest_opts} = Keyword.pop(opts, :child_opts, default_child_opts)

    assigns = ~M{child_opts, classes, error_classes, f, field, rest_opts, type}

    ~H"""
      <%= content_tag :div, class: Enum.join([classes, error_classes], " ") do %>
        <%= apply(Phoenix.HTML.Form, type, [f, field, rest_opts ++ child_opts]) %>
      <% end %>
    """
  end

  defp do_styled_input_tag(_type, input_helper, f, field, nil, opts, classes, error_classes) do
    apply(Phoenix.HTML.Form, input_helper, [f, field, opts ++ [class: Enum.join([classes, error_classes], " ")]])
  end

  defp do_styled_input_tag(_type, input_helper, f, field, options, opts, classes, error_classes) do
    apply(Phoenix.HTML.Form, input_helper, [f, field, options, opts ++ [class: Enum.join([classes, error_classes], " ")]])
  end

  defp default_classes_for_type(type) when type in [:date_select, :time_select, :datetime_select] do
    "bg-white shadow rounded p-3"
  end
  defp default_classes_for_type(:checkbox), do: "appearance-none h-10 w-10 bg-white checked:bg-gray-500 rounded shadow focus:outline-none focus:ring text-white text-xl font-bold mb-2"
  defp default_classes_for_type(_), do: "px-4 py-3 placeholder-gray-400 text-gray-700 bg-white rounded text-sm shadow focus:outline-none focus:ring w-full"

  def styled_button(text) do
    assigns = ~M{text}
    ~H"""
    <%= submit text, class: "bg-gray-900 text-white active:bg-gray-700 text-sm font-bold uppercase px-4 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 w-full" %>
    """
  end

  def styled_button_link(text, opts) do
    assigns = ~M{text, opts}

    ~H"""
    <%= link text, opts ++ [class: "bg-gray-900 text-white active:bg-gray-700 text-sm font-bold uppercase px-4 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 w-full"] %>
    """
  end

  def paginator(first..last, current, callback) do
    [first, current - 1, current, current + 1, last]
    |> Enum.sort()
    |> Enum.filter(& &1 >= first)
    |> Enum.filter(& &1 <= last)
    |> Enum.dedup()
    |> Enum.map(& callback.(first..last, &1))
  end

  def group_rounding_class(first..last, current, [first_class, middle_class, last_class] \\ ["rounded-l", "", "rounded-r"]) do
    cond do
      first == last -> "#{first_class} #{last_class}"
      current == first -> first_class
      current == last -> last_class
      true -> middle_class
    end
  end

  @spec floating_form(any, atom | %{action: any}, [{:do, any}, ...]) :: {:safe, [...]}
  def floating_form(title, changeset, do: content) do
    assigns = ~M{changeset, content, title}

    ~H"""
    <h1 class="relative text-white text-xl font-semibold text-center pb-6"><%= title %></h1>
    <div
      class="relative flex flex-col min-w-0 break-words w-full mb-6 shadow-lg rounded-lg bg-gray-300 border-0"
    >
      <%= changeset_error_block(changeset) %>
      <div class="flex-auto px-4 lg:px-10 pt-6 pb-10">
        <%= content %>
      </div>
    </div>
    """
  end

  def floating_page_wrapper(do: content) do
    assigns = ~M{content}

    ~H"""
    <section class="absolute w-full h-full">
      <div class="absolute top-0 w-full h-full bg-gray-700"></div>
      <div class="container mx-auto px-4 h-full">
        <div class="flex content-center items-center justify-center h-full">
          <div class="w-full lg:w-4/12 px-4">
            <%= content %>
          </div>
        </div>
      </div>
    </section>
    """
  end

  def pow_extension_enabled?(extension) do
    {extensions, _rest} = Application.get_env(:core, :pow) |> Keyword.pop(:extensions, [])

    Enum.any?(extensions, & &1 == extension)
  end
end