forked from shibao/cannery
		
	use core components
This commit is contained in:
		| @@ -1,5 +1,6 @@ | ||||
| # v0.8.4 | ||||
| - Improve accessibility | ||||
| - Code quality improvements | ||||
|  | ||||
| # v0.8.3 | ||||
| - Improve some styles | ||||
|   | ||||
| @@ -44,8 +44,7 @@ defmodule CanneryWeb do | ||||
|  | ||||
|   def live_view do | ||||
|     quote do | ||||
|       use Phoenix.LiveView, | ||||
|         layout: {CanneryWeb.LayoutView, "live.html"} | ||||
|       use Phoenix.LiveView, layout: {CanneryWeb.LayoutView, :live} | ||||
|  | ||||
|       on_mount CanneryWeb.InitAssigns | ||||
|       unquote(view_helpers()) | ||||
| @@ -94,7 +93,7 @@ defmodule CanneryWeb do | ||||
|  | ||||
|       # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) | ||||
|       # Import basic rendering functionality (render, render_layout, etc) | ||||
|       import CanneryWeb.{ErrorHelpers, Gettext, LiveHelpers, ViewHelpers} | ||||
|       import CanneryWeb.{ErrorHelpers, Gettext, CoreComponents, ViewHelpers} | ||||
|       import Phoenix.{Component, View} | ||||
|  | ||||
|       alias CanneryWeb.Endpoint | ||||
|   | ||||
| @@ -1,103 +0,0 @@ | ||||
| defmodule CanneryWeb.Components.AmmoGroupCard do | ||||
|   @moduledoc """ | ||||
|   Display card for an ammo group | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :component | ||||
|   alias Cannery.{Ammo, Ammo.AmmoGroup, Repo} | ||||
|   alias CanneryWeb.Endpoint | ||||
|  | ||||
|   attr :ammo_group, AmmoGroup, required: true | ||||
|   attr :show_container, :boolean, default: false | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   def ammo_group_card(%{ammo_group: ammo_group} = assigns) do | ||||
|     assigns = | ||||
|       %{show_container: show_container} = assigns |> assign_new(:show_container, fn -> false end) | ||||
|  | ||||
|     preloads = if show_container, do: [:ammo_type, :container], else: [:ammo_type] | ||||
|     ammo_group = ammo_group |> Repo.preload(preloads) | ||||
|  | ||||
|     assigns = assigns |> assign(:ammo_group, ammo_group) | ||||
|  | ||||
|     ~H""" | ||||
|     <div | ||||
|       id={"ammo_group-#{@ammo_group.id}"} | ||||
|       class="mx-4 my-2 px-8 py-4 | ||||
|         flex flex-col justify-center items-center | ||||
|         border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|         transition-all duration-300 ease-in-out" | ||||
|     > | ||||
|       <.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="mb-2 link"> | ||||
|         <h1 class="title text-xl title-primary-500"> | ||||
|           <%= @ammo_group.ammo_type.name %> | ||||
|         </h1> | ||||
|       </.link> | ||||
|  | ||||
|       <div class="flex flex-col justify-center items-center"> | ||||
|         <span class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Count:") %> | ||||
|           <%= if @ammo_group.count == 0, do: gettext("Empty"), else: @ammo_group.count %> | ||||
|         </span> | ||||
|  | ||||
|         <span | ||||
|           :if={@ammo_group |> Ammo.get_original_count() != @ammo_group.count} | ||||
|           class="rounded-lg title text-lg" | ||||
|         > | ||||
|           <%= gettext("Original Count:") %> | ||||
|           <%= @ammo_group |> Ammo.get_original_count() %> | ||||
|         </span> | ||||
|  | ||||
|         <span :if={@ammo_group.notes} class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Notes:") %> | ||||
|           <%= @ammo_group.notes %> | ||||
|         </span> | ||||
|  | ||||
|         <span class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Purchased on:") %> | ||||
|           <.date date={@ammo_group.purchased_on} /> | ||||
|         </span> | ||||
|  | ||||
|         <span :if={@ammo_group |> Ammo.get_last_used_shot_group()} class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Last used on:") %> | ||||
|           <.date date={@ammo_group |> Ammo.get_last_used_shot_group() |> Map.get(:date)} /> | ||||
|         </span> | ||||
|  | ||||
|         <%= if @ammo_group.price_paid do %> | ||||
|           <span class="rounded-lg title text-lg"> | ||||
|             <%= gettext("Price paid:") %> | ||||
|             <%= gettext("$%{amount}", | ||||
|               amount: @ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2) | ||||
|             ) %> | ||||
|           </span> | ||||
|  | ||||
|           <span class="rounded-lg title text-lg"> | ||||
|             <%= gettext("CPR:") %> | ||||
|             <%= gettext("$%{amount}", | ||||
|               amount: @ammo_group |> Ammo.get_cpr() |> :erlang.float_to_binary(decimals: 2) | ||||
|             ) %> | ||||
|           </span> | ||||
|         <% end %> | ||||
|  | ||||
|         <span :if={@show_container and @ammo_group.container} class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Container:") %> | ||||
|  | ||||
|           <.link | ||||
|             navigate={Routes.container_show_path(Endpoint, :show, @ammo_group.container)} | ||||
|             class="link" | ||||
|           > | ||||
|             <%= @ammo_group.container.name %> | ||||
|           </.link> | ||||
|         </span> | ||||
|       </div> | ||||
|  | ||||
|       <div | ||||
|         :if={assigns |> Map.has_key?(:inner_block)} | ||||
|         class="mt-4 flex space-x-4 justify-center items-center" | ||||
|       > | ||||
|         <%= render_slot(@inner_block) %> | ||||
|       </div> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -1,81 +0,0 @@ | ||||
| defmodule CanneryWeb.Components.ContainerCard do | ||||
|   @moduledoc """ | ||||
|   Display card for a container | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :component | ||||
|   import CanneryWeb.Components.TagCard | ||||
|   alias Cannery.{Containers, Containers.Container, Repo} | ||||
|   alias CanneryWeb.Endpoint | ||||
|   alias Phoenix.LiveView.Rendered | ||||
|  | ||||
|   attr :container, Container, required: true | ||||
|   slot(:tag_actions) | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   @spec container_card(assigns :: map()) :: Rendered.t() | ||||
|   def container_card(%{container: container} = assigns) do | ||||
|     assigns = | ||||
|       assigns | ||||
|       |> assign(container: container |> Repo.preload([:tags, :ammo_groups])) | ||||
|       |> assign_new(:tag_actions, fn -> [] end) | ||||
|  | ||||
|     ~H""" | ||||
|     <div | ||||
|       id={"container-#{@container.id}"} | ||||
|       class="overflow-hidden max-w-full mx-4 mb-4 px-8 py-4 | ||||
|         flex flex-col justify-center items-center space-y-4 | ||||
|         border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|         transition-all duration-300 ease-in-out" | ||||
|     > | ||||
|       <div class="max-w-full mb-4 flex flex-col justify-center items-center space-y-2"> | ||||
|         <.link navigate={Routes.container_show_path(Endpoint, :show, @container)} class="link"> | ||||
|           <h1 class="px-4 py-2 rounded-lg title text-xl"> | ||||
|             <%= @container.name %> | ||||
|           </h1> | ||||
|         </.link> | ||||
|  | ||||
|         <span :if={@container.desc} class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Description:") %> | ||||
|           <%= @container.desc %> | ||||
|         </span> | ||||
|  | ||||
|         <span class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Type:") %> | ||||
|           <%= @container.type %> | ||||
|         </span> | ||||
|  | ||||
|         <span :if={@container.location} class="rounded-lg title text-lg"> | ||||
|           <%= gettext("Location:") %> | ||||
|           <%= @container.location %> | ||||
|         </span> | ||||
|  | ||||
|         <%= unless @container.ammo_groups |> Enum.empty?() do %> | ||||
|           <span class="rounded-lg title text-lg"> | ||||
|             <%= gettext("Packs:") %> | ||||
|             <%= @container |> Containers.get_container_ammo_group_count!() %> | ||||
|           </span> | ||||
|  | ||||
|           <span class="rounded-lg title text-lg"> | ||||
|             <%= gettext("Rounds:") %> | ||||
|             <%= @container |> Containers.get_container_rounds!() %> | ||||
|           </span> | ||||
|         <% end %> | ||||
|  | ||||
|         <div class="flex flex-wrap justify-center items-center"> | ||||
|           <.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} /> | ||||
|  | ||||
|           <%= render_slot(@tag_actions) %> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div | ||||
|         :if={assigns |> Map.has_key?(:inner_block)} | ||||
|         class="flex space-x-4 justify-center items-center" | ||||
|       > | ||||
|         <%= render_slot(@inner_block) %> | ||||
|       </div> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.Components.ContainerTableComponent do | ||||
|   """ | ||||
|   use CanneryWeb, :live_component | ||||
|   alias Cannery.{Accounts.User, Containers, Containers.Container, Repo} | ||||
|   alias CanneryWeb.Components.TagCard | ||||
|   alias Ecto.UUID | ||||
|   alias Phoenix.LiveView.{Rendered, Socket} | ||||
|  | ||||
| @@ -135,7 +134,7 @@ defmodule CanneryWeb.Components.ContainerTableComponent do | ||||
|     {container.tags |> Enum.map(fn %{name: name} -> name end), | ||||
|      ~H""" | ||||
|      <div class="flex flex-wrap justify-center items-center"> | ||||
|        <TagCard.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} /> | ||||
|        <.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} /> | ||||
|  | ||||
|        <%= render_slot(@tag_actions, @container) %> | ||||
|      </div> | ||||
|   | ||||
							
								
								
									
										189
									
								
								lib/cannery_web/components/core_components.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								lib/cannery_web/components/core_components.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | ||||
| defmodule CanneryWeb.CoreComponents do | ||||
|   @moduledoc """ | ||||
|   Provides core UI components. | ||||
|   """ | ||||
|   use Phoenix.Component | ||||
|   import CanneryWeb.{Gettext, ViewHelpers} | ||||
|   alias Cannery.{Accounts, Ammo, Ammo.AmmoGroup} | ||||
|   alias Cannery.Accounts.{Invite, Invites, User} | ||||
|   alias Cannery.{Containers, Containers.Container, Tags.Tag} | ||||
|   alias CanneryWeb.{Endpoint, HomeLive} | ||||
|   alias CanneryWeb.Router.Helpers, as: Routes | ||||
|   alias Phoenix.LiveView.{JS, Rendered} | ||||
|  | ||||
|   embed_templates "core_components/*" | ||||
|  | ||||
|   attr :title_content, :string, default: nil | ||||
|   attr :current_user, User, default: nil | ||||
|  | ||||
|   def topbar(assigns) | ||||
|  | ||||
|   attr :return_to, :string, required: true | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   @doc """ | ||||
|   Renders a live component inside a modal. | ||||
|  | ||||
|   The rendered modal receives a `:return_to` option to properly update | ||||
|   the URL when the modal is closed. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       <.modal return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)}> | ||||
|         <.live_component | ||||
|           module={<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent} | ||||
|           id={@<%= schema.singular %>.id || :new} | ||||
|           title={@page_title} | ||||
|           action={@live_action} | ||||
|           return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)} | ||||
|           <%= schema.singular %>: @<%= schema.singular %> | ||||
|         /> | ||||
|       </.modal> | ||||
|   """ | ||||
|   def modal(assigns) | ||||
|  | ||||
|   defp hide_modal(js \\ %JS{}) do | ||||
|     js | ||||
|     |> JS.hide(to: "#modal", transition: "fade-out") | ||||
|     |> JS.hide(to: "#modal-bg", transition: "fade-out") | ||||
|     |> JS.hide(to: "#modal-content", transition: "fade-out-scale") | ||||
|   end | ||||
|  | ||||
|   attr :action, :string, required: true | ||||
|   attr :value, :boolean, required: true | ||||
|   attr :id, :string, default: nil | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   @doc """ | ||||
|   A toggle button element that can be directed to a liveview or a | ||||
|   live_component's `handle_event/3`. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|   <.toggle_button action="my_liveview_action" value={@some_value}> | ||||
|     <span>Toggle me!</span> | ||||
|   </.toggle_button> | ||||
|   <.toggle_button action="my_live_component_action" target={@myself} value={@some_value}> | ||||
|     <span>Whatever you want</span> | ||||
|   </.toggle_button> | ||||
|   """ | ||||
|   def toggle_button(assigns) | ||||
|  | ||||
|   attr :container, Container, required: true | ||||
|   slot(:tag_actions) | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   @spec container_card(assigns :: map()) :: Rendered.t() | ||||
|   def container_card(assigns) | ||||
|  | ||||
|   attr :tag, Tag, required: true | ||||
|   slot(:inner_block, required: true) | ||||
|  | ||||
|   def tag_card(assigns) | ||||
|  | ||||
|   attr :tag, Tag, required: true | ||||
|  | ||||
|   def simple_tag_card(assigns) | ||||
|  | ||||
|   attr :ammo_group, AmmoGroup, required: true | ||||
|   attr :show_container, :boolean, default: false | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   def ammo_group_card(assigns) | ||||
|  | ||||
|   attr :user, User, required: true | ||||
|   slot(:inner_block, required: true) | ||||
|  | ||||
|   def user_card(assigns) | ||||
|  | ||||
|   attr :invite, Invite, required: true | ||||
|   attr :current_user, User, required: true | ||||
|   slot(:inner_block) | ||||
|   slot(:code_actions) | ||||
|  | ||||
|   def invite_card(%{invite: invite, current_user: current_user} = assigns) do | ||||
|     assigns = assigns |> assign(:use_count, Invites.get_use_count(invite, current_user)) | ||||
|  | ||||
|     ~H""" | ||||
|     <div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4 | ||||
|       border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|       transition-all duration-300 ease-in-out"> | ||||
|       <h1 class="title text-xl"> | ||||
|         <%= @invite.name %> | ||||
|       </h1> | ||||
|  | ||||
|       <%= if @invite.disabled_at |> is_nil() do %> | ||||
|         <h2 class="title text-md"> | ||||
|           <%= if @invite.uses_left do %> | ||||
|             <%= gettext( | ||||
|               "Uses Left: %{uses_left_count}", | ||||
|               uses_left_count: @invite.uses_left | ||||
|             ) %> | ||||
|           <% else %> | ||||
|             <%= gettext("Uses Left: Unlimited") %> | ||||
|           <% end %> | ||||
|         </h2> | ||||
|       <% else %> | ||||
|         <h2 class="title text-md"> | ||||
|           <%= gettext("Invite Disabled") %> | ||||
|         </h2> | ||||
|       <% end %> | ||||
|  | ||||
|       <.qr_code | ||||
|         content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)} | ||||
|         filename={@invite.name} | ||||
|       /> | ||||
|  | ||||
|       <h2 :if={@use_count != 0} class="title text-md"> | ||||
|         <%= gettext("Uses: %{uses_count}", uses_count: @use_count) %> | ||||
|       </h2> | ||||
|  | ||||
|       <div class="flex flex-row flex-wrap justify-center items-center"> | ||||
|         <code | ||||
|           id={"code-#{@invite.id}"} | ||||
|           class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all text-gray-100 bg-primary-800" | ||||
|           phx-no-format | ||||
|         ><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code> | ||||
|         <%= if @code_actions, do: render_slot(@code_actions) %> | ||||
|       </div> | ||||
|  | ||||
|       <div :if={@inner_block} class="flex space-x-4 justify-center items-center"> | ||||
|         <%= render_slot(@inner_block) %> | ||||
|       </div> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   attr :content, :string, required: true | ||||
|   attr :filename, :string, default: "qrcode", doc: "filename without .png extension" | ||||
|   attr :image_class, :string, default: "w-64 h-max" | ||||
|   attr :width, :integer, default: 384, doc: "width of png to generate" | ||||
|  | ||||
|   @doc """ | ||||
|   Creates a downloadable QR Code element | ||||
|   """ | ||||
|   def qr_code(assigns) | ||||
|  | ||||
|   attr :date, :any, required: true, doc: "A `Date` struct or nil" | ||||
|  | ||||
|   @doc """ | ||||
|   Phoenix.Component for a <date> element that renders the Date in the user's | ||||
|   local timezone with Alpine.js | ||||
|   """ | ||||
|   def date(assigns) | ||||
|  | ||||
|   attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil" | ||||
|  | ||||
|   @doc """ | ||||
|   Phoenix.Component for a <time> element that renders the naivedatetime in the | ||||
|   user's local timezone with Alpine.js | ||||
|   """ | ||||
|   def datetime(assigns) | ||||
|  | ||||
|   @spec cast_datetime(NaiveDateTime.t() | nil) :: String.t() | ||||
|   defp cast_datetime(%NaiveDateTime{} = datetime) do | ||||
|     datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended) | ||||
|   end | ||||
|  | ||||
|   defp cast_datetime(_datetime), do: "" | ||||
| end | ||||
| @@ -0,0 +1,74 @@ | ||||
| <div | ||||
|   id={"ammo_group-#{@ammo_group.id}"} | ||||
|   class="mx-4 my-2 px-8 py-4 | ||||
|     flex flex-col justify-center items-center | ||||
|     border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|     transition-all duration-300 ease-in-out" | ||||
| > | ||||
|   <.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="mb-2 link"> | ||||
|     <h1 class="title text-xl title-primary-500"> | ||||
|       <%= @ammo_group.ammo_type.name %> | ||||
|     </h1> | ||||
|   </.link> | ||||
|  | ||||
|   <div class="flex flex-col justify-center items-center"> | ||||
|     <span class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Count:") %> | ||||
|       <%= if @ammo_group.count == 0, do: gettext("Empty"), else: @ammo_group.count %> | ||||
|     </span> | ||||
|  | ||||
|     <span | ||||
|       :if={@ammo_group |> Ammo.get_original_count() != @ammo_group.count} | ||||
|       class="rounded-lg title text-lg" | ||||
|     > | ||||
|       <%= gettext("Original Count:") %> | ||||
|       <%= @ammo_group |> Ammo.get_original_count() %> | ||||
|     </span> | ||||
|  | ||||
|     <span :if={@ammo_group.notes} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Notes:") %> | ||||
|       <%= @ammo_group.notes %> | ||||
|     </span> | ||||
|  | ||||
|     <span class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Purchased on:") %> | ||||
|       <.date date={@ammo_group.purchased_on} /> | ||||
|     </span> | ||||
|  | ||||
|     <span :if={@ammo_group |> Ammo.get_last_used_shot_group()} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Last used on:") %> | ||||
|       <.date date={@ammo_group |> Ammo.get_last_used_shot_group() |> Map.get(:date)} /> | ||||
|     </span> | ||||
|  | ||||
|     <%= if @ammo_group.price_paid do %> | ||||
|       <span class="rounded-lg title text-lg"> | ||||
|         <%= gettext("Price paid:") %> | ||||
|         <%= gettext("$%{amount}", | ||||
|           amount: @ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2) | ||||
|         ) %> | ||||
|       </span> | ||||
|  | ||||
|       <span class="rounded-lg title text-lg"> | ||||
|         <%= gettext("CPR:") %> | ||||
|         <%= gettext("$%{amount}", | ||||
|           amount: @ammo_group |> Ammo.get_cpr() |> :erlang.float_to_binary(decimals: 2) | ||||
|         ) %> | ||||
|       </span> | ||||
|     <% end %> | ||||
|  | ||||
|     <span :if={@show_container and @ammo_group.container} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Container:") %> | ||||
|  | ||||
|       <.link | ||||
|         navigate={Routes.container_show_path(Endpoint, :show, @ammo_group.container)} | ||||
|         class="link" | ||||
|       > | ||||
|         <%= @ammo_group.container.name %> | ||||
|       </.link> | ||||
|     </span> | ||||
|   </div> | ||||
|  | ||||
|   <div :if={@inner_block} class="mt-4 flex space-x-4 justify-center items-center"> | ||||
|     <%= render_slot(@inner_block) %> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -0,0 +1,55 @@ | ||||
| <div | ||||
|   id={"container-#{@container.id}"} | ||||
|   class="overflow-hidden max-w-full mx-4 mb-4 px-8 py-4 | ||||
|     flex flex-col justify-center items-center space-y-4 | ||||
|     border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|     transition-all duration-300 ease-in-out" | ||||
| > | ||||
|   <div class="max-w-full mb-4 flex flex-col justify-center items-center space-y-2"> | ||||
|     <.link navigate={Routes.container_show_path(Endpoint, :show, @container)} class="link"> | ||||
|       <h1 class="px-4 py-2 rounded-lg title text-xl"> | ||||
|         <%= @container.name %> | ||||
|       </h1> | ||||
|     </.link> | ||||
|  | ||||
|     <span :if={@container.desc} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Description:") %> | ||||
|       <%= @container.desc %> | ||||
|     </span> | ||||
|  | ||||
|     <span class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Type:") %> | ||||
|       <%= @container.type %> | ||||
|     </span> | ||||
|  | ||||
|     <span :if={@container.location} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Location:") %> | ||||
|       <%= @container.location %> | ||||
|     </span> | ||||
|  | ||||
|     <%= unless @container.ammo_groups |> Enum.empty?() do %> | ||||
|       <span class="rounded-lg title text-lg"> | ||||
|         <%= gettext("Packs:") %> | ||||
|         <%= @container |> Containers.get_container_ammo_group_count!() %> | ||||
|       </span> | ||||
|  | ||||
|       <span class="rounded-lg title text-lg"> | ||||
|         <%= gettext("Rounds:") %> | ||||
|         <%= @container |> Containers.get_container_rounds!() %> | ||||
|       </span> | ||||
|     <% end %> | ||||
|  | ||||
|     <div class="flex flex-wrap justify-center items-center"> | ||||
|       <.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} /> | ||||
|  | ||||
|       <%= if @tag_actions, do: render_slot(@tag_actions) %> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
|   <div | ||||
|     :if={assigns |> Map.has_key?(:inner_block)} | ||||
|     class="flex space-x-4 justify-center items-center" | ||||
|   > | ||||
|     <%= render_slot(@inner_block) %> | ||||
|   </div> | ||||
| </div> | ||||
							
								
								
									
										12
									
								
								lib/cannery_web/components/core_components/date.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/cannery_web/components/core_components/date.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <time | ||||
|   :if={@date} | ||||
|   datetime={@date |> Date.to_iso8601(:extended)} | ||||
|   x-data={~s<{ | ||||
|     date: | ||||
|       Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}) | ||||
|         .format(new Date("#{Date.to_iso8601(@date, :extended)}")) | ||||
|   }>} | ||||
|   x-text="date" | ||||
| > | ||||
|   <%= @date |> Date.to_iso8601(:extended) %> | ||||
| </time> | ||||
| @@ -0,0 +1,12 @@ | ||||
| <time | ||||
|   :if={@datetime} | ||||
|   datetime={cast_datetime(@datetime)} | ||||
|   x-data={~s/{ | ||||
|     datetime: | ||||
|       Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'}) | ||||
|         .format(new Date("#{cast_datetime(@datetime)}")) | ||||
|   }/} | ||||
|   x-text="datetime" | ||||
| > | ||||
|   <%= cast_datetime(@datetime) %> | ||||
| </time> | ||||
							
								
								
									
										42
									
								
								lib/cannery_web/components/core_components/modal.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								lib/cannery_web/components/core_components/modal.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <.link | ||||
|   patch={@return_to} | ||||
|   id="modal-bg" | ||||
|   class="fade-in fixed z-10 left-0 top-0 | ||||
|     w-full h-full overflow-hidden | ||||
|     p-8 flex flex-col justify-center items-center cursor-auto" | ||||
|   style="background-color: rgba(0,0,0,0.4);" | ||||
|   phx-remove={hide_modal()} | ||||
| > | ||||
|   <span class="hidden"></span> | ||||
| </.link> | ||||
|  | ||||
| <div | ||||
|   id="modal" | ||||
|   class="fixed z-10 left-0 top-0 pointer-events-none | ||||
|     w-full h-full overflow-hidden | ||||
|     p-4 sm:p-8 flex flex-col justify-center items-center" | ||||
| > | ||||
|   <div | ||||
|     id="modal-content" | ||||
|     class="fade-in-scale w-full max-w-3xl relative | ||||
|       pointer-events-auto overflow-hidden | ||||
|       px-8 py-4 sm:py-8 | ||||
|       flex flex-col justify-start items-center | ||||
|       bg-white border-2 rounded-lg" | ||||
|   > | ||||
|     <.link | ||||
|       patch={@return_to} | ||||
|       id="close" | ||||
|       class="absolute top-8 right-10 | ||||
|         text-gray-500 hover:text-gray-800 | ||||
|         transition-all duration-500 ease-in-out" | ||||
|       phx-remove={hide_modal()} | ||||
|     > | ||||
|       <i class="fa-fw fa-lg fas fa-times"></i> | ||||
|     </.link> | ||||
|  | ||||
|     <div class="overflow-x-hidden overflow-y-auto w-full p-8 flex flex-col space-y-4 justify-start items-center"> | ||||
|       <%= render_slot(@inner_block) %> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -0,0 +1,3 @@ | ||||
| <.link href={qr_code_image(@content)} download={@filename <> ".png"}> | ||||
|   <img class={@image_class} alt={@filename} src={qr_code_image(@content)} /> | ||||
| </.link> | ||||
| @@ -0,0 +1,6 @@ | ||||
| <h1 | ||||
|   class="inline-block break-all mx-2 my-1 px-4 py-2 rounded-lg title text-xl" | ||||
|   style={"color: #{@tag.text_color}; background-color: #{@tag.bg_color}"} | ||||
| > | ||||
|   <%= @tag.name %> | ||||
| </h1> | ||||
| @@ -0,0 +1,9 @@ | ||||
| <div | ||||
|   id={"tag-#{@tag.id}"} | ||||
|   class="mx-4 mb-4 px-8 py-4 space-x-4 flex justify-center items-center | ||||
|     border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|     transition-all duration-300 ease-in-out" | ||||
| > | ||||
|   <.simple_tag_card tag={@tag} /> | ||||
|   <%= render_slot(@inner_block) %> | ||||
| </div> | ||||
| @@ -0,0 +1,30 @@ | ||||
| <label for={@id || @action} class="inline-flex relative items-center cursor-pointer"> | ||||
|   <input | ||||
|     id={@id || @action} | ||||
|     type="checkbox" | ||||
|     value={@value} | ||||
|     checked={@value} | ||||
|     class="sr-only peer" | ||||
|     aria-labelledby={"#{@id || @action}-label"} | ||||
|     { | ||||
|       if assigns |> Map.has_key?(:target), | ||||
|         do: %{"phx-click": @action, "phx-value-value": @value, "phx-target": @target}, | ||||
|         else: %{"phx-click": @action, "phx-value-value": @value} | ||||
|     } | ||||
|   /> | ||||
|   <div class="w-11 h-6 bg-gray-300 rounded-full peer | ||||
|     peer-focus:ring-4 peer-focus:ring-teal-300 dark:peer-focus:ring-teal-800 | ||||
|     peer-checked:bg-gray-600 | ||||
|     peer-checked:after:translate-x-full peer-checked:after:border-white | ||||
|     after:content-[''] after:absolute after:top-1 after:left-[2px] after:bg-white after:border-gray-300 | ||||
|     after:border after:rounded-full after:h-5 after:w-5 | ||||
|     after:transition-all after:duration-250 after:ease-in-out | ||||
|     transition-colors duration-250 ease-in-out"> | ||||
|   </div> | ||||
|   <span | ||||
|     id={"#{@id || @action}-label"} | ||||
|     class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300" | ||||
|   > | ||||
|     <%= render_slot(@inner_block) %> | ||||
|   </span> | ||||
| </label> | ||||
							
								
								
									
										128
									
								
								lib/cannery_web/components/core_components/topbar.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								lib/cannery_web/components/core_components/topbar.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| <nav role="navigation" class="mb-8 px-8 py-4 w-full bg-primary-400"> | ||||
|   <div class="flex flex-col sm:flex-row justify-between items-center"> | ||||
|     <div class="mb-4 sm:mb-0 sm:mr-8 flex flex-row justify-start items-center space-x-2"> | ||||
|       <.link | ||||
|         navigate={Routes.live_path(Endpoint, HomeLive)} | ||||
|         class="inline mx-2 my-1 leading-5 text-xl text-white" | ||||
|       > | ||||
|         <img | ||||
|           src={Routes.static_path(Endpoint, "/images/cannery.svg")} | ||||
|           alt={gettext("Cannery logo")} | ||||
|           class="inline-block h-8 mx-1" | ||||
|         /> | ||||
|         <h1 class="inline hover:underline">Cannery</h1> | ||||
|       </.link> | ||||
|  | ||||
|       <%= if @title_content do %> | ||||
|         <span class="mx-2 my-1"> | ||||
|           | | ||||
|         </span> | ||||
|         <%= @title_content %> | ||||
|       <% end %> | ||||
|     </div> | ||||
|  | ||||
|     <hr class="mb-2 sm:hidden hr-light" /> | ||||
|  | ||||
|     <ul class="flex flex-row flex-wrap justify-center items-center | ||||
|       text-lg text-white text-ellipsis"> | ||||
|       <%= if @current_user do %> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             navigate={Routes.tag_index_path(Endpoint, :index)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <%= gettext("Tags") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             navigate={Routes.container_index_path(Endpoint, :index)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <%= gettext("Containers") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             navigate={Routes.ammo_type_index_path(Endpoint, :index)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <%= gettext("Catalog") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             navigate={Routes.ammo_group_index_path(Endpoint, :index)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <%= gettext("Ammo") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             navigate={Routes.range_index_path(Endpoint, :index)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <%= gettext("Range") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li :if={@current_user |> Accounts.is_already_admin?()} class="mx-2 my-1"> | ||||
|           <.link | ||||
|             navigate={Routes.invite_index_path(Endpoint, :index)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <%= gettext("Invites") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             href={Routes.user_settings_path(Endpoint, :edit)} | ||||
|             class="text-white hover:underline truncate" | ||||
|           > | ||||
|             <%= @current_user.email %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             href={Routes.user_session_path(Endpoint, :delete)} | ||||
|             method="delete" | ||||
|             data-confirm={dgettext("prompts", "Are you sure you want to log out?")} | ||||
|           > | ||||
|             <i class="fas fa-sign-out-alt"></i> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li | ||||
|           :if={ | ||||
|             @current_user |> Accounts.is_already_admin?() and | ||||
|               function_exported?(Routes, :live_dashboard_path, 2) | ||||
|           } | ||||
|           class="mx-2 my-1" | ||||
|         > | ||||
|           <.link | ||||
|             navigate={Routes.live_dashboard_path(Endpoint, :home)} | ||||
|             class="text-white hover:underline" | ||||
|           > | ||||
|             <i class="fas fa-gauge"></i> | ||||
|           </.link> | ||||
|         </li> | ||||
|       <% else %> | ||||
|         <li :if={Accounts.allow_registration?()} class="mx-2 my-1"> | ||||
|           <.link | ||||
|             href={Routes.user_registration_path(Endpoint, :new)} | ||||
|             class="text-white hover:underline truncate" | ||||
|           > | ||||
|             <%= dgettext("actions", "Register") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|         <li class="mx-2 my-1"> | ||||
|           <.link | ||||
|             href={Routes.user_session_path(Endpoint, :new)} | ||||
|             class="text-white hover:underline truncate" | ||||
|           > | ||||
|             <%= dgettext("actions", "Log in") %> | ||||
|           </.link> | ||||
|         </li> | ||||
|       <% end %> | ||||
|     </ul> | ||||
|   </div> | ||||
| </nav> | ||||
| @@ -0,0 +1,36 @@ | ||||
| <div | ||||
|   id={"user-#{@user.id}"} | ||||
|   class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center text-center | ||||
|     border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|     transition-all duration-300 ease-in-out" | ||||
| > | ||||
|   <h1 class="px-4 py-2 rounded-lg title text-xl break-all"> | ||||
|     <%= @user.email %> | ||||
|   </h1> | ||||
|  | ||||
|   <h3 class="px-4 py-2 rounded-lg title text-lg"> | ||||
|     <p> | ||||
|       <%= if @user.confirmed_at do %> | ||||
|         <%= gettext( | ||||
|           "User was confirmed at%{confirmed_datetime}", | ||||
|           confirmed_datetime: "" | ||||
|         ) %> | ||||
|         <.datetime datetime={@user.confirmed_at} /> | ||||
|       <% else %> | ||||
|         <%= gettext("Email unconfirmed") %> | ||||
|       <% end %> | ||||
|     </p> | ||||
|  | ||||
|     <p> | ||||
|       <%= gettext( | ||||
|         "User registered on%{registered_datetime}", | ||||
|         registered_datetime: "" | ||||
|       ) %> | ||||
|       <.datetime datetime={@user.inserted_at} /> | ||||
|     </p> | ||||
|   </h3> | ||||
|  | ||||
|   <div :if={@inner_block} class="px-4 py-2 flex space-x-4 justify-center items-center"> | ||||
|     <%= render_slot(@inner_block) %> | ||||
|   </div> | ||||
| </div> | ||||
| @@ -1,70 +0,0 @@ | ||||
| defmodule CanneryWeb.Components.InviteCard do | ||||
|   @moduledoc """ | ||||
|   Display card for an invite | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :component | ||||
|   alias Cannery.Accounts.{Invite, Invites, User} | ||||
|   alias CanneryWeb.Endpoint | ||||
|  | ||||
|   attr :invite, Invite, required: true | ||||
|   attr :current_user, User, required: true | ||||
|   slot(:inner_block) | ||||
|   slot(:code_actions) | ||||
|  | ||||
|   def invite_card(%{invite: invite, current_user: current_user} = assigns) do | ||||
|     assigns = | ||||
|       assigns | ||||
|       |> assign(:use_count, Invites.get_use_count(invite, current_user)) | ||||
|       |> assign_new(:code_actions, fn -> [] end) | ||||
|  | ||||
|     ~H""" | ||||
|     <div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4 | ||||
|       border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|       transition-all duration-300 ease-in-out"> | ||||
|       <h1 class="title text-xl"> | ||||
|         <%= @invite.name %> | ||||
|       </h1> | ||||
|  | ||||
|       <%= if @invite.disabled_at |> is_nil() do %> | ||||
|         <h2 class="title text-md"> | ||||
|           <%= if @invite.uses_left do %> | ||||
|             <%= gettext( | ||||
|               "Uses Left: %{uses_left_count}", | ||||
|               uses_left_count: @invite.uses_left | ||||
|             ) %> | ||||
|           <% else %> | ||||
|             <%= gettext("Uses Left: Unlimited") %> | ||||
|           <% end %> | ||||
|         </h2> | ||||
|       <% else %> | ||||
|         <h2 class="title text-md"> | ||||
|           <%= gettext("Invite Disabled") %> | ||||
|         </h2> | ||||
|       <% end %> | ||||
|  | ||||
|       <.qr_code | ||||
|         content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)} | ||||
|         filename={@invite.name} | ||||
|       /> | ||||
|  | ||||
|       <h2 :if={@use_count != 0} class="title text-md"> | ||||
|         <%= gettext("Uses: %{uses_count}", uses_count: @use_count) %> | ||||
|       </h2> | ||||
|  | ||||
|       <div class="flex flex-row flex-wrap justify-center items-center"> | ||||
|         <code | ||||
|           id={"code-#{@invite.id}"} | ||||
|           class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all text-gray-100 bg-primary-800" | ||||
|           phx-no-format | ||||
|         ><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code> | ||||
|         <%= render_slot(@code_actions) %> | ||||
|       </div> | ||||
|  | ||||
|       <div :if={@inner_block} class="flex space-x-4 justify-center items-center"> | ||||
|         <%= render_slot(@inner_block) %> | ||||
|       </div> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -1,38 +0,0 @@ | ||||
| defmodule CanneryWeb.Components.TagCard do | ||||
|   @moduledoc """ | ||||
|   Display card for a tag | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :component | ||||
|   alias Cannery.Tags.Tag | ||||
|  | ||||
|   attr :tag, Tag, required: true | ||||
|   slot(:inner_block, required: true) | ||||
|  | ||||
|   def tag_card(assigns) do | ||||
|     ~H""" | ||||
|     <div | ||||
|       id={"tag-#{@tag.id}"} | ||||
|       class="mx-4 mb-4 px-8 py-4 space-x-4 flex justify-center items-center | ||||
|         border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|         transition-all duration-300 ease-in-out" | ||||
|     > | ||||
|       <.simple_tag_card tag={@tag} /> | ||||
|       <%= render_slot(@inner_block) %> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   attr :tag, Tag, required: true | ||||
|  | ||||
|   def simple_tag_card(assigns) do | ||||
|     ~H""" | ||||
|     <h1 | ||||
|       class="inline-block break-all mx-2 my-1 px-4 py-2 rounded-lg title text-xl" | ||||
|       style={"color: #{@tag.text_color}; background-color: #{@tag.bg_color}"} | ||||
|     > | ||||
|       <%= @tag.name %> | ||||
|     </h1> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -1,146 +0,0 @@ | ||||
| defmodule CanneryWeb.Components.Topbar do | ||||
|   @moduledoc """ | ||||
|   Component that renders a topbar with user functions/links | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :component | ||||
|  | ||||
|   alias Cannery.Accounts | ||||
|   alias CanneryWeb.HomeLive | ||||
|  | ||||
|   def topbar(assigns) do | ||||
|     assigns = | ||||
|       %{results: [], title_content: nil, flash: nil, current_user: nil} |> Map.merge(assigns) | ||||
|  | ||||
|     ~H""" | ||||
|     <nav role="navigation" class="mb-8 px-8 py-4 w-full bg-primary-400"> | ||||
|       <div class="flex flex-col sm:flex-row justify-between items-center"> | ||||
|         <div class="mb-4 sm:mb-0 sm:mr-8 flex flex-row justify-start items-center space-x-2"> | ||||
|           <.link | ||||
|             navigate={Routes.live_path(Endpoint, HomeLive)} | ||||
|             class="inline mx-2 my-1 leading-5 text-xl text-white" | ||||
|           > | ||||
|             <img | ||||
|               src={Routes.static_path(Endpoint, "/images/cannery.svg")} | ||||
|               alt={gettext("Cannery logo")} | ||||
|               class="inline-block h-8 mx-1" | ||||
|             /> | ||||
|             <h1 class="inline hover:underline">Cannery</h1> | ||||
|           </.link> | ||||
|  | ||||
|           <%= if @title_content do %> | ||||
|             <span class="mx-2 my-1"> | ||||
|               | | ||||
|             </span> | ||||
|             <%= @title_content %> | ||||
|           <% end %> | ||||
|         </div> | ||||
|  | ||||
|         <hr class="mb-2 sm:hidden hr-light" /> | ||||
|  | ||||
|         <ul class="flex flex-row flex-wrap justify-center items-center | ||||
|           text-lg text-white text-ellipsis"> | ||||
|           <%= if @current_user do %> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 navigate={Routes.tag_index_path(Endpoint, :index)} | ||||
|                 class="text-primary-600 text-white hover:underline" | ||||
|               > | ||||
|                 <%= gettext("Tags") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 navigate={Routes.container_index_path(Endpoint, :index)} | ||||
|                 class="text-primary-600 text-white hover:underline" | ||||
|               > | ||||
|                 <%= gettext("Containers") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 navigate={Routes.ammo_type_index_path(Endpoint, :index)} | ||||
|                 class="text-primary-600 text-white hover:underline" | ||||
|               > | ||||
|                 <%= gettext("Catalog") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 navigate={Routes.ammo_group_index_path(Endpoint, :index)} | ||||
|                 class="text-primary-600 text-white hover:underline" | ||||
|               > | ||||
|                 <%= gettext("Ammo") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 navigate={Routes.range_index_path(Endpoint, :index)} | ||||
|                 class="text-primary-600 text-white hover:underline" | ||||
|               > | ||||
|                 <%= gettext("Range") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li :if={@current_user |> Accounts.is_already_admin?()} class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 navigate={Routes.invite_index_path(Endpoint, :index)} | ||||
|                 class="text-primary-600 text-white hover:underline" | ||||
|               > | ||||
|                 <%= gettext("Invites") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 href={Routes.user_settings_path(Endpoint, :edit)} | ||||
|                 class="text-primary-600 text-white hover:underline truncate" | ||||
|               > | ||||
|                 <%= @current_user.email %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 href={Routes.user_session_path(Endpoint, :delete)} | ||||
|                 method="delete" | ||||
|                 data-confirm={dgettext("prompts", "Are you sure you want to log out?")} | ||||
|               > | ||||
|                 <i class="fas fa-sign-out-alt"></i> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li | ||||
|               :if={ | ||||
|                 @current_user |> Accounts.is_already_admin?() and | ||||
|                   function_exported?(Routes, :live_dashboard_path, 2) | ||||
|               } | ||||
|               class="mx-2 my-1" | ||||
|             > | ||||
|               <.link | ||||
|                 navigate={Routes.live_dashboard_path(Endpoint, :home)} | ||||
|                 class="text-white text-white hover:underline" | ||||
|               > | ||||
|                 <i class="fas fa-gauge"></i> | ||||
|               </.link> | ||||
|             </li> | ||||
|           <% else %> | ||||
|             <li :if={Accounts.allow_registration?()} class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 href={Routes.user_registration_path(Endpoint, :new)} | ||||
|                 class="text-white hover:underline truncate" | ||||
|               > | ||||
|                 <%= dgettext("actions", "Register") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|             <li class="mx-2 my-1"> | ||||
|               <.link | ||||
|                 href={Routes.user_session_path(Endpoint, :new)} | ||||
|                 class="text-white hover:underline truncate" | ||||
|               > | ||||
|                 <%= dgettext("actions", "Log in") %> | ||||
|               </.link> | ||||
|             </li> | ||||
|           <% end %> | ||||
|         </ul> | ||||
|       </div> | ||||
|     </nav> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -1,52 +0,0 @@ | ||||
| defmodule CanneryWeb.Components.UserCard do | ||||
|   @moduledoc """ | ||||
|   Display card for a user | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :component | ||||
|   alias Cannery.Accounts.User | ||||
|  | ||||
|   attr :user, User, required: true | ||||
|   slot(:inner_block, required: true) | ||||
|  | ||||
|   def user_card(assigns) do | ||||
|     ~H""" | ||||
|     <div | ||||
|       id={"user-#{@user.id}"} | ||||
|       class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center text-center | ||||
|         border border-gray-400 rounded-lg shadow-lg hover:shadow-md | ||||
|         transition-all duration-300 ease-in-out" | ||||
|     > | ||||
|       <h1 class="px-4 py-2 rounded-lg title text-xl break-all"> | ||||
|         <%= @user.email %> | ||||
|       </h1> | ||||
|  | ||||
|       <h3 class="px-4 py-2 rounded-lg title text-lg"> | ||||
|         <p> | ||||
|           <%= if @user.confirmed_at do %> | ||||
|             <%= gettext( | ||||
|               "User was confirmed at%{confirmed_datetime}", | ||||
|               confirmed_datetime: "" | ||||
|             ) %> | ||||
|             <.datetime datetime={@user.confirmed_at} /> | ||||
|           <% else %> | ||||
|             <%= gettext("Email unconfirmed") %> | ||||
|           <% end %> | ||||
|         </p> | ||||
|  | ||||
|         <p> | ||||
|           <%= gettext( | ||||
|             "User registered on%{registered_datetime}", | ||||
|             registered_datetime: "" | ||||
|           ) %> | ||||
|           <.datetime datetime={@user.inserted_at} /> | ||||
|         </p> | ||||
|       </h3> | ||||
|  | ||||
|       <div :if={@inner_block} class="px-4 py-2 flex space-x-4 justify-center items-center"> | ||||
|         <%= render_slot(@inner_block) %> | ||||
|       </div> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -45,7 +45,8 @@ | ||||
|   <div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl"> | ||||
|     <.form | ||||
|       :let={f} | ||||
|       for={:search} | ||||
|       for={%{}} | ||||
|       as={:search} | ||||
|       phx-change="search" | ||||
|       phx-submit="search" | ||||
|       class="grow self-stretch flex flex-col items-stretch" | ||||
|   | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.AmmoGroupLive.Show do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.ContainerCard | ||||
|   alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.AmmoGroup, Repo} | ||||
|   alias CanneryWeb.Endpoint | ||||
|   alias Phoenix.LiveView.Socket | ||||
|   | ||||
| @@ -20,7 +20,8 @@ | ||||
|     <div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl"> | ||||
|       <.form | ||||
|         :let={f} | ||||
|         for={:search} | ||||
|         for={%{}} | ||||
|         as={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow self-stretch flex flex-col items-stretch" | ||||
|   | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.AmmoTypeLive.Show do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.AmmoGroupCard | ||||
|   alias Cannery.{Ammo, Ammo.AmmoType} | ||||
|   alias CanneryWeb.Endpoint | ||||
|  | ||||
|   | ||||
| @@ -36,7 +36,8 @@ | ||||
|  | ||||
|     <.form | ||||
|       :let={f} | ||||
|       for={:tag} | ||||
|       for={%{}} | ||||
|       as={:tag} | ||||
|       id="add-tag-to-container-form" | ||||
|       class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" | ||||
|       phx-target={@myself} | ||||
|   | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.ContainerLive.Index do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.ContainerCard | ||||
|   alias Cannery.{Containers, Containers.Container, Repo} | ||||
|   alias Ecto.Changeset | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,8 @@ | ||||
|     <div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl"> | ||||
|       <.form | ||||
|         :let={f} | ||||
|         for={:search} | ||||
|         for={%{}} | ||||
|         as={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow self-stretch flex flex-col items-stretch" | ||||
|   | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.ContainerLive.Show do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.{AmmoGroupCard, TagCard} | ||||
|   alias Cannery.{Accounts.User, Ammo, Containers, Containers.Container, Repo, Tags} | ||||
|   alias CanneryWeb.Endpoint | ||||
|   alias Ecto.Changeset | ||||
|   | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.InviteLive.Index do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.{InviteCard, UserCard} | ||||
|   alias Cannery.Accounts | ||||
|   alias Cannery.Accounts.{Invite, Invites} | ||||
|   alias CanneryWeb.HomeLive | ||||
|   | ||||
| @@ -1,136 +0,0 @@ | ||||
| defmodule CanneryWeb.LiveHelpers do | ||||
|   @moduledoc """ | ||||
|   Contains common helper functions for liveviews | ||||
|   """ | ||||
|  | ||||
|   use Phoenix.Component | ||||
|   alias Phoenix.LiveView.JS | ||||
|  | ||||
|   attr :return_to, :string, required: true | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   @doc """ | ||||
|   Renders a live component inside a modal. | ||||
|  | ||||
|   The rendered modal receives a `:return_to` option to properly update | ||||
|   the URL when the modal is closed. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       <.modal return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)}> | ||||
|         <.live_component | ||||
|           module={<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent} | ||||
|           id={@<%= schema.singular %>.id || :new} | ||||
|           title={@page_title} | ||||
|           action={@live_action} | ||||
|           return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)} | ||||
|           <%= schema.singular %>: @<%= schema.singular %> | ||||
|         /> | ||||
|       </.modal> | ||||
|   """ | ||||
|   def modal(assigns) do | ||||
|     ~H""" | ||||
|     <.link | ||||
|       patch={@return_to} | ||||
|       id="modal-bg" | ||||
|       class="fade-in fixed z-10 left-0 top-0 | ||||
|         w-full h-full overflow-hidden | ||||
|         p-8 flex flex-col justify-center items-center cursor-auto" | ||||
|       style="background-color: rgba(0,0,0,0.4);" | ||||
|       phx-remove={hide_modal()} | ||||
|     > | ||||
|       <span class="hidden"></span> | ||||
|     </.link> | ||||
|  | ||||
|     <div | ||||
|       id="modal" | ||||
|       class="fixed z-10 left-0 top-0 pointer-events-none | ||||
|         w-full h-full overflow-hidden | ||||
|         p-4 sm:p-8 flex flex-col justify-center items-center" | ||||
|     > | ||||
|       <div | ||||
|         id="modal-content" | ||||
|         class="fade-in-scale w-full max-w-3xl relative | ||||
|           pointer-events-auto overflow-hidden | ||||
|           px-8 py-4 sm:py-8 | ||||
|           flex flex-col justify-start items-center | ||||
|           bg-white border-2 rounded-lg" | ||||
|       > | ||||
|         <.link | ||||
|           patch={@return_to} | ||||
|           id="close" | ||||
|           class="absolute top-8 right-10 | ||||
|             text-gray-500 hover:text-gray-800 | ||||
|             transition-all duration-500 ease-in-out" | ||||
|           phx-remove={hide_modal()} | ||||
|         > | ||||
|           <i class="fa-fw fa-lg fas fa-times"></i> | ||||
|         </.link> | ||||
|  | ||||
|         <div class="overflow-x-hidden overflow-y-auto w-full p-8 flex flex-col space-y-4 justify-start items-center"> | ||||
|           <%= render_slot(@inner_block) %> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   defp hide_modal(js \\ %JS{}) do | ||||
|     js | ||||
|     |> JS.hide(to: "#modal", transition: "fade-out") | ||||
|     |> JS.hide(to: "#modal-bg", transition: "fade-out") | ||||
|     |> JS.hide(to: "#modal-content", transition: "fade-out-scale") | ||||
|   end | ||||
|  | ||||
|   attr :action, :string, required: true | ||||
|   attr :value, :boolean, required: true | ||||
|   attr :id, :string | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   @doc """ | ||||
|   A toggle button element that can be directed to a liveview or a | ||||
|   live_component's `handle_event/3`. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|   <.toggle_button action="my_liveview_action" value={@some_value}> | ||||
|     <span>Toggle me!</span> | ||||
|   </.toggle_button> | ||||
|   <.toggle_button action="my_live_component_action" target={@myself} value={@some_value}> | ||||
|     <span>Whatever you want</span> | ||||
|   </.toggle_button> | ||||
|   """ | ||||
|   def toggle_button(assigns) do | ||||
|     assigns = assigns |> assign_new(:id, fn -> assigns.action end) | ||||
|  | ||||
|     ~H""" | ||||
|     <label for={@id} class="inline-flex relative items-center cursor-pointer"> | ||||
|       <input | ||||
|         id={@id} | ||||
|         type="checkbox" | ||||
|         value={@value} | ||||
|         checked={@value} | ||||
|         class="sr-only peer" | ||||
|         aria-labelledby={"#{@id}-label"} | ||||
|         { | ||||
|           if assigns |> Map.has_key?(:target), | ||||
|             do: %{"phx-click": @action, "phx-value-value": @value, "phx-target": @target}, | ||||
|             else: %{"phx-click": @action, "phx-value-value": @value} | ||||
|         } | ||||
|       /> | ||||
|       <div class="w-11 h-6 bg-gray-300 rounded-full peer | ||||
|         peer-focus:ring-4 peer-focus:ring-teal-300 dark:peer-focus:ring-teal-800 | ||||
|         peer-checked:bg-gray-600 | ||||
|         peer-checked:after:translate-x-full peer-checked:after:border-white | ||||
|         after:content-[''] after:absolute after:top-1 after:left-[2px] after:bg-white after:border-gray-300 | ||||
|         after:border after:rounded-full after:h-5 after:w-5 | ||||
|         after:transition-all after:duration-250 after:ease-in-out | ||||
|         transition-colors duration-250 ease-in-out"> | ||||
|       </div> | ||||
|       <span id={"#{@id}-label"} class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300"> | ||||
|         <%= render_slot(@inner_block) %> | ||||
|       </span> | ||||
|     </label> | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.RangeLive.Index do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.AmmoGroupCard | ||||
|   alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo, Repo} | ||||
|   alias CanneryWeb.Endpoint | ||||
|   alias Phoenix.LiveView.Socket | ||||
|   | ||||
| @@ -70,7 +70,8 @@ | ||||
|     <div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl"> | ||||
|       <.form | ||||
|         :let={f} | ||||
|         for={:search} | ||||
|         for={%{}} | ||||
|         as={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow self-stretch flex flex-col items-stretch" | ||||
|   | ||||
| @@ -4,7 +4,6 @@ defmodule CanneryWeb.TagLive.Index do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   import CanneryWeb.Components.TagCard | ||||
|   alias Cannery.{Tags, Tags.Tag} | ||||
|   alias CanneryWeb.ViewHelpers | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,8 @@ | ||||
|   <div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl"> | ||||
|     <.form | ||||
|       :let={f} | ||||
|       for={:search} | ||||
|       for={%{}} | ||||
|       as={:search} | ||||
|       phx-change="search" | ||||
|       phx-submit="search" | ||||
|       class="grow self-stretch flex flex-col items-stretch" | ||||
|   | ||||
| @@ -5,7 +5,8 @@ | ||||
|  | ||||
|   <.form | ||||
|     :let={f} | ||||
|     for={:user} | ||||
|     for={%{}} | ||||
|     as={:user} | ||||
|     action={Routes.user_confirmation_path(@conn, :create)} | ||||
|     class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" | ||||
|   > | ||||
|   | ||||
| @@ -5,7 +5,8 @@ | ||||
|  | ||||
|   <.form | ||||
|     :let={f} | ||||
|     for={:user} | ||||
|     for={%{}} | ||||
|     as={:user} | ||||
|     action={Routes.user_reset_password_path(@conn, :create)} | ||||
|     class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" | ||||
|   > | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| defmodule CanneryWeb.ErrorView do | ||||
|   use CanneryWeb, :view | ||||
|   import CanneryWeb.Components.Topbar | ||||
|   alias CanneryWeb.HomeLive | ||||
|  | ||||
|   def template_not_found(error_path, _assigns) do | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| defmodule CanneryWeb.LayoutView do | ||||
|   use CanneryWeb, :view | ||||
|   import CanneryWeb.Components.Topbar | ||||
|   alias CanneryWeb.HomeLive | ||||
|  | ||||
|   # Phoenix LiveDashboard is available only in development by default, | ||||
|   | ||||
| @@ -7,61 +7,6 @@ defmodule CanneryWeb.ViewHelpers do | ||||
|  | ||||
|   use Phoenix.Component | ||||
|  | ||||
|   @doc """ | ||||
|   Phoenix.Component for a <time> element that renders the naivedatetime in the | ||||
|   user's local timezone with Alpine.js | ||||
|   """ | ||||
|  | ||||
|   attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil" | ||||
|  | ||||
|   def datetime(assigns) do | ||||
|     ~H""" | ||||
|     <time | ||||
|       :if={@datetime} | ||||
|       datetime={cast_datetime(@datetime)} | ||||
|       x-data={"{ | ||||
|         datetime: | ||||
|           Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'}) | ||||
|             .format(new Date(\"#{cast_datetime(@datetime)}\")) | ||||
|       }"} | ||||
|       x-text="datetime" | ||||
|     > | ||||
|       <%= cast_datetime(@datetime) %> | ||||
|     </time> | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   @spec cast_datetime(NaiveDateTime.t() | nil) :: String.t() | ||||
|   defp cast_datetime(%NaiveDateTime{} = datetime) do | ||||
|     datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended) | ||||
|   end | ||||
|  | ||||
|   defp cast_datetime(_datetime), do: "" | ||||
|  | ||||
|   @doc """ | ||||
|   Phoenix.Component for a <date> element that renders the Date in the user's | ||||
|   local timezone with Alpine.js | ||||
|   """ | ||||
|  | ||||
|   attr :date, :any, required: true, doc: "A `Date` struct or nil" | ||||
|  | ||||
|   def date(assigns) do | ||||
|     ~H""" | ||||
|     <time | ||||
|       :if={@date} | ||||
|       datetime={@date |> Date.to_iso8601(:extended)} | ||||
|       x-data={"{ | ||||
|         date: | ||||
|           Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}) | ||||
|             .format(new Date(\"#{@date |> Date.to_iso8601(:extended)}\")) | ||||
|       }"} | ||||
|       x-text="date" | ||||
|     > | ||||
|       <%= @date |> Date.to_iso8601(:extended) %> | ||||
|     </time> | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Displays emoji as text emoji if SHIBAO_MODE is set to true :) | ||||
|   """ | ||||
| @@ -87,23 +32,6 @@ defmodule CanneryWeb.ViewHelpers do | ||||
|     "data:image/png;base64," <> img_data | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Creates a downloadable QR Code element | ||||
|   """ | ||||
|  | ||||
|   attr :content, :string, required: true | ||||
|   attr :filename, :string, default: "qrcode", doc: "filename without .png extension" | ||||
|   attr :image_class, :string, default: "w-64 h-max" | ||||
|   attr :width, :integer, default: 384, doc: "width of png to generate" | ||||
|  | ||||
|   def qr_code(assigns) do | ||||
|     ~H""" | ||||
|     <a href={qr_code_image(@content)} download={@filename <> ".png"}> | ||||
|       <img class={@image_class} alt={@filename} src={qr_code_image(@content)} /> | ||||
|     </a> | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Get a random color in `#ffffff` hex format | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								mix.exs
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								mix.exs
									
									
									
									
									
								
							| @@ -47,13 +47,13 @@ defmodule Cannery.MixProject do | ||||
|   # Type `mix help deps` for examples and options. | ||||
|   defp deps do | ||||
|     [ | ||||
|       {:bcrypt_elixir, "~> 2.0"}, | ||||
|       {:bcrypt_elixir, "~> 3.0"}, | ||||
|       {:phoenix, "~> 1.6.0"}, | ||||
|       {:phoenix_ecto, "~> 4.4"}, | ||||
|       {:phoenix_html, "~> 3.0"}, | ||||
|       {:phoenix_live_reload, "~> 1.2", only: :dev}, | ||||
|       {:phoenix_live_view, "~> 0.18.0"}, | ||||
|       {:phoenix_view, "~> 1.1"}, | ||||
|       {:phoenix_view, "~> 2.0"}, | ||||
|       {:phoenix_live_dashboard, "~> 0.6"}, | ||||
|       {:ecto_sql, "~> 3.6"}, | ||||
|       {:postgrex, ">= 0.0.0"}, | ||||
|   | ||||
							
								
								
									
										33
									
								
								mix.lock
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								mix.lock
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| %{ | ||||
|   "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"}, | ||||
|   "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"}, | ||||
|   "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, | ||||
|   "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, | ||||
|   "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, | ||||
| @@ -11,38 +11,39 @@ | ||||
|   "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"}, | ||||
|   "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, | ||||
|   "dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"}, | ||||
|   "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, | ||||
|   "earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"}, | ||||
|   "ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"}, | ||||
|   "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"}, | ||||
|   "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"}, | ||||
|   "elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"}, | ||||
|   "elixir_make": {:hex, :elixir_make, "0.7.5", "784cc00f5fa24239067cc04d449437dcc5f59353c44eb08f188b2b146568738a", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "c3d63e8d5c92fa3880d89ecd41de59473fa2e83eeb68148155e25e8b95aa2887"}, | ||||
|   "eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"}, | ||||
|   "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, | ||||
|   "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"}, | ||||
|   "expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"}, | ||||
|   "ex_doc": {:hex, :ex_doc, "0.29.2", "dfa97532ba66910b2a3016a4bbd796f41a86fc71dd5227e96f4c8581fdf0fdf0", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "6b5d7139eda18a753e3250e27e4a929f8d2c880dd0d460cb9986305dea3e03af"}, | ||||
|   "expo": {:hex, :expo, "0.4.0", "bbe4bf455e2eb2ebd2f1e7d83530ce50fb9990eb88fc47855c515bfdf1c6626f", [:mix], [], "hexpm", "a8ed1683ec8b7c7fa53fd7a41b2c6935f539168a6bb0616d7fd6b58a36f3abf2"}, | ||||
|   "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, | ||||
|   "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"}, | ||||
|   "floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"}, | ||||
|   "gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"}, | ||||
|   "gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"}, | ||||
|   "gettext": {:hex, :gettext, "0.22.1", "e7942988383c3d9eed4bdc22fc63e712b655ae94a672a27e4900e3d4a2c43581", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "ad105b8dab668ee3f90c0d3d94ba75e9aead27a62495c101d94f2657a190ac5d"}, | ||||
|   "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, | ||||
|   "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, | ||||
|   "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, | ||||
|   "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, | ||||
|   "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, | ||||
|   "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, | ||||
|   "oban": {:hex, :oban, "2.13.6", "a0cb1bce3bd393770512231fb5a3695fa19fd3af10d7575bf73f837aee7abf43", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c1c5eb16f377b3cbbf2ea14be24d20e3d91285af9d1ac86260b7c2af5464887"}, | ||||
|   "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"}, | ||||
|   "oban": {:hex, :oban, "2.14.2", "ae925d9a33e110addaa59ff7ec1b2fd84270ac7eb00fbb4b4a179d74c407bba3", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "32bf30127c8c44ac42f05f229a50fadc2177b3e799c29499f5daf90d5e5b5d3c"}, | ||||
|   "phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"}, | ||||
|   "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, | ||||
|   "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, | ||||
|   "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.1", "b0bf8f3348dec4910907a2ad1453e642f6fe4d444376c1c9b26222d63c73cf97", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "b6c5d744bf4b40692b1b361d3608bdfd05aeab83e17c7bc217d730f007f31abf"}, | ||||
|   "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, | ||||
|   "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"}, | ||||
|   "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, | ||||
|   "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.2", "635cf07de947235deb030cd6b776c71a3b790ab04cebf526aa8c879fe17c7784", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da287a77327e996cc166e4c440c3ad5ab33ccdb151b91c793209b39ebbce5b75"}, | ||||
|   "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"}, | ||||
|   "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, | ||||
|   "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.1.0", "f8e4780705c9f254cc853f7a40e25f7198ba4d91102bcfad2226669b69766b35", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "aa82f10afd9a4b6080fdf3274dbb9432b25b210d42b4b6b55308f6e59cd87c3d"}, | ||||
|   "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, | ||||
|   "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"}, | ||||
|   "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, | ||||
|   "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"}, | ||||
|   "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"}, | ||||
|   "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"}, | ||||
|   "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"}, | ||||
|   "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, | ||||
|   "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"}, | ||||
|   "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"}, | ||||
|   "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, | ||||
|   "swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"}, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user