diff --git a/lib/ex338_web.ex b/lib/ex338_web.ex
index f2df893d..ad53ac68 100644
--- a/lib/ex338_web.ex
+++ b/lib/ex338_web.ex
@@ -111,6 +111,38 @@ defmodule Ex338Web do
end
end
+ def html do
+ quote do
+ use Phoenix.Component
+
+ # Import convenience functions from controllers
+ import Phoenix.Controller,
+ only: [get_csrf_token: 0, view_module: 1, view_template: 1]
+
+ # include while migrating to Phoenix.Component
+ import Phoenix.View
+
+ # Include general helpers for rendering HTML
+ unquote(html_helpers())
+ end
+ end
+
+ defp html_helpers do
+ quote do
+ # HTML escaping functionality
+ # Core UI components and translation
+ import Ex338Web.CoreComponents
+ import Ex338Web.Gettext
+ import Phoenix.HTML
+
+ # Shortcut for generating JS commands
+ alias Phoenix.LiveView.JS
+
+ # Routes generation with the ~p sigil
+ unquote(verified_routes())
+ end
+ end
+
def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
diff --git a/lib/ex338_web/components/core_components.ex b/lib/ex338_web/components/core_components.ex
new file mode 100644
index 00000000..027334af
--- /dev/null
+++ b/lib/ex338_web/components/core_components.ex
@@ -0,0 +1,676 @@
+defmodule Ex338Web.CoreComponents do
+ @moduledoc """
+ Provides core UI components.
+
+ At first glance, this module may seem daunting, but its goal is to provide
+ core building blocks for your application, such as modals, tables, and
+ forms. The components consist mostly of markup and are well-documented
+ with doc strings and declarative assigns. You may customize and style
+ them in any way you want, based on your application growth and needs.
+
+ The default components use Tailwind CSS, a utility-first CSS framework.
+ See the [Tailwind CSS documentation](https://tailwindcss.com) to learn
+ how to customize them or feel free to swap in another framework altogether.
+
+ Icons are provided by [heroicons](https://heroicons.com). See `icon/1` for usage.
+ """
+ use Phoenix.Component
+
+ import Ex338Web.Gettext
+
+ alias Phoenix.LiveView.JS
+
+ @doc """
+ Renders a modal.
+
+ ## Examples
+
+ <.modal id="confirm-modal">
+ This is a modal.
+
+
+ JS commands may be passed to the `:on_cancel` to configure
+ the closing/cancel event, for example:
+
+ <.modal id="confirm" on_cancel={JS.navigate(~p"/posts")}>
+ This is another modal.
+
+
+ """
+ attr :id, :string, required: true
+ attr :show, :boolean, default: false
+ attr :on_cancel, JS, default: %JS{}
+ slot :inner_block, required: true
+
+ def modal(assigns) do
+ ~H"""
+
+ """
+ end
+
+ @doc """
+ Shows the flash group with standard titles and content.
+
+ ## Examples
+
+ <.flash_group flash={@flash} />
+ """
+ attr :flash, :map, required: true, doc: "the map of flash messages"
+ attr :id, :string, default: "flash-group", doc: "the optional id of flash container"
+
+ def flash_group(assigns) do
+ ~H"""
+
+ <.flash kind={:info} title={gettext("Success!")} flash={@flash} />
+ <.flash kind={:error} title={gettext("Error!")} flash={@flash} />
+ <.flash
+ id="client-error"
+ kind={:error}
+ title={gettext("We can't find the internet")}
+ phx-disconnected={show(".phx-client-error #client-error")}
+ phx-connected={hide("#client-error")}
+ hidden
+ >
+ <%= gettext("Attempting to reconnect") %>
+ <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+
+
+ <.flash
+ id="server-error"
+ kind={:error}
+ title={gettext("Something went wrong!")}
+ phx-disconnected={show(".phx-server-error #server-error")}
+ phx-connected={hide("#server-error")}
+ hidden
+ >
+ <%= gettext("Hang in there while we get back on track") %>
+ <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" />
+
+
+ """
+ end
+
+ @doc """
+ Renders a simple form.
+
+ ## Examples
+
+ <.simple_form for={@form} phx-change="validate" phx-submit="save">
+ <.input field={@form[:email]} label="Email"/>
+ <.input field={@form[:username]} label="Username" />
+ <:actions>
+ <.button>Save
+
+
+ """
+ attr :for, :any, required: true, doc: "the datastructure for the form"
+ attr :as, :any, default: nil, doc: "the server side parameter to collect all input under"
+
+ attr :rest, :global,
+ include: ~w(autocomplete name rel action enctype method novalidate target multipart),
+ doc: "the arbitrary HTML attributes to apply to the form tag"
+
+ slot :inner_block, required: true
+ slot :actions, doc: "the slot for form actions, such as a submit button"
+
+ def simple_form(assigns) do
+ ~H"""
+ <.form :let={f} for={@for} as={@as} {@rest}>
+
+ <%= render_slot(@inner_block, f) %>
+
+ <%= render_slot(action, f) %>
+
+
+
+ """
+ end
+
+ @doc """
+ Renders a button.
+
+ ## Examples
+
+ <.button>Send!
+ <.button phx-click="go" class="ml-2">Send!
+ """
+ attr :type, :string, default: nil
+ attr :class, :string, default: nil
+ attr :rest, :global, include: ~w(disabled form name value)
+
+ slot :inner_block, required: true
+
+ def button(assigns) do
+ ~H"""
+
+ """
+ end
+
+ @doc """
+ Renders an input with label and error messages.
+
+ A `Phoenix.HTML.FormField` may be passed as argument,
+ which is used to retrieve the input name, id, and values.
+ Otherwise all attributes may be passed explicitly.
+
+ ## Types
+
+ This function accepts all HTML input types, considering that:
+
+ * You may also set `type="select"` to render a `