Update to Phoenix 1.7

This commit is contained in:
Chris McCord 2022-11-17 10:01:20 -05:00
parent 6b02cfc614
commit 7bbce31bb9
35 changed files with 273 additions and 890 deletions

View File

@ -1,6 +1,6 @@
import "phoenix_html"
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import {LiveSocket} from "./phoenix_live_view"
import topbar from "../vendor/topbar"
let nowSeconds = () => Math.round(Date.now() / 1000)
@ -187,7 +187,7 @@ Hooks.Ping = {
this.handleEvent("pong", () => {
let rtt = Date.now() - this.nowMs
this.el.innerText = `ping: ${rtt}ms`
this.timer = setTimeout(() => this.ping(rtt), 1000)
// this.timer = setTimeout(() => this.ping(rtt), 1000)
})
this.ping(null)
},

View File

@ -1 +0,0 @@
/Users/chris/oss/phoenix/assets/js/phoenix

View File

@ -17,32 +17,11 @@ config :live_beats, :files, admin_usernames: []
config :live_beats, LiveBeatsWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "55naB2xjgnsDeN+kKz7xoeqx3vIPcpCkAmg+CoVR/F7iZ5MQgNE6ykiNXoFa7wcC",
render_errors: [view: LiveBeatsWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: LiveBeats.PubSub,
live_view: [signing_salt: "OHBVr+w4"]
# Configures the mailer
#
# By default it uses the "Local" adapter which stores the emails
# locally. You can see the emails in your browser, at "/dev/mailbox".
#
# For production it's recommended to configure a different adapter
# at the `config/runtime.exs`.
config :live_beats, LiveBeats.Mailer, adapter: Swoosh.Adapters.Local
# Swoosh API client is needed for adapters other than SMTP.
config :swoosh, :api_client, false
# Configure esbuild (the version is required)
config :tailwind,
version: "3.0.10",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
live_view: [signing_salt: "OHBVr+w4"],
render_errors: [
formats: [html: LiveBeatsWeb.ErrorHTML, json: LiveBeatsWeb.ErrorJSON],
layout: false
]
config :esbuild,
@ -53,6 +32,18 @@ config :esbuild,
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]
# Configure tailwind (the version is required)
config :tailwind,
version: "3.1.8",
default: [
args: ~w(
--config=tailwind.config.js
--input=css/app.css
--output=../priv/static/assets/app.css
),
cd: Path.expand("../assets", __DIR__)
]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",

View File

@ -102,3 +102,6 @@ config :phoenix, :stacktrace_depth, 20
# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
# Disable swoosh api client as it is only required for production adapters.
config :swoosh, :api_client, false

View File

@ -16,6 +16,8 @@ config :live_beats, LiveBeatsWeb.Endpoint,
# Do not print debug messages in production
config :logger, level: :info
config :swoosh, :api_client, LiveBeats.Finch
# ## SSL Support
#
# To get SSL working, you will need to add the `https` key

View File

@ -36,11 +36,11 @@ config :live_beats, LiveBeatsWeb.Endpoint,
http: [ip: {127, 0, 0, 1}, port: 4002],
server: false
# In test we don't send emails.
config :live_beats, LiveBeats.Mailer, adapter: Swoosh.Adapters.Test
# Print only warnings and errors during test
config :logger, level: :warn
# Initialize plugs at runtime for faster test compilation
config :phoenix, :plug_init_mode, :runtime
# Disable swoosh api client as it is only required for production adapters.
config :swoosh, :api_client, false

View File

@ -22,6 +22,7 @@ defmodule LiveBeats.Application do
{Phoenix.PubSub, name: LiveBeats.PubSub},
# start presence
LiveBeatsWeb.Presence,
{Finch, name: LiveBeats.Finch},
# Start the Endpoint (http/https)
LiveBeatsWeb.Endpoint,
# Expire songs every six hours

View File

@ -5,7 +5,7 @@ defmodule LiveBeats.MP3Stat do
MP3 decoding and duration calculation credit to:
https://shadowfacts.net/2021/mp3-duration/
"""
use Bitwise
import Bitwise
alias LiveBeats.MP3Stat
defstruct duration: 0, size: 0, path: nil, title: nil, artist: nil, tags: nil

View File

@ -6,7 +6,7 @@ defmodule LiveBeatsWeb do
This can be used in your application as:
use LiveBeatsWeb, :controller
use LiveBeatsWeb, :view
use LiveBeatsWeb, :html
The definitions below will be executed for every view,
controller, etc, so keep them short and clean, focused
@ -17,9 +17,14 @@ defmodule LiveBeatsWeb do
and import those modules here.
"""
def static_paths, do: ~w(assets fonts images favicon.ico robots.txt)
def controller do
quote do
use Phoenix.Controller, namespace: LiveBeatsWeb
use Phoenix.Controller,
namespace: LiveBeatsWeb,
formats: [:html, :json],
layouts: [html: LiveBeatsWeb.Layouts]
import Plug.Conn
import LiveBeatsWeb.Gettext
@ -27,18 +32,25 @@ defmodule LiveBeatsWeb do
end
end
def view do
def html do
quote do
use Phoenix.View,
root: "lib/live_beats_web/templates",
namespace: LiveBeatsWeb
use Phoenix.Component
# Import convenience functions from controllers
import Phoenix.Controller,
only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
only: [get_csrf_token: 0, view_module: 1, view_template: 1]
# Include shared imports and aliases for views
unquote(view_helpers())
# Include general helpers for rendering HTML
unquote(html_helpers())
end
end
def verified_routes do
quote do
use Phoenix.VerifiedRoutes,
endpoint: LiveBeatsWeb.Endpoint,
router: LiveBeatsWeb.Router,
statics: LiveBeatsWeb.static_paths()
end
end
@ -46,14 +58,14 @@ defmodule LiveBeatsWeb do
quote do
@opts Keyword.merge(
[
layout: {LiveBeatsWeb.LayoutView, "live.html"},
layout: {LiveBeatsWeb.Layouts, :live},
container: {:div, class: "relative h-screen flex overflow-hidden bg-white"}
],
unquote(opts)
)
use Phoenix.LiveView, @opts
unquote(view_helpers())
unquote(html_helpers())
end
end
@ -61,7 +73,7 @@ defmodule LiveBeatsWeb do
quote do
use Phoenix.LiveComponent
unquote(view_helpers())
unquote(html_helpers())
end
end
@ -82,19 +94,15 @@ defmodule LiveBeatsWeb do
end
end
defp view_helpers do
defp html_helpers do
quote do
# Use all HTML functionality (forms, tags, etc)
use Phoenix.HTML
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
import Phoenix.LiveView.Helpers
use Phoenix.Component
# Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View
import LiveBeatsWeb.ErrorHelpers
import LiveBeatsWeb.LiveHelpers
import LiveBeatsWeb.CoreComponents
import LiveBeatsWeb.Gettext
alias LiveBeatsWeb.Router.Helpers, as: Routes
alias Phoenix.LiveView.JS

View File

@ -12,8 +12,7 @@ defmodule LiveBeatsWeb.Presence do
@pubsub LiveBeats.PubSub
import Phoenix.LiveView.Helpers
import LiveBeatsWeb.LiveHelpers
use LiveBeatsWeb, :html
alias LiveBeats.{Accounts, MediaLibrary}
alias LiveBeatsWeb.Presence.BadgeComponent
@ -81,7 +80,6 @@ defmodule LiveBeatsWeb.Presence do
end
def listening_now(assigns) do
import Phoenix.LiveView
count = Enum.count(assigns.presence_ids)
assigns =

View File

@ -1,4 +1,4 @@
defmodule LiveBeatsWeb.LiveHelpers do
defmodule LiveBeatsWeb.CoreComponents do
use Phoenix.Component
alias LiveBeatsWeb.Router.Helpers, as: Routes
@ -24,6 +24,8 @@ defmodule LiveBeatsWeb.LiveHelpers do
profile_path(profile.username, action)
end
slot :inner_block
def connection_status(assigns) do
~H"""
<div
@ -62,62 +64,60 @@ defmodule LiveBeatsWeb.LiveHelpers do
end
attr :flash, :map
attr :kiny, :atom
attr :kind, :atom
def flash(%{kind: :error} = assigns) do
~H"""
<%= if live_flash(@flash, @kind) do %>
<div
id="flash"
class="rounded-md bg-red-50 p-4 fixed top-1 right-1 w-96 fade-in-scale z-50"
phx-click={
JS.push("lv:clear-flash")
|> JS.remove_class("fade-in-scale", to: "#flash")
|> hide("#flash")
}
phx-hook="Flash"
>
<div class="flex justify-between items-center space-x-3 text-red-700">
<.icon name={:exclamation_circle} class="w-5 w-5" />
<p class="flex-1 text-sm font-medium" role="alert">
<%= live_flash(@flash, @kind) %>
</p>
<button
type="button"
class="inline-flex bg-red-50 rounded-md p-1.5 text-red-500 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-red-50 focus:ring-red-600"
>
<.icon name={:x} class="w-4 h-4" />
</button>
</div>
<div
:if={msg = Phoenix.Flash.get(@flash, @kind)}
id="flash"
class="rounded-md bg-red-50 p-4 fixed top-1 right-1 w-96 fade-in-scale z-50"
phx-click={
JS.push("lv:clear-flash")
|> JS.remove_class("fade-in-scale", to: "#flash")
|> hide("#flash")
}
phx-hook="Flash"
>
<div class="flex justify-between items-center space-x-3 text-red-700">
<.icon name={:exclamation_circle} class="w-5 w-5" />
<p class="flex-1 text-sm font-medium" role="alert">
<%= msg %>
</p>
<button
type="button"
class="inline-flex bg-red-50 rounded-md p-1.5 text-red-500 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-red-50 focus:ring-red-600"
>
<.icon name={:x} class="w-4 h-4" />
</button>
</div>
<% end %>
</div>
"""
end
def flash(%{kind: :info} = assigns) do
~H"""
<%= if live_flash(@flash, @kind) do %>
<div
id="flash"
class="rounded-md bg-green-50 p-4 fixed top-1 right-1 w-96 fade-in-scale z-50"
phx-click={JS.push("lv:clear-flash") |> JS.remove_class("fade-in-scale") |> hide("#flash")}
phx-value-key="info"
phx-hook="Flash"
>
<div class="flex justify-between items-center space-x-3 text-green-700">
<.icon name={:check_circle} class="w-5 h-5" />
<p class="flex-1 text-sm font-medium" role="alert">
<%= live_flash(@flash, @kind) %>
</p>
<button
type="button"
class="inline-flex bg-green-50 rounded-md p-1.5 text-green-500 hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-green-50 focus:ring-green-600"
>
<.icon name={:x} class="w-4 h-4" />
</button>
</div>
<div
:if={msg = Phoenix.Flash.get(@flash, @kind)}
id="flash"
class="rounded-md bg-green-50 p-4 fixed top-1 right-1 w-96 fade-in-scale z-50"
phx-click={JS.push("lv:clear-flash") |> JS.remove_class("fade-in-scale") |> hide("#flash")}
phx-value-key="info"
phx-hook="Flash"
>
<div class="flex justify-between items-center space-x-3 text-green-700">
<.icon name={:check_circle} class="w-5 h-5" />
<p class="flex-1 text-sm font-medium" role="alert">
<%= msg %>
</p>
<button
type="button"
class="inline-flex bg-green-50 rounded-md p-1.5 text-green-500 hover:bg-green-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-green-50 focus:ring-green-600"
>
<.icon name={:x} class="w-4 h-4" />
</button>
</div>
<% end %>
</div>
"""
end
@ -180,11 +180,18 @@ defmodule LiveBeatsWeb.LiveHelpers do
</.dropdown>
"""
attr :id, :string, required: true
attr :ok, :string, required: true
attr :img, :list, default: []
attr :title, :list, default: []
attr :subtitle, :list, default: []
attr :link, :list, default: []
slot :img do
attr :src, :string
end
slot :title
slot :subtitle
slot :link do
attr :navigate, :string
attr :href, :string
attr :method, :any
end
def dropdown(assigns) do
~H"""
@ -364,12 +371,12 @@ defmodule LiveBeatsWeb.LiveHelpers do
attr :navigate, :string, default: nil
attr :on_cancel, JS, default: %JS{}
attr :on_confirm, JS, default: %JS{}
# slots
attr :title, :list, default: []
attr :confirm, :list, default: []
attr :cancel, :list, default: []
attr :rest, :global
slot :title
slot :confirm
slot :cancel
def modal(assigns) do
~H"""
<div
@ -377,7 +384,7 @@ defmodule LiveBeatsWeb.LiveHelpers do
class={"fixed z-10 inset-0 overflow-y-auto #{if @show, do: "fade-in", else: "hidden"}"}
{@rest}
>
<.focus_wrap id={"#{@id}-focus-wrap"} content={"##{@id}-container"}>
<.focus_wrap id={"#{@id}-focus-wrap"}>
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
aria-labelledby={"#{@id}-title"}
@ -477,7 +484,7 @@ defmodule LiveBeatsWeb.LiveHelpers do
"""
end
attr :actions, :list, default: []
slot :actions
def title_bar(assigns) do
~H"""
@ -501,6 +508,8 @@ defmodule LiveBeatsWeb.LiveHelpers do
attr :primary, :boolean, default: false
attr :rest, :global
slot :inner_block
def button(%{patch: _} = assigns) do
~H"""
<%= if @primary do %>
@ -541,8 +550,8 @@ defmodule LiveBeatsWeb.LiveHelpers do
attr :row_id, :any, default: false
attr :rows, :list, required: true
# slots
attr :col, :list, required: true
slot :col, required: true
def table(assigns) do
assigns =
@ -590,8 +599,11 @@ defmodule LiveBeatsWeb.LiveHelpers do
attr :rows, :list, required: true
attr :owns_profile?, :boolean, default: false
attr :active_id, :any, default: nil
# slots
attr :col, :list
slot :col do
attr :label, :string
attr :class, :string
end
def live_table(assigns) do
assigns = assign(assigns, :col, for(col <- assigns.col, col[:if] != false, do: col))
@ -648,4 +660,72 @@ defmodule LiveBeatsWeb.LiveHelpers do
|> JS.dispatch("js:focus-closest", to: to)
|> hide(to)
end
@doc """
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
error(%{
errors: form.errors,
field: field,
input_name: Phoenix.HTML.Form.input_name(form, field)
})
end
def error(%{errors: errors, field: field} = assigns) do
assigns =
assigns
|> assign(:error_values, Keyword.get_values(errors, field))
|> assign_new(:class, fn -> "" end)
~H"""
<%= for error <- @error_values do %>
<span
phx-feedback-for={@input_name}
class={
"invalid-feedback inline-block pl-2 pr-2 text-sm text-white bg-red-600 rounded-md #{@class}"
}
>
<%= translate_error(error) %>
</span>
<% end %>
<%= if Enum.empty?(@error_values) do %>
<span class={"invalid-feedback inline-block h-0 #{@class}"}></span>
<% end %>
"""
end
@doc """
Translates an error message using gettext.
"""
def translate_error({msg, opts}) do
# When using gettext, we typically pass the strings we want
# to translate as a static argument:
#
# # Translate "is invalid" in the "errors" domain
# dgettext("errors", "is invalid")
#
# # Translate the number of files with plural rules
# dngettext("errors", "1 file", "%{count} files", count)
#
# Because the error messages we show in our forms and APIs
# are defined inside Ecto, we need to translate them dynamically.
# This requires us to call the Gettext module passing our gettext
# backend as first argument.
#
# Note we use the "errors" domain, which means translations
# should be written to the errors.po file. The :count option is
# set by Ecto and indicates we should also apply plural rules.
if count = opts[:count] do
Gettext.dngettext(LiveBeatsWeb.Gettext, "errors", msg, msg, count, opts)
else
Gettext.dgettext(LiveBeatsWeb.Gettext, "errors", msg, opts)
end
end
def translate_changeset_errors(changeset) do
changeset.errors
|> Enum.map(fn {key, value} -> "#{key} #{translate_error(value)}" end)
|> Enum.join("\n")
end
end

View File

@ -1,13 +1,13 @@
defmodule LiveBeatsWeb.LayoutView do
use LiveBeatsWeb, :view
use Phoenix.Component
# Phoenix LiveDashboard is available only in development by default,
# so we instruct Elixir to not warn if the dashboard route is missing.
@compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
defmodule LiveBeatsWeb.Layouts do
use LiveBeatsWeb, :html
alias LiveBeatsWeb.Endpoint
embed_templates "layouts/*"
attr :id, :string
attr :users, :list
def sidebar_active_users(assigns) do
~H"""
<div class="mt-8">
@ -31,6 +31,10 @@ defmodule LiveBeatsWeb.LayoutView do
"""
end
attr :id, :string
attr :current_user, :any
attr :active_tab, :atom
def sidebar_nav_links(assigns) do
~H"""
<div class="space-y-1">
@ -90,9 +94,11 @@ defmodule LiveBeatsWeb.LayoutView do
"""
end
attr :id, :string
attr :current_user, :any
def sidebar_account_dropdown(assigns) do
~H"""
<.dropdown id={@id} foo="bar">
<.dropdown id={@id}>
<:img src={@current_user.avatar_url} />
<:title><%= @current_user.name %></:title>
<:subtitle>@<%= @current_user.username %></:subtitle>

View File

@ -0,0 +1,5 @@
<main>
<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
<%= @inner_content %>
</main>

View File

@ -0,0 +1,7 @@
defmodule LiveBeatsWeb.ErrorHTML do
use LiveBeatsWeb, :html
def template_not_found(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
end

View File

@ -1,7 +0,0 @@
defmodule LiveBeatsWeb.PageController do
use LiveBeatsWeb, :controller
def index(conn, _params) do
render(conn, "index.html")
end
end

View File

@ -9,10 +9,10 @@ defmodule LiveBeatsWeb.UserAuth do
def on_mount(:current_user, _params, session, socket) do
case session do
%{"user_id" => user_id} ->
{:cont, LiveView.assign_new(socket, :current_user, fn -> Accounts.get_user(user_id) end)}
{:cont, Phoenix.Component.assign_new(socket, :current_user, fn -> Accounts.get_user(user_id) end)}
%{} ->
{:cont, LiveView.assign(socket, :current_user, nil)}
{:cont, Phoenix.Component.assign(socket, :current_user, nil)}
end
end
@ -20,7 +20,7 @@ defmodule LiveBeatsWeb.UserAuth do
case session do
%{"user_id" => user_id} ->
new_socket =
LiveView.assign_new(socket, :current_user, fn -> Accounts.get_user!(user_id) end)
Phoenix.Component.assign_new(socket, :current_user, fn -> Accounts.get_user!(user_id) end)
%Accounts.User{} = new_socket.assigns.current_user
{:cont, new_socket}
@ -145,6 +145,6 @@ defmodule LiveBeatsWeb.UserAuth do
defp maybe_store_return_to(conn), do: conn
def signed_in_path(conn) do
LiveBeatsWeb.LiveHelpers.profile_path(conn.assigns.current_user)
LiveBeatsWeb.CoreComponents.profile_path(conn.assigns.current_user)
end
end

View File

@ -20,7 +20,7 @@ defmodule LiveBeatsWeb.Endpoint do
at: "/",
from: :live_beats,
gzip: false,
only: ~w(assets fonts images favicon.ico robots.txt uploads)
only: LiveBeatsWeb.static_paths()
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.

View File

@ -1,5 +1,6 @@
defmodule LiveBeatsWeb.Nav do
import Phoenix.LiveView
use Phoenix.Component
alias LiveBeats.{Accounts, MediaLibrary}
alias LiveBeatsWeb.{ProfileLive, SettingsLive}

View File

@ -185,6 +185,7 @@ defmodule LiveBeatsWeb.PlayerLive do
socket =
socket
|> assign(
foo: true,
song: nil,
playing: false,
profile: nil,

View File

@ -95,7 +95,7 @@ defmodule LiveBeatsWeb.ProfileLive do
<%= song.attribution %>
</:col>
<:col :let={%{song: song}} label="Duration"><%= MP3Stat.to_mmss(song.duration) %></:col>
<:col :let={%{song: song}} label="" if={@owns_profile?}>
<:col :let={%{song: song}} label="" :if={@owns_profile?}>
<.link
id={"delete-song-#{song.id}"}
phx-click={show_modal("delete-modal-#{song.id}")}
@ -109,6 +109,7 @@ defmodule LiveBeatsWeb.ProfileLive do
end
def mount(%{"profile_username" => profile_username}, _session, socket) do
IO.inspect(get_connect_params(socket))
%{current_user: current_user} = socket.assigns
profile =

View File

@ -169,10 +169,10 @@ defmodule LiveBeatsWeb.ProfileLive.UploadFormComponent do
do: ~H|Something went wrong|
defp file_error(%{kind: %Ecto.Changeset{}} = assigns),
do: ~H|<%= @label %>: <%= LiveBeatsWeb.ErrorHelpers.translate_changeset_errors(@kind) %>|
do: ~H|<%= @label %>: translate_changeset_errors(@kind) %>|
defp file_error(%{kind: {msg, opts}} = assigns) when is_binary(msg) and is_list(opts),
do: ~H|<%= @label %>: <%= LiveBeatsWeb.ErrorHelpers.translate_error(@kind) %>|
do: ~H|<%= @label %>: translate_error(@kind) %>|
defp put_stats(socket, entry_ref, %MP3Stat{} = stat) do
if changeset = get_changeset(socket, entry_ref) do

View File

@ -64,7 +64,7 @@
<span phx-click={js_exec("##{@uploads.mp3.ref}", "click", [])}>
Upload files
</span>
<%= live_file_input(@uploads.mp3, class: "sr-only", tabindex: "0") %>
<.live_file_input upload={@uploads.mp3} class="sr-only" tabindex="0" />
</label>
<p class="pl-1">or drag and drop</p>
</div>

View File

@ -98,6 +98,7 @@ defmodule LiveBeatsWeb.SettingsLive do
end
def mount(_params, _session, socket) do
IO.inspect(get_connect_params(socket))
changeset = Accounts.change_settings(socket.assigns.current_user, %{})
{:ok, assign(socket, changeset: changeset)}
end

View File

@ -8,7 +8,7 @@ defmodule LiveBeatsWeb.Router do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, {LiveBeatsWeb.LayoutView, :root}
plug :put_root_layout, {LiveBeatsWeb.Layouts, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end

View File

@ -1,5 +0,0 @@
<main>
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= @inner_content %>
</main>

View File

@ -1,615 +0,0 @@
<!-- Pinned projects -->
<div class="px-4 mt-6 sm:px-6 lg:px-8">
<h2 class="text-gray-500 text-xs font-medium uppercase tracking-wide">Who's Here</h2>
<ul
role="list"
class="grid grid-cols-1 gap-4 sm:gap-6 sm:grid-cols-2 xl:grid-cols-4 mt-3"
x-max="1"
>
<li class="relative col-span-1 flex shadow-sm rounded-md">
<div class="flex-shrink-0 flex items-center justify-center w-16 bg-pink-600 text-white text-sm font-medium rounded-l-md">
CM
</div>
<div class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-md truncate">
<div class="flex-1 px-4 py-2 text-sm truncate">
<a href="#" class="text-gray-900 font-medium hover:text-gray-600">
Chris
</a>
<p class="text-gray-500">5 songs</p>
</div>
</div>
</li>
<li class="relative col-span-1 flex shadow-sm rounded-md">
<div class="flex-shrink-0 flex items-center justify-center w-16 bg-purple-600 text-white text-sm font-medium rounded-l-md">
KM
</div>
<div class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-md truncate">
<div class="flex-1 px-4 py-2 text-sm truncate">
<a href="#" class="text-gray-900 font-medium hover:text-gray-600">
Kurt
</a>
<p class="text-gray-500">1 song</p>
</div>
</div>
</li>
<li class="relative col-span-1 flex shadow-sm rounded-md">
<div class="flex-shrink-0 flex items-center justify-center w-16 bg-green-600 text-white text-sm font-medium rounded-l-md">
JV
</div>
<div class="flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-md truncate">
<div class="flex-1 px-4 py-2 text-sm truncate">
<a href="#" class="text-gray-900 font-medium hover:text-gray-600">
José
</a>
<p class="text-gray-500">1 song</p>
</div>
</div>
</li>
</ul>
</div>
<!-- Projects list (only on smallest breakpoint) -->
<div class="mt-10 sm:hidden">
<div class="px-4 sm:px-6">
<h2 class="text-gray-500 text-xs font-medium uppercase tracking-wide">Projects</h2>
</div>
<ul role="list" class="mt-3 border-t border-gray-200 divide-y divide-gray-100">
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-pink-600" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
GraphQL API
<!-- space -->
<span class="truncate font-normal text-gray-500">in Engineering</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-purple-600" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
New Benefits Plan
<!-- space -->
<span class="truncate font-normal text-gray-500">in Human Resources</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-yellow-500" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Onboarding Emails
<!-- space -->
<span class="truncate font-normal text-gray-500">in Customer Success</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-green-500" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
iOS App
<!-- space -->
<span class="truncate font-normal text-gray-500">in Engineering</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-pink-600" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Marketing Site Redesign
<!-- space -->
<span class="truncate font-normal text-gray-500">in Engineering</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-purple-600" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Hire CFO
<!-- space -->
<span class="truncate font-normal text-gray-500">in Human Resources</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-yellow-500" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Android App
<!-- space -->
<span class="truncate font-normal text-gray-500">in Engineering</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-green-500" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
New Customer Portal
<!-- space -->
<span class="truncate font-normal text-gray-500">in Engineering</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-pink-600" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Co-op Program
<!-- space -->
<span class="truncate font-normal text-gray-500">in Human Resources</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-purple-600" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Implement NPS
<!-- space -->
<span class="truncate font-normal text-gray-500">in Customer Success</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-yellow-500" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Employee Recognition Program
<!-- space -->
<span class="truncate font-normal text-gray-500">in Human Resources</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
<li>
<a
href="#"
class="group flex items-center justify-between px-4 py-4 hover:bg-gray-50 sm:px-6"
>
<span class="flex items-center truncate space-x-3">
<span class="w-2.5 h-2.5 flex-shrink-0 rounded-full bg-green-500" aria-hidden="true">
</span>
<span class="font-medium truncate text-sm leading-6">
Open Source Web Client
<!-- space -->
<span class="truncate font-normal text-gray-500">in Engineering</span>
</span>
</span>
<svg
class="ml-4 h-5 w-5 text-gray-400 group-hover:text-gray-500"
x-description="Heroicon name: solid/chevron-right"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
>
</path>
</svg>
</a>
</li>
</ul>
</div>
<!-- Songs table (small breakpoint and up) -->
<div class="hidden mt-8 sm:block">
<div class="align-middle inline-block min-w-full border-b border-gray-200">
<table class="min-w-full">
<thead>
<tr class="border-t border-gray-200">
<th class="px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<span class="lg:pl-2">Nextup</span>
</th>
<th class="px-6 py-3 border-b border-gray-200 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
likes
</th>
<th class="hidden md:table-cell px-6 py-3 border-b border-gray-200 bg-gray-50 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
user
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
<%= for _ <- 1..20 do %>
<tr>
<td class="px-6 py-3 max-w-0 w-full whitespace-nowrap text-sm font-medium text-gray-900">
<div class="flex items-center space-x-3 lg:pl-2">
<div class="flex-shrink-0 w-2.5 h-2.5 rounded-full bg-pink-600" aria-hidden="true">
</div>
<a href="#" class="truncate hover:text-gray-600">
<span>
GraphQL API
<!-- space -->
<span class="text-gray-500 font-normal">in Engineering</span>
</span>
</a>
</div>
</td>
<td class="px-6 py-3 text-sm text-gray-500 font-medium">
<div class="flex items-center space-x-2">
<div class="flex flex-shrink-0 -space-x-1">
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Dries Vincent"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Lindsay Walton"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Courtney Henry"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Tom Cook"
/>
</div>
<span class="flex-shrink-0 text-xs leading-5 font-medium">+8</span>
</div>
</td>
<td class="hidden md:table-cell px-6 py-3 whitespace-nowrap text-sm text-gray-500 text-right">
March 17, 2020
</td>
</tr>
<% end %>
<tr>
<td class="px-6 py-3 max-w-0 w-full whitespace-nowrap text-sm font-medium text-gray-900">
<div class="flex items-center space-x-3 lg:pl-2">
<div class="flex-shrink-0 w-2.5 h-2.5 rounded-full bg-purple-600" aria-hidden="true">
</div>
<a href="#" class="truncate hover:text-gray-600">
<span>
New Benefits Plan
<!-- space -->
<span class="text-gray-500 font-normal">in Human Resources</span>
</span>
</a>
</div>
</td>
<td class="px-6 py-3 text-sm text-gray-500 font-medium">
<div class="flex items-center space-x-2">
<div class="flex flex-shrink-0 -space-x-1">
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Leonard Krasner"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1463453091185-61582044d556?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Floyd Miles"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Emily Selman"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1500917293891-ef795e70e1f6?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Kristin Watson"
/>
</div>
<span class="flex-shrink-0 text-xs leading-5 font-medium">+4</span>
</div>
</td>
<td class="hidden md:table-cell px-6 py-3 whitespace-nowrap text-sm text-gray-500 text-right">
April 4, 2020
</td>
</tr>
<tr>
<td class="px-6 py-3 max-w-0 w-full whitespace-nowrap text-sm font-medium text-gray-900">
<div class="flex items-center space-x-3 lg:pl-2">
<div class="flex-shrink-0 w-2.5 h-2.5 rounded-full bg-yellow-500" aria-hidden="true">
</div>
<a href="#" class="truncate hover:text-gray-600">
<span>
Onboarding Emails
<!-- space -->
<span class="text-gray-500 font-normal">in Customer Success</span>
</span>
</a>
</div>
</td>
<td class="px-6 py-3 text-sm text-gray-500 font-medium">
<div class="flex items-center space-x-2">
<div class="flex flex-shrink-0 -space-x-1">
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Emily Selman"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1500917293891-ef795e70e1f6?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Kristin Watson"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1505840717430-882ce147ef2d?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Emma Dorsey"
/>
<img
class="max-w-none h-6 w-6 rounded-full ring-2 ring-white"
src="https://images.unsplash.com/photo-1509783236416-c9ad59bae472?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjEyMDd9&amp;auto=format&amp;fit=facearea&amp;facepad=2&amp;w=256&amp;h=256&amp;q=80"
alt="Alicia Bell"
/>
</div>
<span class="flex-shrink-0 text-xs leading-5 font-medium">+10</span>
</div>
</td>
<td class="hidden md:table-cell px-6 py-3 whitespace-nowrap text-sm text-gray-500 text-right">
March 30, 2020
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -1,74 +0,0 @@
defmodule LiveBeatsWeb.ErrorHelpers do
@moduledoc """
Conveniences for translating and building error messages.
"""
use Phoenix.HTML
import Phoenix.LiveView
import Phoenix.LiveView.Helpers
@doc """
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
error(%{errors: form.errors, field: field, input_name: input_name(form, field)})
end
def error(%{errors: errors, field: field} = assigns) do
assigns =
assigns
|> assign(:error_values, Keyword.get_values(errors, field))
|> assign_new(:class, fn -> "" end)
~H"""
<%= for error <- @error_values do %>
<span
phx-feedback-for={@input_name}
class={
"invalid-feedback inline-block pl-2 pr-2 text-sm text-white bg-red-600 rounded-md #{@class}"
}
>
<%= translate_error(error) %>
</span>
<% end %>
<%= if Enum.empty?(@error_values) do %>
<span class={"invalid-feedback inline-block h-0 #{@class}"}></span>
<% end %>
"""
end
@doc """
Translates an error message using gettext.
"""
def translate_error({msg, opts}) do
# When using gettext, we typically pass the strings we want
# to translate as a static argument:
#
# # Translate "is invalid" in the "errors" domain
# dgettext("errors", "is invalid")
#
# # Translate the number of files with plural rules
# dngettext("errors", "1 file", "%{count} files", count)
#
# Because the error messages we show in our forms and APIs
# are defined inside Ecto, we need to translate them dynamically.
# This requires us to call the Gettext module passing our gettext
# backend as first argument.
#
# Note we use the "errors" domain, which means translations
# should be written to the errors.po file. The :count option is
# set by Ecto and indicates we should also apply plural rules.
if count = opts[:count] do
Gettext.dngettext(LiveBeatsWeb.Gettext, "errors", msg, msg, count, opts)
else
Gettext.dgettext(LiveBeatsWeb.Gettext, "errors", msg, opts)
end
end
def translate_changeset_errors(changeset) do
changeset.errors
|> Enum.map(fn {key, value} -> "#{key} #{translate_error(value)}" end)
|> Enum.join("\n")
end
end

View File

@ -1,16 +0,0 @@
defmodule LiveBeatsWeb.ErrorView do
use LiveBeatsWeb, :view
# If you want to customize a particular status code
# for a certain format, you may uncomment below.
# def render("500.html", _assigns) do
# "Internal Server Error"
# end
# By default, Phoenix returns the status message from
# the template name. For example, "404.html" becomes
# "Not Found".
def template_not_found(template, _assigns) do
Phoenix.Controller.status_message_from_template(template)
end
end

View File

@ -1,3 +0,0 @@
defmodule LiveBeatsWeb.PageView do
use LiveBeatsWeb, :view
end

View File

@ -7,7 +7,6 @@ defmodule LiveBeats.MixProject do
version: "0.1.0",
elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:gettext, :phoenix_live_view] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps()
@ -33,18 +32,19 @@ defmodule LiveBeats.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
{:phoenix, github: "phoenixframework/phoenix", override: true},
{:phoenix, "~> 1.7.0-rc.0", override: true},
{:phoenix_live_view, "~> 0.18.3"},
{:phoenix_live_dashboard, "~> 0.7.2"},
{:phoenix_ecto, "~> 4.4"},
{:ecto_sql, "~> 3.6"},
{:ecto_network, "~> 1.3.0"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view", override: true},
{:floki, ">= 0.30.0", only: :test},
{:phoenix_live_dashboard, "~> 0.6"},
{:esbuild, "~> 0.2", runtime: Mix.env() == :dev},
{:swoosh, "~> 1.3"},
{:finch, "~> 0.13"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.18"},

View File

@ -1,46 +1,45 @@
%{
"castore": {:hex, :castore, "0.1.13", "ccf3ab251ffaebc4319f41d788ce59a6ab3f42b6c27e598ad838ffecee0b04f9", [:mix], [], "hexpm", "a14a7eecfec7e20385493dbb92b0d12c5d77ecfd6307de10102d58c94e8c49c0"},
"castore": {:hex, :castore, "0.1.19", "a2c3e46d62b7f3aa2e6f88541c21d7400381e53704394462b9fd4f06f6d42bb6", [:mix], [], "hexpm", "e96e0161a5dc82ef441da24d5fa74aefc40d920f3a6645d15e1f9f3e66bb2109"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"db_connection": {:hex, :db_connection, "2.4.0", "d04b1b73795dae60cead94189f1b8a51cc9e1f911c234cc23074017c43c031e5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad416c21ad9f61b3103d254a71b63696ecadb6a917b36f563921e0de00d7d7c8"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"ecto": {:hex, :ecto, "3.7.1", "a20598862351b29f80f285b21ec5297da1181c0442687f9b8329f0445d228892", [: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", "d36e5b39fc479e654cffd4dbe1865d9716e4a9b6311faff799b6f90ab81b8638"},
"ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [: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", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"ecto_network": {:hex, :ecto_network, "1.3.0", "1e77fa37c20e0f6a426d3862732f3317b0fa4c18f123d325f81752a491d7304e", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "053a5e46ef2837e8ea5ea97c82fa0f5494699209eddd764e663c85f11b2865bd"},
"ecto_sql": {:hex, :ecto_sql, "3.7.0", "2fcaad4ab0c8d76a5afbef078162806adbe709c04160aca58400d5cbbe8eeac6", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a26135dfa1d99bf87a928c464cfa25bba6535a4fe761eefa56077a4febc60f70"},
"erlog": {:git, "git://github.com/segun/erlog.git", "76825e0500b6b62b99f28411933dc15a1cb2817f", [ref: "master"]},
"erlp3tags": {:git, "https://github.com/segun/erlp3tags.git", "9b6c72da9057b9e00c7711148ce10e6cdbacae18", []},
"esbuild": {:hex, :esbuild, "0.3.4", "416203c642eb84b207f882cf7953a1fd7bb71e23f5f86554f983bb7bad18b897", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "c472e38b37e9547113776b1e4b64b44ec540bcc7056dd252c2c3ffba41aa9793"},
"ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [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", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"floki": {:hex, :floki, "0.31.0", "f05ee8a8e6a3ced4e62beeb2c79a63bc8e12ab98fbaaf6e6a3d9b76b1278e23f", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "b05afa372f5c345a5bf240ac25ea1f0f3d5fcfd7490ac0beeb4a203f9444891e"},
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
"finch": {:hex, :finch, "0.13.0", "c881e5460ec563bf02d4f4584079e62201db676ed4c0ef3e59189331c4eddf7b", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49957dcde10dcdc042a123a507a9c5ec5a803f53646d451db2f7dea696fba6cc"},
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
"gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
"heroicons": {:hex, :heroicons, "0.2.4", "12824795f25340415bffa9a501b96c89dc856ed732e82acccdab635fee92411b", [:mix], [{:phoenix_html, "~> 2.14 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "7d6c51dc8ecaadb37943247da83ec2fbf3f6b479a23f4cf0195bd61ec2268128"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"id3": {:hex, :id3, "1.0.1", "344840dee445c19be4b6dca9c70f0a7cd5dd03cb4260bae8ad9a21457ef12813", [:mix], [{:rustler, "~> 0.21.0", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "07ee2a284414065920751a528f237e77dc2aeed222a75302f909f3c07fd867e1"},
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"libcluster": {:hex, :libcluster, "3.3.1", "e7a4875cd1290cee7a693d6bd46076863e9e433708b01339783de6eff5b7f0aa", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b575ca63c1cd84e01f3fa0fc45e6eb945c1ee7ae8d441d33def999075e9e5398"},
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
"mint": {:hex, :mint, "1.3.0", "396b3301102f7b775e103da5a20494b25753aed818d6d6f0ad222a3a018c3600", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "a9aac960562e43ca69a77e5176576abfa78b8398cec5543dd4fb4ab0131d5c1e"},
"phoenix": {:git, "https://github.com/phoenixframework/phoenix.git", "063bb8c37b05a61b6f411ef4b678efe0cd4e3841", []},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"},
"nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"phoenix": {:hex, :phoenix, "1.7.0-rc.0", "8e328572f496b5170e879da94baa57c5f878f354d50eac052c9a7c6d57c2cf54", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, 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]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [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]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "ed503f6c55184afc0a453e44e6ab2a09f014f59b7fdd682313fdc52ec2f82859"},
"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.6.2", "0769470265eb13af01b5001b29cb935f4710d6adaa1ffc18417a570a337a2f0f", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [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.17.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5bc6c6b38a2ca8b5020b442322fcee6afd5e641637a0b1fb059d4bd89bc58e7b"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [: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", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
"phoenix_live_view": {:git, "https://github.com/phoenixframework/phoenix_live_view.git", "aab28ef96bf79a4218c1402a4aef6c3613380e1a", []},
"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.0", "4fe222c0be55fdc3f9c711e24955fc42a7cd9b7a2f5f406f2580a567c335a573", [: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", "bebf0fc2d2113b61cb5968f585367234b7b4c21d963d691de7b4b2dc6cdaae6f"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.3", "2e3d009422addf8b15c3dccc65ce53baccbe26f7cfd21d264680b5867789a9c1", [: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.1", [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", "c8845177a866e017dcb7083365393c8f00ab061b8b6b2bda575891079dce81b2"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_view": {:hex, :phoenix_view, "1.0.0", "fea71ecaaed71178b26dd65c401607de5ec22e2e9ef141389c721b3f3d4d8011", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "82be3e2516f5633220246e2e58181282c71640dab7afc04f70ad94253025db0c"},
"plug": {:hex, :plug, "1.13.4", "addb6e125347226e3b11489e23d22a60f7ab74786befb86c14f94fb5f23ca9a4", [: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", "06114c1f2a334212fe3ae567dbb3b1d29fd492c1a09783d52f3d489c1a6f4cf2"},
"plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
"porcelain": {:hex, :porcelain, "2.0.3", "2d77b17d1f21fed875b8c5ecba72a01533db2013bd2e5e62c6d286c029150fdc", [:mix], [], "hexpm", "dc996ab8fadbc09912c787c7ab8673065e50ea1a6245177b0c24569013d23620"},
"postgrex": {:hex, :postgrex, "0.15.10", "2809dee1b1d76f7cbabe570b2a9285c2e7b41be60cf792f5f2804a54b838a067", [:mix], [{:connection, "~> 1.0", [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]}], "hexpm", "1560ca427542f6b213f8e281633ae1a3b31cdbcd84ebd7f50628765b8f6132be"},
"phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"},
"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"},
"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"},
"rustler": {:hex, :rustler, "0.22.2", "f92d6dba71bef6fe5f0d955649cb071127adc92f32a78890e8fa9939e59a1b41", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.5.2", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "56b129141e86d60a2d670af9a2b55a9071e10933ef593034565af77e84655118"},
"swoosh": {:hex, :swoosh, "1.5.0", "2be4cfc1be10f2203d1854c85b18d8c7be0321445a782efd53ef0b2b88f03ce4", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, 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", "b53891359e3ddca263ece784051243de84c9244c421a0dee1bff1d52fc5ca420"},
"tailwind": {:hex, :tailwind, "0.1.3", "117ada41ee06343d77269ee5f5ef58ef93ddf4d596a0486db7be9d6157487f9e", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "1fe9ecb50ae521a328a787f22b60fb0e14d29c8d2a5983186962fdbd8162608a"},
"telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"},
"swoosh": {:hex, :swoosh, "1.8.2", "af9a22ab2c0d20b266f61acca737fa11a121902de9466a39e91bacdce012101c", [: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", "d058ba750eafadb6c09a84a352c14c5d1eeeda6e84945fcc95785b7f3067b7db"},
"tailwind": {:hex, :tailwind, "0.1.9", "25ba09d42f7bfabe170eb67683a76d6ec2061952dc9bd263a52a99ba3d24bd4d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "9213f87709c458aaec313bb5f2df2b4d2cedc2b630e4ae821bf3c54c47a56d0b"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
"toml": {:hex, :toml, "0.5.2", "e471388a8726d1ce51a6b32f864b8228a1eb8edc907a0edf2bb50eab9321b526", [:mix], [], "hexpm", "f1e3dabef71fb510d015fad18c0e05e7c57281001141504c6b69d94e99750a07"},
"websock": {:hex, :websock, "0.4.3", "184ac396bdcd3dfceb5b74c17d221af659dd559a95b1b92041ecb51c9b728093", [:mix], [], "hexpm", "5e4dd85f305f43fd3d3e25d70bec4a45228dfed60f0f3b072d8eddff335539cf"},
"websock_adapter": {:hex, :websock_adapter, "0.4.5", "30038a3715067f51a9580562c05a3a8d501126030336ffc6edb53bf57d6d2d26", [:mix], [{:bandit, "~> 0.6", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.4", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "1d9812dc7e703c205049426fd4fe0852a247a825f91b099e53dc96f68bafe4c8"},
}

View File

@ -5,7 +5,7 @@ defmodule LiveBeatsWeb.ProfileLiveTest do
import LiveBeats.AccountsFixtures
alias LiveBeats.MediaLibrary
alias LiveBeatsWeb.LiveHelpers
alias LiveBeatsWeb.CoreComponents
setup %{conn: conn} do
current_user = user_fixture(%{username: "chrismccord"})
@ -17,7 +17,7 @@ defmodule LiveBeatsWeb.ProfileLiveTest do
describe "own profile" do
test "profile page uploads", %{conn: conn, current_user: current_user} do
profile = MediaLibrary.get_profile!(current_user)
{:ok, lv, dead_html} = live(conn, LiveHelpers.profile_path(current_user))
{:ok, lv, dead_html} = live(conn, CoreComponents.profile_path(current_user))
assert dead_html =~ "chrismccord&#39;s beats"
@ -59,7 +59,7 @@ defmodule LiveBeatsWeb.ProfileLiveTest do
song = MediaLibrary.get_first_song(profile)
assert lv |> element("#delete-modal-#{song.id}-confirm") |> render_click()
{:ok, refreshed_lv, _} = live(conn, LiveHelpers.profile_path(current_user))
{:ok, refreshed_lv, _} = live(conn, CoreComponents.profile_path(current_user))
refute render(refreshed_lv) =~ "silence1s"
end