improved input fields, icons, error helpers
This commit is contained in:
parent
0039146cd4
commit
6aa9a4bd48
16 changed files with 250 additions and 269 deletions
|
@ -9,7 +9,6 @@
|
||||||
/* sets default bootstrap form-control styles if field not touched yet */
|
/* sets default bootstrap form-control styles if field not touched yet */
|
||||||
.phx-no-feedback .form-control.is-valid,
|
.phx-no-feedback .form-control.is-valid,
|
||||||
.phx-no-feedback .form-control.is-invalid {
|
.phx-no-feedback .form-control.is-invalid {
|
||||||
padding: $input-padding-y $input-padding-x;
|
|
||||||
border: $input-border-width solid $input-border-color;
|
border: $input-border-width solid $input-border-color;
|
||||||
background-image: none;
|
background-image: none;
|
||||||
|
|
||||||
|
|
|
@ -31,3 +31,36 @@
|
||||||
.cursor-pointer {
|
.cursor-pointer {
|
||||||
cursor: pointer !important;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ import "../node_modules/bootstrap-icons/icons/at.svg"; // email field
|
||||||
import "../node_modules/bootstrap-icons/icons/key.svg"; // new password field
|
import "../node_modules/bootstrap-icons/icons/key.svg"; // new password field
|
||||||
import "../node_modules/bootstrap-icons/icons/key-fill.svg"; // pw confirm field
|
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/lock.svg"; // current pw field
|
||||||
import "../node_modules/bootstrap-icons/icons/shield-shaded.svg"; // role
|
import "../node_modules/bootstrap-icons/icons/shield.svg"; // role
|
||||||
// live tables
|
// live tables
|
||||||
import "../node_modules/bootstrap-icons/icons/filter.svg";
|
import "../node_modules/bootstrap-icons/icons/funnel.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/backspace.svg"; // clear filter
|
import "../node_modules/bootstrap-icons/icons/backspace.svg"; // clear filter
|
||||||
import "../node_modules/bootstrap-icons/icons/sort-down-alt.svg";
|
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/sort-up-alt.svg";
|
||||||
|
@ -29,6 +29,7 @@ import "../node_modules/bootstrap-icons/icons/pencil.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/trash.svg";
|
import "../node_modules/bootstrap-icons/icons/trash.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/check.svg";
|
import "../node_modules/bootstrap-icons/icons/check.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/x.svg";
|
import "../node_modules/bootstrap-icons/icons/x.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/x-circle-fill.svg";
|
||||||
// page headers
|
// page headers
|
||||||
import "../node_modules/bootstrap-icons/icons/shield-lock.svg"; // reset password
|
import "../node_modules/bootstrap-icons/icons/shield-lock.svg"; // reset password
|
||||||
import "../node_modules/bootstrap-icons/icons/arrow-repeat.svg"; // resend confirmation
|
import "../node_modules/bootstrap-icons/icons/arrow-repeat.svg"; // resend confirmation
|
||||||
|
|
|
@ -7,39 +7,33 @@
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :name)%>">
|
|
||||||
<%= label f, :name, class: "form-label" %>
|
<%= label f, :name, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="mb-3" phx-feedback-for="<%= input_id(f, :name) %>">
|
||||||
<%= text_input f, :name,
|
<%= text_input f, :name,
|
||||||
class: input_class(f, :name, "form-control"),
|
class: input_class(f, :name, "form-control"),
|
||||||
aria_describedby: error_id(f, :name)
|
aria_describedby: error_ids(f, :name)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :name %>
|
<%= error_tag f, :name %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :price)%>">
|
|
||||||
<%= label f, :price, class: "form-label" %>
|
<%= label f, :price, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="mb-3" phx-feedback-for="<%= input_id(f, :price) %>">
|
||||||
<%= number_input f, :price,
|
<%= number_input f, :price,
|
||||||
class: input_class(f, :price, "form-control"),
|
class: input_class(f, :price, "form-control"),
|
||||||
step: "any",
|
step: "any",
|
||||||
aria_describedby: error_id(f, :price)
|
aria_describedby: error_ids(f, :price)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :price %>
|
<%= error_tag f, :price %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :description)%>">
|
|
||||||
<%= label f, :description, class: "form-label" %>
|
<%= label f, :description, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="mb-3" phx-feedback-for="<%= input_id(f, :description) %>">
|
||||||
<%= textarea f, :description,
|
<%= textarea f, :description,
|
||||||
class: input_class(f, :description, "form-control"),
|
class: input_class(f, :description, "form-control"),
|
||||||
aria_describedby: error_id(f, :description)
|
aria_describedby: error_ids(f, :description)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :description %>
|
<%= error_tag f, :description %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
|
@ -9,12 +9,10 @@
|
||||||
|
|
||||||
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "reg_form"], fn f -> %>
|
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "reg_form"], fn f -> %>
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
|
|
||||||
<%= label f, :email, class: "form-label" %>
|
<%= label f, :email, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-at", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-at", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= email_input f, :email,
|
<%= email_input f, :email,
|
||||||
value: input_value(f, :email),
|
value: input_value(f, :email),
|
||||||
class: input_class(f, :email, "form-control"),
|
class: input_class(f, :email, "form-control"),
|
||||||
|
@ -22,28 +20,23 @@
|
||||||
maxlength: User.max_email,
|
maxlength: User.max_email,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
phx_debounce: "blur",
|
phx_debounce: "blur",
|
||||||
aria_describedby: error_id(f, :email)
|
aria_describedby: error_ids(f, :email)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :email %>
|
<%= error_tag f, :email %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
|
||||||
<%= label f, :password, class: "form-label" %>
|
<%= label f, :password, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-key", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password,
|
<%= password_input f, :password,
|
||||||
value: input_value(f, :password),
|
value: input_value(f, :password),
|
||||||
class: input_class(f, :password, "form-control"),
|
class: input_class(f, :password, "form-control"),
|
||||||
maxlength: User.max_password,
|
maxlength: User.max_password,
|
||||||
phx_debounce: "250",
|
phx_debounce: "250",
|
||||||
aria_describedby: error_id(f, :password)
|
aria_describedby: error_ids(f, :password)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :password %>
|
<%= error_tag f, :password %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<%= submit (@trigger_submit && "Saving..." || "Register"),
|
<%= submit (@trigger_submit && "Saving..." || "Register"),
|
||||||
|
|
|
@ -9,38 +9,31 @@
|
||||||
|
|
||||||
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "pw_reset_form"], fn f -> %>
|
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "pw_reset_form"], fn f -> %>
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
|
||||||
<%= label f, :password, "New password", class: "form-label" %>
|
<%= label f, :password, "New password", class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-key", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password,
|
<%= password_input f, :password,
|
||||||
value: input_value(f, :password),
|
value: input_value(f, :password),
|
||||||
class: input_class(f, :password, "form-control"),
|
class: input_class(f, :password, "form-control"),
|
||||||
maxlength: User.max_password,
|
maxlength: User.max_password,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
aria_describedby: error_id(f, :password)
|
aria_describedby: error_ids(f, :password)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :password %>
|
<%= error_tag f, :password %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
|
|
||||||
<%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
|
<%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-key-fill", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-key-fill", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password_confirmation,
|
<%= password_input f, :password_confirmation,
|
||||||
value: input_value(f, :password_confirmation),
|
value: input_value(f, :password_confirmation),
|
||||||
class: input_class(f, :password_confirmation, "form-control"),
|
class: input_class(f, :password_confirmation, "form-control"),
|
||||||
maxlength: User.max_password,
|
maxlength: User.max_password,
|
||||||
aria_describedby: error_id(f, :password_confirmation)
|
aria_describedby: error_ids(f, :password_confirmation)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :password_confirmation %>
|
<%= error_tag f, :password_confirmation %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<%= submit "Reset password",
|
<%= submit "Reset password",
|
||||||
|
|
|
@ -4,39 +4,33 @@
|
||||||
|
|
||||||
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
|
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
|
|
||||||
<%= label f, :email, class: "form-label" %>
|
<%= label f, :email, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-at", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-at", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= email_input f, :email,
|
<%= email_input f, :email,
|
||||||
value: input_value(f, :email),
|
value: input_value(f, :email),
|
||||||
class: input_class(f, :email, "form-control"),
|
class: input_class(f, :email, "form-control"),
|
||||||
placeholder: "e.g., babka@73k.us",
|
placeholder: "e.g., babka@73k.us",
|
||||||
maxlength: User.max_email,
|
maxlength: User.max_email,
|
||||||
phx_debounce: "500",
|
phx_debounce: "500",
|
||||||
aria_describedby: error_id(f, :email)
|
aria_describedby: error_ids(f, :email)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :email %>
|
<%= error_tag f, :email %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
|
|
||||||
<%= label f, :current_password, class: "form-label" %>
|
<%= label f, :current_password, class: "form-label" %>
|
||||||
<div class="input-group">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-lock", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-lock", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :current_password,
|
<%= password_input f, :current_password,
|
||||||
value: input_value(f, :current_password),
|
value: input_value(f, :current_password),
|
||||||
class: "form-control",
|
class: "form-control",
|
||||||
aria_describedby: error_id(f, :current_password)
|
aria_describedby: error_ids(f, :current_password)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :current_password %>
|
<%= error_tag f, :current_password %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<%= submit "Change email",
|
<%= submit "Change email",
|
||||||
|
|
|
@ -4,53 +4,42 @@
|
||||||
|
|
||||||
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
|
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, phx_target: @myself], fn f -> %>
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
|
||||||
<%= label f, :password, "New password", class: "form-label" %>
|
<%= label f, :password, "New password", class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-key", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-key", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password,
|
<%= password_input f, :password,
|
||||||
value: input_value(f, :password),
|
value: input_value(f, :password),
|
||||||
class: input_class(f, :password, "form-control"),
|
class: input_class(f, :password, "form-control"),
|
||||||
maxlength: User.max_password,
|
maxlength: User.max_password,
|
||||||
phx_debounce: "500",
|
phx_debounce: "500",
|
||||||
aria_describedby: error_id(f, :password)
|
aria_describedby: error_ids(f, :password)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :password %>
|
<%= error_tag f, :password %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
|
|
||||||
<%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
|
<%= label f, :password_confirmation, "Confirm new password", class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :password_confirmation) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-key-fill", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-key-fill", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password_confirmation,
|
<%= password_input f, :password_confirmation,
|
||||||
value: input_value(f, :password_confirmation),
|
value: input_value(f, :password_confirmation),
|
||||||
class: input_class(f, :password_confirmation, "form-control"),
|
class: input_class(f, :password_confirmation, "form-control"),
|
||||||
maxlength: User.max_password,
|
maxlength: User.max_password,
|
||||||
aria_describedby: error_id(f, :password_confirmation)
|
aria_describedby: error_ids(f, :password_confirmation)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :password_confirmation %>
|
<%= error_tag f, :password_confirmation %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
|
|
||||||
<%= label f, :current_password, class: "form-label" %>
|
<%= label f, :current_password, class: "form-label" %>
|
||||||
<div class="input-group">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :current_password) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-lock", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-lock", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :current_password,
|
<%= password_input f, :current_password,
|
||||||
value: input_value(f, :current_password),
|
value: input_value(f, :current_password),
|
||||||
class: "form-control",
|
class: "form-control",
|
||||||
aria_describedby: error_id(f, :current_password)
|
aria_describedby: error_ids(f, :current_password)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :current_password %>
|
<%= error_tag f, :current_password %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<%= submit "Change password",
|
<%= submit "Change password",
|
||||||
|
@ -68,10 +57,4 @@
|
||||||
<%= hidden_input f, :params_token, value: Phoenix.Token.encrypt(Bones73kWeb.Endpoint, "login_params", @login_params) %>
|
<%= hidden_input f, :params_token, value: Phoenix.Token.encrypt(Bones73kWeb.Endpoint, "login_params", @login_params) %>
|
||||||
<% end %>
|
<% 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(Bones73kWeb.Endpoint, "login_params", @login_params) %>
|
|
||||||
<%#= hidden_input f, :remember_me, value: false %>
|
|
||||||
<%# end %>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,12 +6,9 @@
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
<div class="mb-3" phx-feedback-for="<%= input_id(f, :email)%>">
|
|
||||||
<%= label f, :email, class: "form-label" %>
|
<%= label f, :email, class: "form-label" %>
|
||||||
<div class="input-group has-validation">
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :email) %>">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-at", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-at", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= email_input f, :email,
|
<%= email_input f, :email,
|
||||||
value: input_value(f, :email),
|
value: input_value(f, :email),
|
||||||
class: input_class(f, :email, "form-control"),
|
class: input_class(f, :email, "form-control"),
|
||||||
|
@ -19,18 +16,27 @@
|
||||||
maxlength: User.max_email,
|
maxlength: User.max_email,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
phx_debounce: "250",
|
phx_debounce: "250",
|
||||||
aria_describedby: error_id(f, :email)
|
aria_describedby: error_ids(f, :email)
|
||||||
%>
|
%>
|
||||||
<%= error_tag f, :email %>
|
<%= error_tag f, :email %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<%= 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"),
|
||||||
|
maxlength: User.max_password,
|
||||||
|
aria_describedby: error_ids(f, :password)
|
||||||
|
%>
|
||||||
|
<%= error_tag f, :password %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= if Roles.can?(@current_user, %User{}, :edit_role) do %>
|
<%= if Roles.can?(@current_user, %User{}, :edit_role) do %>
|
||||||
<%= label f, :role, class: "form-label" %>
|
<%= label f, :role, class: "form-label" %>
|
||||||
<div class="input-group mb-3">
|
<div class="inner-addon left-addon mb-3">
|
||||||
<span class="input-group-text">
|
<%= icon_div @socket, "bi-shield", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @socket, "bi-shield-shaded", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= select f, :role, Enum.map(User.roles(), fn {k, _v} -> {String.capitalize(Atom.to_string(k)), k} end), class: "form-select" %>
|
<%= select f, :role, Enum.map(User.roles(), fn {k, _v} -> {String.capitalize(Atom.to_string(k)), k} end), class: "form-select" %>
|
||||||
<span class="valid-feedback text-primary" style="display: block;">
|
<span class="valid-feedback text-primary" style="display: block;">
|
||||||
<%= role_description(input_value(f, :role)) %>
|
<%= role_description(input_value(f, :role)) %>
|
||||||
|
@ -40,21 +46,6 @@
|
||||||
<%= hidden_input f, :role, value: input_value(f, :role) %>
|
<%= hidden_input f, :role, value: input_value(f, :role) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div phx-feedback-for="<%= input_id(f, :password) %>">
|
|
||||||
<%= label f, :password, class: "form-label" %>
|
|
||||||
<div class="input-group has-validation">
|
|
||||||
<span class="input-group-text">
|
|
||||||
<%= icon_div @socket, "bi-key", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password,
|
|
||||||
value: input_value(f, :password),
|
|
||||||
class: input_class(f, :password, "form-control"),
|
|
||||||
maxlength: User.max_password,
|
|
||||||
aria_describedby: error_id(f, :password)
|
|
||||||
%>
|
|
||||||
<%= error_tag f, :password %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<%# filtering and new item creation %>
|
<%# 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="d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-end mb-3">
|
||||||
|
|
||||||
<%= live_patch to: Routes.user_management_index_path(@socket, :new, Enum.into(@query, [])),
|
<%= live_patch to: Routes.user_management_index_path(@socket, :new, Enum.into(@query, [])),
|
||||||
class: "btn btn-primary mb-3 mb-sm-0" do %>
|
class: "btn btn-primary mb-3 mb-sm-0" do %>
|
||||||
|
@ -31,24 +31,12 @@
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= form_for :filter, "#", [phx_change: "filter-change"], fn flt -> %>
|
<%= form_for :filter, "#", [phx_change: "filter-change"], fn flt -> %>
|
||||||
<div class="input-group">
|
<div class="inner-addon left-addon right-addon">
|
||||||
|
<%= icon_div @socket, "bi-funnel", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<span class="input-group-text">
|
<%= if @query.filter != "" do %>
|
||||||
<%= icon_div @socket, "bi-filter", [class: "icon"] %>
|
<%= 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"] %>
|
||||||
</span>
|
|
||||||
|
|
||||||
<%= text_input flt, :filter, name: "filter", class: "form-control", placeholder: "Filter users...", value: @query.filter %>
|
|
||||||
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<%= if @query.filter == "" do %>
|
|
||||||
<button class="btn btn-outline-secondary" type="button" aria-label="Clear filter" aria-disabled="true" disabled>
|
|
||||||
<% else %>
|
|
||||||
<button class="btn btn-outline-secondary" type="button" aria-label="Clear filter" phx-click="filter-clear">
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= icon_div @socket, "bi-backspace", [class: "icon baseline"], [role: "img", aria_hidden: false] %>
|
<%= text_input flt, :filter, name: "filter", class: "form-control", placeholder: "Filter users...", value: @query.filter %>
|
||||||
</button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
@ -118,18 +106,18 @@
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle text-end text-nowrap">
|
<td class="align-middle text-end text-nowrap">
|
||||||
|
|
||||||
|
<%= 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 %>
|
<%= 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;"] %>
|
<%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||||
Edit
|
Edit
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= if Roles.can?(@current_user, user, :delete) do %>
|
<%= 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 %>">
|
<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;"] %>
|
<%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
@ -159,7 +147,7 @@
|
||||||
class: "form-select"
|
class: "form-select"
|
||||||
%>
|
%>
|
||||||
<% end %>
|
<% end %>
|
||||||
<span class="ms-2"><%= @page.total_entries %> found</span>
|
<span class="ms-2"><%= @page.total_entries %> total</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%# main pagination %>
|
<%# main pagination %>
|
||||||
|
|
|
@ -10,10 +10,8 @@
|
||||||
<%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
<%= form_for :user, Routes.user_confirmation_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
||||||
|
|
||||||
<%= label f, :email, class: "form-label" %>
|
<%= label f, :email, class: "form-label" %>
|
||||||
<div class="input-group has-validation mb-3">
|
<div class="inner-addon left-addon mb-3">
|
||||||
<span class="input-group-text">
|
<%= icon_div @conn, "bi-at", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @conn, "bi-at", [class: "icon fs-5"] %>
|
|
||||||
</span>
|
|
||||||
<%= email_input f, :email,
|
<%= email_input f, :email,
|
||||||
value: @current_user && @current_user.email || "",
|
value: @current_user && @current_user.email || "",
|
||||||
placeholder: "e.g., babka@73k.us",
|
placeholder: "e.g., babka@73k.us",
|
||||||
|
|
|
@ -10,10 +10,8 @@
|
||||||
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
<%= form_for :user, Routes.user_reset_password_path(@conn, :create), [class: "needs-validation", novalidate: true], fn f -> %>
|
||||||
|
|
||||||
<%= label f, :email, class: "form-label" %>
|
<%= label f, :email, class: "form-label" %>
|
||||||
<div class="input-group has-validation mb-3">
|
<div class="inner-addon left-addon mb-3">
|
||||||
<span class="input-group-text">
|
<%= icon_div @conn, "bi-at", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @conn, "bi-at", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= email_input f, :email,
|
<%= email_input f, :email,
|
||||||
placeholder: "e.g., babka@73k.us",
|
placeholder: "e.g., babka@73k.us",
|
||||||
class: "form-control",
|
class: "form-control",
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
<%= label f, :email, class: "form-label" %>
|
<%= label f, :email, class: "form-label" %>
|
||||||
<div class="input-group has-validation mb-3">
|
<div class="inner-addon left-addon mb-3">
|
||||||
<span class="input-group-text">
|
<%= icon_div @conn, "bi-at", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @conn, "bi-at", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= email_input f, :email,
|
<%= email_input f, :email,
|
||||||
class: "form-control",
|
class: "form-control",
|
||||||
placeholder: "e.g., babka@73k.us",
|
placeholder: "e.g., babka@73k.us",
|
||||||
|
@ -30,10 +29,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= label f, :password, class: "form-label" %>
|
<%= label f, :password, class: "form-label" %>
|
||||||
<div class="input-group has-validation mb-3">
|
<div class="inner-addon left-addon mb-3">
|
||||||
<span class="input-group-text">
|
<%= icon_div @conn, "bi-lock", [class: "icon is-left text-muted fs-5"] %>
|
||||||
<%= icon_div @conn, "bi-lock", [class: "icon"] %>
|
|
||||||
</span>
|
|
||||||
<%= password_input f, :password,
|
<%= password_input f, :password,
|
||||||
class: "form-control",
|
class: "form-control",
|
||||||
required: true
|
required: true
|
||||||
|
@ -41,6 +38,7 @@
|
||||||
<span class="invalid-feedback">password is required</span>
|
<span class="invalid-feedback">password is required</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-check mb-3 no-valid-style">
|
<div class="form-check mb-3 no-valid-style">
|
||||||
<%= checkbox f, :remember_me, class: "form-check-input" %>
|
<%= checkbox f, :remember_me, class: "form-check-input" %>
|
||||||
<%= label f, :remember_me, "Keep me logged in for 60 days", class: "form-check-label" %>
|
<%= label f, :remember_me, "Keep me logged in for 60 days", class: "form-check-label" %>
|
||||||
|
|
|
@ -8,35 +8,50 @@ defmodule Bones73kWeb.ErrorHelpers do
|
||||||
@doc """
|
@doc """
|
||||||
Generates tag for inlined form input errors.
|
Generates tag for inlined form input errors.
|
||||||
"""
|
"""
|
||||||
def error_tag(form, field, opts \\ []) do
|
def error_tag(%Phoenix.HTML.Form{} = form, field, opts \\ []) do
|
||||||
opts = error_opts(form, field, opts)
|
opts = error_common_opts(form, field, "invalid-feedback", opts)
|
||||||
|
|
||||||
form.errors
|
form.errors
|
||||||
|> Keyword.get_values(field)
|
|> Keyword.get_values(field)
|
||||||
|> Enum.map(fn error -> content_tag(:span, translate_error(error), opts) end)
|
|> Stream.with_index()
|
||||||
|
|> Enum.map(fn err_with_index -> error_tag_span(err_with_index, opts) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp error_opts(form, field, opts) do
|
defp error_tag_span({err, _} = err_with_index, opts) do
|
||||||
append = "invalid-feedback"
|
opts = error_tag_opts(err_with_index, opts)
|
||||||
input_id = input_id(form, field)
|
content_tag(:span, translate_error(err), opts)
|
||||||
|
end
|
||||||
|
|
||||||
opts
|
defp error_common_opts(form, field, append, opts) do
|
||||||
|> Keyword.put_new(:id, error_id(input_id))
|
Keyword.put(opts, :phx_feedback_for, input_id(form, field))
|
||||||
|> Keyword.put_new(:phx_feedback_for, input_id)
|
|
||||||
|> Keyword.update(:class, append, fn c -> "#{append} #{c}" end)
|
|> Keyword.update(:class, append, fn c -> "#{append} #{c}" end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_id(%Phoenix.HTML.Form{} = form, field), do: input_id(form, field) |> error_id()
|
defp error_tag_opts({_err, err_index}, opts) do
|
||||||
def error_id(input_id) when is_binary(input_id), do: "#{input_id}_feedback"
|
input_id = Keyword.get(opts, :phx_feedback_for, "")
|
||||||
|
Keyword.put(opts, :id, error_id(input_id, err_index))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp error_id(input_id, err_index), do: "#{input_id}_feedback-#{err_index}"
|
||||||
|
|
||||||
|
def error_ids(%Phoenix.HTML.Form{} = form, field) do
|
||||||
|
input_id = input_id(form, field)
|
||||||
|
|
||||||
|
form.errors
|
||||||
|
|> Keyword.get_values(field)
|
||||||
|
|> Stream.with_index()
|
||||||
|
|> Stream.map(fn {_, index} -> error_id(input_id, index) end)
|
||||||
|
|> Enum.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
def input_class(form, field, classes \\ "") do
|
def input_class(form, field, classes \\ "") do
|
||||||
case form.source.action do
|
if form.source.action do
|
||||||
nil -> classes
|
|
||||||
_ ->
|
|
||||||
case Keyword.has_key?(form.errors, field) do
|
case Keyword.has_key?(form.errors, field) do
|
||||||
true -> "#{classes} is-invalid"
|
true -> "#{classes} is-invalid"
|
||||||
_ -> "#{classes} is-valid"
|
_ -> "#{classes} is-valid"
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
classes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,10 @@ defmodule Bones73kWeb.IconHelpers do
|
||||||
opts = aria_hidden?(opts)
|
opts = aria_hidden?(opts)
|
||||||
|
|
||||||
content_tag(:svg, tag_opts(name, opts)) do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,9 @@ count_to_take = 123
|
||||||
mock_users = users_json |> File.read!() |> Jason.decode!() |> Enum.take_random(count_to_take)
|
mock_users = users_json |> File.read!() |> Jason.decode!() |> Enum.take_random(count_to_take)
|
||||||
|
|
||||||
mock_users = ~s([
|
mock_users = ~s([
|
||||||
{"email":"adam@73k.us","password":"adamadam","role":"admin","inserted_at":"2018-12-14T01:01:01Z","confirmed_at":true},
|
{"email":"adam@73k.us","password":"adamadamA1","role":"admin","inserted_at":"2018-12-14T01:01:01Z","confirmed_at":true},
|
||||||
{"email":"karen@73k.us","password":"karenkaren","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true},
|
{"email":"karen@73k.us","password":"karenkarenA1","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true},
|
||||||
{"email":"kat@73k.us","password":"katkat","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true}
|
{"email":"kat@73k.us","password":"katkatA1","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true}
|
||||||
]) |> Jason.decode!() |> Enum.concat(mock_users)
|
]) |> Jason.decode!() |> Enum.concat(mock_users)
|
||||||
|
|
||||||
mock_users =
|
mock_users =
|
||||||
|
|
Loading…
Reference in a new issue