diff --git a/assets/css/_phx-liveview.scss b/assets/css/_phx-liveview.scss index 4dadcd43..702bdd9f 100644 --- a/assets/css/_phx-liveview.scss +++ b/assets/css/_phx-liveview.scss @@ -9,7 +9,6 @@ /* sets default bootstrap form-control styles if field not touched yet */ .phx-no-feedback .form-control.is-valid, .phx-no-feedback .form-control.is-invalid { - padding: $input-padding-y $input-padding-x; border: $input-border-width solid $input-border-color; background-image: none; diff --git a/assets/css/app.scss b/assets/css/app.scss index d1d02301..384fc844 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -31,3 +31,36 @@ .cursor-pointer { cursor: pointer !important; } + +/* + icons in input fields + */ +/* enable absolute positioning */ +.inner-addon { + position: relative; +} + +/* style icon */ +.inner-addon > .icon { + position: absolute; + padding: 0.5625rem 0.5rem; + pointer-events: none; +} + +/* align icon */ +.inner-addon > .icon.is-left { + left: 0px; +} +.inner-addon > .icon.is-right { + right: 0px; +} + +/* add padding */ +.left-addon input, +.left-addon select { + padding-left: 2rem; +} +.right-addon input, +.right-addon select { + padding-right: 2rem; +} diff --git a/assets/js/app.js b/assets/js/app.js index aeee1e5f..5d161237 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -19,7 +19,9 @@ import "../node_modules/bootstrap-icons/icons/key-fill.svg"; // pw confirm field import "../node_modules/bootstrap-icons/icons/lock.svg"; // current pw field import "../node_modules/bootstrap-icons/icons/shield.svg"; // role // live tables -import "../node_modules/bootstrap-icons/icons/backspace.svg"; // clear filter +import "../node_modules/bootstrap-icons/icons/arrow-down-up.svg"; // sort +import "../node_modules/bootstrap-icons/icons/funnel.svg"; // filter +import "../node_modules/bootstrap-icons/icons/x-circle-fill.svg"; // clear filter import "../node_modules/bootstrap-icons/icons/sort-down-alt.svg"; import "../node_modules/bootstrap-icons/icons/sort-up-alt.svg"; import "../node_modules/bootstrap-icons/icons/chevron-left.svg"; diff --git a/lib/shift73k_web/live/user/registration.html.leex b/lib/shift73k_web/live/user/registration.html.leex index 511a9666..0bc2afa8 100644 --- a/lib/shift73k_web/live/user/registration.html.leex +++ b/lib/shift73k_web/live/user/registration.html.leex @@ -9,8 +9,9 @@ <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "reg_form"], fn f -> %> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :email) %>"> + <%= label f, :email, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :email) %>"> + <%= icon_div @socket, "bi-at", [class: "icon is-left text-muted fs-5"] %> <%= email_input f, :email, value: input_value(f, :email), class: input_class(f, :email, "form-control"), @@ -20,31 +21,22 @@ phx_debounce: "blur", aria_describedby: error_ids(f, :email) %> - <%= label f, :email do %> - <%= icon_div @socket, "bi-at", [class: "icon baseline text-muted"] %> - Email - <% end %> <%= error_tag f, :email %> </div> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= label f, :password, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password, value: input_value(f, :password), class: input_class(f, :password, "form-control"), - placeholder: "Password", maxlength: User.max_password, phx_debounce: "250", aria_describedby: error_ids(f, :password) %> - <%= label f, :password do %> - <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %> - Password - <% end %> <%= error_tag f, :password %> </div> - <div class="mb-3"> <%= submit (@trigger_submit && "Saving..." || "Register"), class: "btn btn-primary", diff --git a/lib/shift73k_web/live/user/reset_password.html.leex b/lib/shift73k_web/live/user/reset_password.html.leex index b29ae36c..049d15a5 100644 --- a/lib/shift73k_web/live/user/reset_password.html.leex +++ b/lib/shift73k_web/live/user/reset_password.html.leex @@ -9,39 +9,31 @@ <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "pw_reset_form"], fn f -> %> - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= label f, :password, "New password", class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password, value: input_value(f, :password), class: input_class(f, :password, "form-control"), - placeholder: "New Password", maxlength: User.max_password, autofocus: true, aria_describedby: error_ids(f, :password) %> - <%= label f, :password do %> - <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %> - New password - <% end %> <%= error_tag f, :password %> </div> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>"> + <%= label f, :password_confirmation, "Confirm new password", class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>"> + <%= icon_div @socket, "bi-key-fill", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password_confirmation, value: input_value(f, :password_confirmation), class: input_class(f, :password_confirmation, "form-control"), - placeholder: "Confirm new password", maxlength: User.max_password, aria_describedby: error_ids(f, :password_confirmation) %> - <%= label f, :password do %> - <%= icon_div @socket, "bi-key-fill", [class: "icon baseline text-muted"] %> - Confirm new password - <% end %> <%= error_tag f, :password_confirmation %> </div> - <div class="mb-3"> <%= submit "Reset password", class: "btn btn-primary", diff --git a/lib/shift73k_web/live/user/settings/email.html.leex b/lib/shift73k_web/live/user/settings/email.html.leex index 2d1b6e47..d50ce902 100644 --- a/lib/shift73k_web/live/user/settings/email.html.leex +++ b/lib/shift73k_web/live/user/settings/email.html.leex @@ -4,8 +4,9 @@ <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :email) %>"> + <%= label f, :email, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :email) %>"> + <%= icon_div @socket, "bi-at", [class: "icon is-left text-muted fs-5"] %> <%= email_input f, :email, value: input_value(f, :email), class: input_class(f, :email, "form-control"), @@ -14,29 +15,20 @@ phx_debounce: "500", aria_describedby: error_ids(f, :email) %> - <%= label f, :email do %> - <%= icon_div @socket, "bi-at", [class: "icon baseline text-muted"] %> - Email - <% end %> <%= error_tag f, :email %> </div> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>"> + <%= label f, :current_password, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>"> + <%= icon_div @socket, "bi-lock", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :current_password, value: input_value(f, :current_password), class: "form-control", - placeholder: "Current Password", aria_describedby: error_ids(f, :current_password) %> - <%= label f, :current_password do %> - <%= icon_div @socket, "bi-lock", [class: "icon baseline text-muted"] %> - Current password - <% end %> <%= error_tag f, :current_password %> </div> - <div class="mb-3"> <%= submit "Change email", class: "btn btn-primary", diff --git a/lib/shift73k_web/live/user/settings/password.html.leex b/lib/shift73k_web/live/user/settings/password.html.leex index 6d781dfd..a15d30ff 100644 --- a/lib/shift73k_web/live/user/settings/password.html.leex +++ b/lib/shift73k_web/live/user/settings/password.html.leex @@ -4,51 +4,39 @@ <%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= label f, :password, "New password", class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password, value: input_value(f, :password), class: input_class(f, :password, "form-control"), - placeholder: "New Password", maxlength: User.max_password, phx_debounce: "500", aria_describedby: error_ids(f, :password) %> - <%= label f, :password do %> - <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %> - New password - <% end %> <%= error_tag f, :password %> </div> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>"> + <%= label f, :password_confirmation, "Confirm new password", class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>"> + <%= icon_div @socket, "bi-key-fill", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password_confirmation, value: input_value(f, :password_confirmation), class: input_class(f, :password_confirmation, "form-control"), - placeholder: "Confirm new password", maxlength: User.max_password, aria_describedby: error_ids(f, :password_confirmation) %> - <%= label f, :password_confirmation do %> - <%= icon_div @socket, "bi-key-fill", [class: "icon baseline text-muted"] %> - Confirm new password - <% end %> <%= error_tag f, :password_confirmation %> </div> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>"> + <%= label f, :current_password, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>"> + <%= icon_div @socket, "bi-lock", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :current_password, value: input_value(f, :current_password), class: "form-control", - placeholder: "Current Password", aria_describedby: error_ids(f, :current_password) %> - <%= label f, :current_password do %> - <%= icon_div @socket, "bi-lock", [class: "icon baseline text-muted"] %> - Current password - <% end %> <%= error_tag f, :current_password %> </div> @@ -68,10 +56,4 @@ <%= hidden_input f, :params_token, value: Phoenix.Token.encrypt(Shift73kWeb.Endpoint, "login_params", @login_params) %> <% end %> - <%# hidden form to submit user for relogin after password change %> - <%#= form_for :user_login, Routes.user_session_path(@socket, :create), [phx_trigger_action: @trigger_submit], fn f -> %> - <%#= hidden_input f, :login_params_token, value: Phoenix.Token.encrypt(Shift73kWeb.Endpoint, "login_params", @login_params) %> - <%#= hidden_input f, :remember_me, value: false %> - <%# end %> - </div> diff --git a/lib/shift73k_web/live/user_management/form_component.html.leex b/lib/shift73k_web/live/user_management/form_component.html.leex index 65607c0c..db0accd5 100644 --- a/lib/shift73k_web/live/user_management/form_component.html.leex +++ b/lib/shift73k_web/live/user_management/form_component.html.leex @@ -6,7 +6,9 @@ <div class="modal-body"> - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :email) %>"> + <%= label f, :email, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :email) %>"> + <%= icon_div @socket, "bi-at", [class: "icon is-left text-muted fs-5"] %> <%= email_input f, :email, value: input_value(f, :email), class: input_class(f, :email, "form-control"), @@ -16,39 +18,26 @@ phx_debounce: "250", aria_describedby: error_ids(f, :email) %> - <%= label f, :email do %> - <%= icon_div @socket, "bi-at", [class: "icon baseline text-muted"] %> - Email - <% end %> <%= error_tag f, :email %> </div> - - <div class="form-floating mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= label f, :password, class: "form-label" %> + <div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>"> + <%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password, value: input_value(f, :password), class: input_class(f, :password, "form-control"), - placeholder: "Password", maxlength: User.max_password, aria_describedby: error_ids(f, :password) %> - <%= label f, :password do %> - <%= icon_div @socket, "bi-key", [class: "icon baseline text-muted"] %> - Password - <% end %> <%= error_tag f, :password %> </div> - - - <%= if Roles.can?(@current_user, %User{}, :edit_role) do %> - <div class="form-floating"> + <%= label f, :role, class: "form-label" %> + <div class="inner-addon left-addon mb-3"> + <%= icon_div @socket, "bi-shield", [class: "icon is-left text-muted fs-5"] %> <%= select f, :role, Enum.map(User.roles(), fn {k, _v} -> {String.capitalize(Atom.to_string(k)), k} end), class: "form-select" %> - <%= label f, :role do %> - <%= icon_div @socket, "bi-shield", [class: "icon baseline text-muted"] %> - User role - <% end %> <span class="valid-feedback text-primary" style="display: block;"> <%= role_description(input_value(f, :role)) %> </span> @@ -57,7 +46,6 @@ <%= hidden_input f, :role, value: input_value(f, :role) %> <% end %> - </div> <div class="modal-footer"> diff --git a/lib/shift73k_web/live/user_management/index.html.leex b/lib/shift73k_web/live/user_management/index.html.leex index 8cdb428e..8861fc1a 100644 --- a/lib/shift73k_web/live/user_management/index.html.leex +++ b/lib/shift73k_web/live/user_management/index.html.leex @@ -22,34 +22,100 @@ </h2> <%# filtering and new item creation %> -<div class="d-flex flex-column flex-sm-row justify-content-between d-flex align-items-start mb-3"> +<div class="row justify-content-between align-items-start align-items-sm-end mb-3"> + <div class="col-12 col-sm-6 col-lg-4 col-xl-3"> <%= live_patch to: Routes.user_management_index_path(@socket, :new, Enum.into(@query, [])), class: "btn btn-primary mb-3 mb-sm-0" do %> <%= icon_div @socket, "bi-person-plus", [class: "icon baseline me-1"] %> New User <% end %> - - <%= form_for :filter, "#", [phx_change: "filter-change"], fn flt -> %> - <div class="input-group"> - - <%= text_input flt, :filter, name: "filter", class: "form-control", placeholder: "Filter users...", value: @query.filter %> - - <%= if @query.filter == "" do %> - <button class="btn btn-outline-primary" type="button" aria-label="Clear filter" aria-disabled="true" disabled> - <% else %> - <button class="btn btn-primary" type="button" aria-label="Clear filter" phx-click="filter-clear"> - <% end %> - <%= icon_div @socket, "bi-backspace", [class: "icon baseline"], [role: "img", aria_hidden: false] %> - </button> - </div> + + <div class="col-12 d-block d-sm-none"> + <%= form_for :sort, "#", [phx_change: "sort-by-change"], fn srt -> %> + <%= label srt, :sort_by, class: "visually-hidden" %> + <div class="input-group inner-addon left-addon mb-3 mb-sm-0"> + <%= icon_div @socket, "bi-arrow-down-up", [class: "icon is-left text-muted fs-5", style: "z-index:1001;"], [style: "padding: 1px;"] %> + <%= Phoenix.HTML.Form.select srt, :sort_by, ["Email": "email", "Role": "role", "Created at": "inserted_at"], value: @query.sort_by, class: "form-select" %> + <button class="btn btn-primary" type="button" aria-label="Change sort order" phx-click="sort-order-change"> + <%= icon_div @socket, (@query.sort_order == "desc" && "bi-sort-up-alt" || "bi-sort-down-alt"), [class: "icon baseline"] %> + </button> + </div> + <% end %> + </div> + + <div class="col-12 col-sm-6 col-lg-4 col-xl-3"> + <%= form_for :filter, "#", [phx_change: "filter-change"], fn flt -> %> + <%= label flt, :filter, class: "visually-hidden" %> + <div class="inner-addon left-addon right-addon"> + <%= icon_div @socket, "bi-funnel", [class: "icon is-left text-muted fs-5"] %> + <%= if @query.filter != "" do %> + <%= icon_div @socket, "bi-x-circle-fill", [class: "icon is-right text-primary fs-5"], [role: "img", aria_hidden: false, aria_label: "Clear filter", class: "cursor-pointer pe-auto", phx_click: "filter-clear"] %> + <% end %> + <%= text_input flt, :filter, + name: "filter", + value: @query.filter, + class: "form-control", + placeholder: "Filter users", + aria_label: "Filter users" + %> + </div> + <% end %> + </div> +</div> + +<%# mobile data cards %> +<div class="d-block d-sm-none"> + + + <%= if !@page do %> + <div class="card"> + <div class="spinner-border text-primary my-5 mx-auto" role="status"> + <span class="visually-hidden">Loading...</span> + </div> + </div> + <% else %> + <%= for user <- @page.entries do %> + <div class="card mb-3"> + <div class="card-body"> + <dl class="row"> + <dt class="col-sm-3">Email</dt> + <dd class="col-sm-9"><%= user.email %></dd> + <dt class="col-sm-3">Role</dt> + <dd class="col-sm-9"><%= user.role |> Atom.to_string() |> String.capitalize() %></dd> + <dt class="col-sm-3">Created at</dt> + <dd class="col-sm-9"><%= dt_out(user.inserted_at) %></dd> + <dt class="col-sm-3"> + Confirmed? + <span class="visually-hidden"><%= user.confirmed_at && "Yes" || "No" %></span> + <input type="checkbox" class="form-check-input ms-1" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %>> + </dt> + </dl> + + <%= if Roles.can?(@current_user, user, :edit) do %> + <%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-outline-primary btn-sm text-nowrap" do %> + <%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %> + Edit + <% end %> + <% end %> + + <%= if Roles.can?(@current_user, user, :delete) do %> + <button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>"> + <%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %> + Delete + </button> + <% end %> + + </div> + </div> + <% end %> <% end %> </div> -<%# main data table %> -<div class="table-responsive"> +<%# non-mobile main data table %> +<div class="table-responsive d-none d-sm-block"> <table class="table"> <thead> @@ -102,13 +168,8 @@ <td class="align-middle"><%= user.role |> Atom.to_string() |> String.capitalize() %></td> <td class="align-middle" style="white-space: nowrap;"><%= dt_out(user.inserted_at) %></td> <td class="align-middle"> - <%= if user.confirmed_at do %> - <span class="visually-hidden">Yes</span> - <%= icon_div @socket, "bi-check", [class: "icon baseline fs-4 text-success"], [role: "img", aria_hidden: false] %> - <% else %> - <span class="visually-hidden">No</span> - <%= icon_div @socket, "bi-x", [class: "icon baseline fs-4 text-warning"], [role: "img", aria_hidden: false] %> - <% end %> + <span class="visually-hidden"><%= user.confirmed_at && "Confirmed" || "Not confirmed" %></span> + <input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %>> </td> <td class="align-middle text-end text-nowrap"> @@ -153,7 +214,7 @@ class: "form-select" %> <% end %> - <span class="ms-2"><%= @page.total_entries %> found</span> + <span class="ms-2"><%= @page.total_entries %> total</span> </div> <%# main pagination %> diff --git a/lib/shift73k_web/templates/user_confirmation/new.html.eex b/lib/shift73k_web/templates/user_confirmation/new.html.eex index 5053dad7..1fb18e7e 100644 --- a/lib/shift73k_web/templates/user_confirmation/new.html.eex +++ b/lib/shift73k_web/templates/user_confirmation/new.html.eex @@ -9,7 +9,9 @@ <%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %> - <div class="form-floating mb-3"> + <%= label f, :email, class: "form-label" %> + <div class="inner-addon left-addon mb-3"> + <%= icon_div @conn, "bi-at", [class: "icon is-left text-muted fs-5"] %> <%= email_input f, :email, value: @current_user && @current_user.email || "", placeholder: "e.g., babka@73k.us", @@ -18,10 +20,6 @@ required: true, autofocus: !@current_user %> - <%= label f, :email do %> - <%= icon_div @conn, "bi-at", [class: "icon baseline text-muted"] %> - Email - <% end %> <span class="invalid-feedback">must be a valid email address</span> </div> diff --git a/lib/shift73k_web/templates/user_reset_password/new.html.eex b/lib/shift73k_web/templates/user_reset_password/new.html.eex index aa2f5476..06aefbc1 100644 --- a/lib/shift73k_web/templates/user_reset_password/new.html.eex +++ b/lib/shift73k_web/templates/user_reset_password/new.html.eex @@ -9,17 +9,16 @@ <%= form_for :user, Routes.user_reset_password_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %> - <div class="form-floating mb-3"> + <%= label f, :email, class: "form-label" %> + <div class="inner-addon left-addon mb-3"> + <%= icon_div @conn, "bi-at", [class: "icon is-left text-muted fs-5"] %> <%= email_input f, :email, - class: "form-control", placeholder: "e.g., babka@73k.us", + class: "form-control", maxlength: User.max_email, - required: true + required: true, + autofocus: true %> - <%= label f, :email do %> - <%= icon_div @conn, "bi-at", [class: "icon baseline text-muted"] %> - Email - <% end %> <span class="invalid-feedback">must be a valid email address</span> </div> diff --git a/lib/shift73k_web/templates/user_session/new.html.eex b/lib/shift73k_web/templates/user_session/new.html.eex index 7b62f589..310f089d 100644 --- a/lib/shift73k_web/templates/user_session/new.html.eex +++ b/lib/shift73k_web/templates/user_session/new.html.eex @@ -15,36 +15,28 @@ </div> <% end %> - - <div class="form-floating mb-3"> + <%= label f, :email, class: "form-label" %> + <div class="inner-addon left-addon mb-3"> + <%= icon_div @conn, "bi-at", [class: "icon is-left text-muted fs-5"] %> <%= email_input f, :email, class: "form-control", placeholder: "e.g., babka@73k.us", maxlength: User.max_email, required: true %> - <%= label f, :email do %> - <%= icon_div @conn, "bi-at", [class: "icon baseline text-muted"] %> - Email - <% end %> <span class="invalid-feedback">must be a valid email address</span> </div> - - <div class="form-floating mb-3"> + <%= label f, :password, class: "form-label" %> + <div class="inner-addon left-addon mb-3"> + <%= icon_div @conn, "bi-lock", [class: "icon is-left text-muted fs-5"] %> <%= password_input f, :password, class: "form-control", - placeholder: "Password", required: true %> - <%= label f, :password do %> - <%= icon_div @conn, "bi-lock", [class: "icon baseline text-muted"] %> - Password - <% end %> <span class="invalid-feedback">password is required</span> </div> - <div class="form-check mb-3 no-valid-style"> <%= checkbox f, :remember_me, class: "form-check-input" %> <%= label f, :remember_me, "Keep me logged in for 60 days", class: "form-check-label" %> diff --git a/lib/shift73k_web/views/error_helpers.ex b/lib/shift73k_web/views/error_helpers.ex index 0c3703c1..76b8145b 100644 --- a/lib/shift73k_web/views/error_helpers.ex +++ b/lib/shift73k_web/views/error_helpers.ex @@ -22,7 +22,6 @@ defmodule Shift73kWeb.ErrorHelpers do content_tag(:span, translate_error(err), opts) end - defp error_common_opts(form, field, append, opts) do Keyword.put(opts, :phx_feedback_for, input_id(form, field)) |> Keyword.update(:class, append, fn c -> "#{append} #{c}" end) @@ -37,6 +36,7 @@ defmodule Shift73kWeb.ErrorHelpers do def error_ids(%Phoenix.HTML.Form{} = form, field) do input_id = input_id(form, field) + form.errors |> Keyword.get_values(field) |> Stream.with_index() @@ -45,15 +45,13 @@ defmodule Shift73kWeb.ErrorHelpers do end def input_class(form, field, classes \\ "") do - case form.source.action do - nil -> - classes - - _ -> - case Keyword.has_key?(form.errors, field) do - true -> "#{classes} is-invalid" - _ -> "#{classes} is-valid" - end + if form.source.action do + case Keyword.has_key?(form.errors, field) do + true -> "#{classes} is-invalid" + _ -> "#{classes} is-valid" + end + else + classes end end diff --git a/lib/shift73k_web/views/icon_helpers.ex b/lib/shift73k_web/views/icon_helpers.ex index 32f15b37..048373f8 100644 --- a/lib/shift73k_web/views/icon_helpers.ex +++ b/lib/shift73k_web/views/icon_helpers.ex @@ -16,7 +16,10 @@ defmodule Shift73kWeb.IconHelpers do opts = aria_hidden?(opts) content_tag(:svg, tag_opts(name, opts)) do - tag(:use, "xlink:href": Routes.static_path(conn, "/images/icons.svg##{name}")) + ~E""" + <%= if title = Keyword.get(opts, :aria_label), do: content_tag(:title, title) %> + <%= tag(:use, "xlink:href": Routes.static_path(conn, "/images/icons.svg##{name}")) %> + """ end end