implemented optional 'allow_registration' config, with first registered user being pre-confirmed Admin, registration unavailable after that point if allow_registration: :false
This commit is contained in:
parent
ea74a89078
commit
f27df8d676
22 changed files with 395 additions and 244 deletions
|
@ -1,12 +1,14 @@
|
|||
const togglerBtn = document.getElementById('navbarSupportedContentToggler');
|
||||
const navbarContent = document.getElementById('navbarSupportedContent');
|
||||
|
||||
navbarContent.addEventListener('show.bs.collapse', () => {
|
||||
console.log('opening navbar content');
|
||||
togglerBtn.classList.toggle('is-active');
|
||||
});
|
||||
|
||||
navbarContent.addEventListener('hide.bs.collapse', () => {
|
||||
console.log('closing navbar content');
|
||||
togglerBtn.classList.toggle('is-active');
|
||||
});
|
||||
if (navbarContent != null) {
|
||||
navbarContent.addEventListener('show.bs.collapse', () => {
|
||||
console.log('opening navbar content');
|
||||
togglerBtn.classList.toggle('is-active');
|
||||
});
|
||||
|
||||
navbarContent.addEventListener('hide.bs.collapse', () => {
|
||||
console.log('closing navbar content');
|
||||
togglerBtn.classList.toggle('is-active');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ config :shift73k,
|
|||
config :shift73k, :app_global_vars,
|
||||
time_zone: "America/New_York",
|
||||
mailer_reply_to: "reply_to@example.com",
|
||||
mailer_from: "app_name@example.com"
|
||||
mailer_from: "app_name@example.com",
|
||||
allow_registration: :true
|
||||
|
||||
# Configures the endpoint
|
||||
config :shift73k, Shift73kWeb.Endpoint,
|
||||
|
|
|
@ -108,6 +108,13 @@ defmodule Shift73k.Accounts do
|
|||
|
||||
"""
|
||||
def register_user(attrs) do
|
||||
# If attrs has atom keys, convert to string
|
||||
# If attrs don't include role, put default role
|
||||
attrs =
|
||||
attrs
|
||||
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|
||||
|> Map.put_new("role", registration_role())
|
||||
|
||||
%User{}
|
||||
|> User.registration_changeset(attrs)
|
||||
|> Repo.insert()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule Shift73kWeb.UserLive.Registration do
|
||||
use Shift73kWeb, :live_view
|
||||
|
||||
alias Shift73k.Repo
|
||||
alias Shift73k.Accounts
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
|
@ -20,9 +20,7 @@ defmodule Shift73kWeb.UserLive.Registration do
|
|||
user_id: nil,
|
||||
user_return_to: Map.get(session, "user_return_to", "/"),
|
||||
messages: [
|
||||
success: "Welcome! Your new account has been created, and you've been logged in.",
|
||||
info:
|
||||
"Some features may be unavailable until you confirm your email address. Check your inbox for instructions."
|
||||
success: "Welcome! Your new account has been created, and you've been logged in."
|
||||
]
|
||||
}
|
||||
end
|
||||
|
@ -35,19 +33,33 @@ defmodule Shift73kWeb.UserLive.Registration do
|
|||
|
||||
@impl true
|
||||
def handle_event("save", %{"user" => user_params}, socket) do
|
||||
is_first_user = !Repo.exists?(User)
|
||||
user_params
|
||||
|> Map.put("role", Accounts.registration_role())
|
||||
|> Accounts.register_user()
|
||||
|> case do
|
||||
{:ok, user} ->
|
||||
{:ok, _, %Swoosh.Email{} = _captured_email} =
|
||||
Accounts.deliver_user_confirmation_instructions(
|
||||
user,
|
||||
&Routes.user_confirmation_url(socket, :confirm, &1)
|
||||
)
|
||||
# If this is the first user, we just confirm them
|
||||
if is_first_user do
|
||||
user |> User.confirm_changeset() |> Repo.update()
|
||||
else
|
||||
# Otherwise, all new users require email confirmation so we wend instructions
|
||||
{:ok, _, %Swoosh.Email{} = _captured_email} =
|
||||
Accounts.deliver_user_confirmation_instructions(
|
||||
user,
|
||||
&Routes.user_confirmation_url(socket, :confirm, &1)
|
||||
)
|
||||
end
|
||||
|
||||
login_params =
|
||||
if is_first_user do
|
||||
socket.assigns.login_params
|
||||
else
|
||||
put_in(socket.assigns.login_params, [:messages, :info], "Some features may be unavailable until you confirm your email address. Check your inbox for instructions.")
|
||||
end
|
||||
|> put_in([:user_id], user.id)
|
||||
|
||||
socket
|
||||
|> assign(login_params: %{socket.assigns.login_params | user_id: user.id})
|
||||
|> assign(login_params: login_params)
|
||||
|> assign(trigger_submit: true)
|
||||
|> live_noreply()
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</h2>
|
||||
<p class="lead">Create an account to manage your work shifts with us.</p>
|
||||
|
||||
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save, novalidate: true, id: "reg_form"], fn f -> %>
|
||||
<.form let={f} for={@changeset} phx-change="validate" phx-submit="save" novalidate id="reg_form">
|
||||
|
||||
<%= label f, :email, class: "form-label" %>
|
||||
<div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :email)}>
|
||||
|
@ -45,7 +45,7 @@
|
|||
%>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
</.form>
|
||||
|
||||
<p>
|
||||
<%= link "Log in", to: Routes.user_session_path(@socket, :new) %> |
|
||||
|
|
|
@ -4,6 +4,9 @@ defmodule Shift73kWeb.UserLive.ResetPassword do
|
|||
alias Shift73k.Accounts
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
|
||||
@app_allow_registration @app_vars[:allow_registration]
|
||||
|
||||
@impl true
|
||||
def mount(_params, session, socket) do
|
||||
user = Accounts.get_user!(session["user_id"])
|
||||
|
@ -37,4 +40,6 @@ defmodule Shift73kWeb.UserLive.ResetPassword do
|
|||
|> assign(changeset: changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
def allow_registration, do: @app_allow_registration
|
||||
end
|
||||
|
|
|
@ -45,7 +45,9 @@
|
|||
<% end %>
|
||||
|
||||
<p class="mt-3 is-pulled-right">
|
||||
<%= link "Register", to: Routes.user_registration_path(@socket, :new) %> |
|
||||
<%= if allow_registration() do %>
|
||||
<%= link "Register", to: Routes.user_registration_path(@socket, :new) %> |
|
||||
<% end %>
|
||||
<%= link "Log in", to: Routes.user_session_path(@socket, :new) %>
|
||||
</p>
|
||||
|
||||
|
|
35
lib/shift73k_web/plugs/ensure_allow_registration_plug.ex
Normal file
35
lib/shift73k_web/plugs/ensure_allow_registration_plug.ex
Normal file
|
@ -0,0 +1,35 @@
|
|||
defmodule Shift73kWeb.EnsureAllowRegistrationPlug do
|
||||
@moduledoc """
|
||||
This plug ensures that there is at least one known User.
|
||||
"""
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
|
||||
alias Shift73k.Repo
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
|
||||
@app_allow_registration @app_vars[:allow_registration]
|
||||
|
||||
@doc false
|
||||
@spec init(any()) :: any()
|
||||
def init(config), do: config
|
||||
|
||||
@doc false
|
||||
@spec call(Conn.t(), atom() | [atom()]) :: Conn.t()
|
||||
def call(conn, _opts) do
|
||||
# If there aren't even any users, or registration is allowed
|
||||
if !Repo.exists?(User) || @app_allow_registration do
|
||||
# We will allow registration
|
||||
conn
|
||||
else
|
||||
# Otherwise,
|
||||
# if app is configured to not allow registration,
|
||||
# and there is a user,
|
||||
# then we redirect to root URL
|
||||
conn
|
||||
|> redirect(to: "/")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -27,8 +27,7 @@ defmodule Shift73kWeb.EnsureRolePlug do
|
|||
def call(conn, roles) do
|
||||
user_token = get_session(conn, :user_token)
|
||||
|
||||
(user_token &&
|
||||
Accounts.get_user_by_session_token(user_token))
|
||||
(user_token && Accounts.get_user_by_session_token(user_token))
|
||||
|> has_role?(roles)
|
||||
|> maybe_halt(conn)
|
||||
end
|
||||
|
|
30
lib/shift73k_web/plugs/ensure_user_exist_plug.ex
Normal file
30
lib/shift73k_web/plugs/ensure_user_exist_plug.ex
Normal file
|
@ -0,0 +1,30 @@
|
|||
defmodule Shift73kWeb.EnsureUserExistPlug do
|
||||
@moduledoc """
|
||||
This plug ensures that there is at least one known User.
|
||||
"""
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
|
||||
alias Shift73k.Repo
|
||||
alias Shift73k.Accounts.User
|
||||
alias Shift73kWeb.Router.Helpers, as: Routes
|
||||
|
||||
@doc false
|
||||
@spec init(any()) :: any()
|
||||
def init(config), do: config
|
||||
|
||||
@doc false
|
||||
@spec call(Conn.t(), atom() | [atom()]) :: Conn.t()
|
||||
def call(conn, _opts) do
|
||||
# If there aren't even any users,
|
||||
if !Repo.exists?(User) do
|
||||
# We're just going to redirect to registration
|
||||
conn
|
||||
|> redirect(to: Routes.user_registration_path(conn, :new))
|
||||
|> halt()
|
||||
else
|
||||
# Otherwise we proceed as normal
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,27 +2,37 @@ defmodule Shift73kWeb.Router do
|
|||
use Shift73kWeb, :router
|
||||
import Shift73kWeb.UserAuth
|
||||
alias Shift73kWeb.EnsureRolePlug
|
||||
alias Shift73kWeb.EnsureUserExistPlug
|
||||
alias Shift73kWeb.EnsureAllowRegistrationPlug
|
||||
|
||||
pipeline :browser do
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
plug(:fetch_live_flash)
|
||||
plug(:put_root_layout, {Shift73kWeb.LayoutView, :root})
|
||||
plug(:protect_from_forgery)
|
||||
plug(:put_secure_browser_headers)
|
||||
plug(:fetch_current_user)
|
||||
plug :accepts, ["html"]
|
||||
plug :fetch_session
|
||||
plug :fetch_live_flash
|
||||
plug :put_root_layout, {Shift73kWeb.LayoutView, :root}
|
||||
plug :protect_from_forgery
|
||||
plug :put_secure_browser_headers
|
||||
plug :fetch_current_user
|
||||
end
|
||||
|
||||
pipeline :user do
|
||||
plug(EnsureRolePlug, [:admin, :manager, :user])
|
||||
pipeline :ensure_role_user do
|
||||
plug EnsureRolePlug, [:admin, :manager, :user]
|
||||
end
|
||||
|
||||
pipeline :manager do
|
||||
plug(EnsureRolePlug, [:admin, :manager])
|
||||
pipeline :ensure_user_exist do
|
||||
plug EnsureUserExistPlug
|
||||
end
|
||||
|
||||
pipeline :admin do
|
||||
plug(EnsureRolePlug, :admin)
|
||||
pipeline :ensure_allow_registration do
|
||||
plug EnsureAllowRegistrationPlug
|
||||
end
|
||||
|
||||
pipeline :ensure_role_manager do
|
||||
plug EnsureRolePlug, [:admin, :manager]
|
||||
end
|
||||
|
||||
pipeline :ensure_role_admin do
|
||||
plug EnsureRolePlug, :admin
|
||||
end
|
||||
|
||||
# Enables the Swoosh mailbox preview in development.
|
||||
|
@ -38,49 +48,54 @@ defmodule Shift73kWeb.Router do
|
|||
end
|
||||
|
||||
scope "/", Shift73kWeb do
|
||||
pipe_through([:browser])
|
||||
pipe_through([:browser, :ensure_user_exist])
|
||||
|
||||
get("/", Redirector, to: "/assign")
|
||||
get "/", Redirector, to: "/assign"
|
||||
end
|
||||
|
||||
scope "/", Shift73kWeb do
|
||||
pipe_through([:browser, :redirect_if_user_is_authenticated])
|
||||
pipe_through [:browser, :redirect_if_user_is_authenticated, :ensure_allow_registration]
|
||||
|
||||
get "/users/register", UserRegistrationController, :new
|
||||
end
|
||||
|
||||
scope "/", Shift73kWeb do
|
||||
pipe_through [:browser, :redirect_if_user_is_authenticated, :ensure_user_exist]
|
||||
|
||||
# session routes, irrelevant if user is authenticated
|
||||
get("/users/register", UserRegistrationController, :new)
|
||||
get("/users/log_in", UserSessionController, :new)
|
||||
post("/users/log_in", UserSessionController, :create)
|
||||
get("/users/reset_password", UserResetPasswordController, :new)
|
||||
post("/users/reset_password", UserResetPasswordController, :create)
|
||||
get("/users/reset_password/:token", UserResetPasswordController, :edit)
|
||||
get "/users/log_in", UserSessionController, :new
|
||||
post "/users/log_in", UserSessionController, :create
|
||||
get "/users/reset_password", UserResetPasswordController, :new
|
||||
post "/users/reset_password", UserResetPasswordController, :create
|
||||
get "/users/reset_password/:token", UserResetPasswordController, :edit
|
||||
end
|
||||
|
||||
scope "/", Shift73kWeb do
|
||||
pipe_through([:browser, :require_authenticated_user])
|
||||
pipe_through [:browser, :require_authenticated_user]
|
||||
|
||||
# user settings (change email, password, calendar week start, etc)
|
||||
live("/users/settings", UserLive.Settings, :edit)
|
||||
live "/users/settings", UserLive.Settings, :edit
|
||||
|
||||
# confirm email by token
|
||||
get("/users/settings/confirm_email/:token", UserSettingsController, :confirm_email)
|
||||
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
|
||||
end
|
||||
|
||||
scope "/", Shift73kWeb do
|
||||
pipe_through([:browser])
|
||||
pipe_through [:browser, :ensure_user_exist]
|
||||
|
||||
# session paths
|
||||
delete("/users/log_out", UserSessionController, :delete)
|
||||
get("/users/force_logout", UserSessionController, :force_logout)
|
||||
get("/users/confirm", UserConfirmationController, :new)
|
||||
post("/users/confirm", UserConfirmationController, :create)
|
||||
get("/users/confirm/:token", UserConfirmationController, :confirm)
|
||||
delete "/users/log_out", UserSessionController, :delete
|
||||
get "/users/force_logout", UserSessionController, :force_logout
|
||||
get "/users/confirm", UserConfirmationController, :new
|
||||
post "/users/confirm", UserConfirmationController, :create
|
||||
get "/users/confirm/:token", UserConfirmationController, :confirm
|
||||
|
||||
# ics/ical route for user's shifts
|
||||
get("/ics/:slug", UserShiftsIcsController, :index)
|
||||
get "/ics/:slug", UserShiftsIcsController, :index
|
||||
end
|
||||
|
||||
scope "/", Shift73kWeb do
|
||||
pipe_through([:browser, :require_authenticated_user, :user])
|
||||
pipe_through [:browser, :require_authenticated_user, :ensure_role_user]
|
||||
|
||||
live "/templates", ShiftTemplateLive.Index, :index
|
||||
live "/templates/new", ShiftTemplateLive.Index, :new
|
||||
|
@ -98,16 +113,16 @@ defmodule Shift73kWeb.Router do
|
|||
end
|
||||
|
||||
# scope "/", Shift73kWeb do
|
||||
# pipe_through([:browser, :require_authenticated_user, :admin])
|
||||
# pipe_through([:browser, :require_authenticated_user, :ensure_role_admin])
|
||||
# end
|
||||
|
||||
# Users Management
|
||||
scope "/users", Shift73kWeb do
|
||||
pipe_through([:browser, :require_authenticated_user, :manager, :require_email_confirmed])
|
||||
pipe_through [:browser, :require_authenticated_user, :ensure_role_manager, :require_email_confirmed]
|
||||
|
||||
live("/", UserManagementLive.Index, :index)
|
||||
live("/new", UserManagementLive.Index, :new)
|
||||
live("/edit/:id", UserManagementLive.Index, :edit)
|
||||
live "/", UserManagementLive.Index, :index
|
||||
live "/new", UserManagementLive.Index, :new
|
||||
live "/edit/:id", UserManagementLive.Index, :edit
|
||||
# resources "/", UserManagementController, only: [:new, :create, :edit, :update]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,79 +8,63 @@
|
|||
<% end %>
|
||||
</h1>
|
||||
|
||||
<%= if @current_user do %>
|
||||
<%# If there's a current user,
|
||||
OR if there are users & we allow registration,
|
||||
THEN we will show a full menu configuration %>
|
||||
|
||||
<%= if @current_user || (Repo.exists?(User) && allow_registration()) do %>
|
||||
|
||||
<button class="hamburger hamburger--squeeze collapsed navbar-toggler" id="navbarSupportedContentToggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="hamburger-box d-flex">
|
||||
<span class="hamburger-inner"></span>
|
||||
</span>
|
||||
</button>
|
||||
<% else %>
|
||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light d-block d-lg-none") do %>
|
||||
<i class="bi bi-door-open me-1"></i> Log in
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
|
||||
<%# nav LEFT items %>
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<%# nav LEFT items %>
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<%#= if @current_user do %>
|
||||
<%# <li class="nav-item"> %>
|
||||
<%#= link nav_link_opts(@conn, to: Routes.shift_template_index_path(@conn, :index), class: "nav-link") do %>
|
||||
<%#= icon_div @conn, "bi-clock-history", [class: "icon baseline me-1"] %>
|
||||
<%# Templates %>
|
||||
<%# end %>
|
||||
<%# </li> %>
|
||||
<%# end %>
|
||||
</ul>
|
||||
|
||||
<%# normal navbar link example %>
|
||||
<%# <li class="nav-item"> %>
|
||||
<%#= link "Properties", nav_link_opts(@conn, to: Routes.property_index_path(@conn, :index), class: "nav-link") %>
|
||||
<%# </li> %>
|
||||
|
||||
<%# ACTIVE page link example %>
|
||||
<%# <li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">Home</a>
|
||||
</li> %>
|
||||
<%# nav RIGHT items %>
|
||||
<ul class="navbar-nav">
|
||||
|
||||
<%# DISABLED page link example %>
|
||||
<%# <li class="nav-item">
|
||||
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
|
||||
</li> %>
|
||||
<%= if @current_user do %>
|
||||
|
||||
<%# normal dropdown menu example %>
|
||||
<%# <li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownExample" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
|
||||
<ul class="dropdown-menu" aria-labelledby="navbarDropdownExample">
|
||||
<li><a class="dropdown-item" href="#">Action</a></li>
|
||||
<li><a class="dropdown-item" href="#">Another action</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
</ul>
|
||||
</li> %>
|
||||
<%= render "navbar/_shifts_menu.html", assigns %>
|
||||
|
||||
</ul>
|
||||
<%= render "navbar/_user_menu.html", assigns %>
|
||||
|
||||
<% else %>
|
||||
|
||||
<%# nav RIGHT items %>
|
||||
<ul class="navbar-nav">
|
||||
<%= render "navbar/_nouser_menu.html", assigns %>
|
||||
|
||||
<%= if @current_user do %>
|
||||
|
||||
<%= render "navbar/_shifts_menu.html", assigns %>
|
||||
|
||||
<%= render "navbar/_user_menu.html", assigns %>
|
||||
|
||||
<% else %>
|
||||
|
||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light d-none d-lg-block") do %>
|
||||
<i class="bi bi-door-open me-1"></i> Log in
|
||||
<% end %>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<%# If there's no current user,
|
||||
AND:
|
||||
There are no users -- [REGISTER]
|
||||
OR no registration allowed -- [LOG IN] %>
|
||||
<%= else %>
|
||||
|
||||
<%= if !Repo.exists?(User) || allow_registration() do %>
|
||||
<%= link nav_link_opts(@conn, to: Routes.user_registration_path(@conn, :new), class: "btn btn-outline-light") do %>
|
||||
<i class="bi bi-journal-plus"></i> Register
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light") do %>
|
||||
<i class="bi bi-door-open"></i> Log in
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
</ul>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<li class="nav-item dropdown">
|
||||
|
||||
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-person-circle me-1"></i> Hello?
|
||||
</a>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownUserMenu">
|
||||
|
||||
<li>
|
||||
<%= link nav_link_opts(@conn, to: Routes.user_registration_path(@conn, :new), class: "dropdown-item") do %>
|
||||
<i class="bi bi-journal-plus me-1"></i> Register
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "dropdown-item") do %>
|
||||
<i class="bi bi-door-open me-1"></i> Log in
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
|
@ -1,7 +1,7 @@
|
|||
<li class="nav-item dropdown">
|
||||
|
||||
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-person-circle me-1"></i> <%= @current_user && "Hello!" || "Hello?" %>
|
||||
<i class="bi bi-person-circle me-1"></i> Hello!
|
||||
</a>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownUserMenu">
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= if allow_registration() do %>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||
<% end %>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
|
||||
</p>
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||
<%= if allow_registration() do %>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||
<% end %>
|
||||
<%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@
|
|||
<% end %>
|
||||
|
||||
<p>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||
<%= if allow_registration() do %>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new) %> |
|
||||
<% end %>
|
||||
<%= link "Forgot your password?", to: Routes.user_reset_password_path(@conn, :new) %>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
defmodule Shift73kWeb.LayoutView do
|
||||
use Shift73kWeb, :view
|
||||
|
||||
alias Shift73k.Repo
|
||||
alias Shift73k.Accounts.User
|
||||
alias Shift73kWeb.Roles
|
||||
|
||||
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
|
||||
@app_allow_registration @app_vars[:allow_registration]
|
||||
|
||||
# With a Vite.js-based workflow, we will import different asset files in development
|
||||
# and in production builds. Therefore, we will need a way to conditionally render
|
||||
# <script> tags based on Mix environment. However, since Mix is not available in
|
||||
|
@ -11,6 +14,8 @@ defmodule Shift73kWeb.LayoutView do
|
|||
@env Mix.env() # remember value at compile time
|
||||
def dev_env?, do: @env == :dev
|
||||
|
||||
def allow_registration, do: @app_allow_registration
|
||||
|
||||
def nav_link_opts(conn, opts) do
|
||||
case Keyword.get(opts, :to) == Phoenix.Controller.current_path(conn) do
|
||||
false -> opts
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
defmodule Shift73kWeb.UserConfirmationView do
|
||||
use Shift73kWeb, :view
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
|
||||
@app_allow_registration @app_vars[:allow_registration]
|
||||
|
||||
def allow_registration, do: @app_allow_registration
|
||||
end
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
defmodule Shift73kWeb.UserResetPasswordView do
|
||||
use Shift73kWeb, :view
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
|
||||
@app_allow_registration @app_vars[:allow_registration]
|
||||
|
||||
def allow_registration, do: @app_allow_registration
|
||||
end
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
defmodule Shift73kWeb.UserSessionView do
|
||||
use Shift73kWeb, :view
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
|
||||
@app_allow_registration @app_vars[:allow_registration]
|
||||
|
||||
def allow_registration, do: @app_allow_registration
|
||||
end
|
||||
|
|
|
@ -14,141 +14,150 @@ alias Shift73k.Repo
|
|||
alias Shift73k.Accounts
|
||||
alias Shift73k.Accounts.User
|
||||
|
||||
############################################################################
|
||||
## INSERTING MOCK USER DATA
|
||||
|
||||
{:ok, _admin} =
|
||||
Accounts.register_user(%{
|
||||
email: "admin@company.com",
|
||||
password: "123456789abC",
|
||||
password_confirmation: "123456789abC",
|
||||
role: Accounts.registration_role()
|
||||
})
|
||||
if Mix.env() == :dev do
|
||||
|
||||
{:ok, _user_1} =
|
||||
Accounts.register_user(%{
|
||||
email: "user1@company.com",
|
||||
password: "123456789abC",
|
||||
password_confirmation: "123456789abC",
|
||||
role: Accounts.registration_role()
|
||||
})
|
||||
if System.get_env("ECTO_SEED_DB") do
|
||||
|
||||
{:ok, _user_2} =
|
||||
Accounts.register_user(%{
|
||||
email: "user2@company.com",
|
||||
password: "123456789abC",
|
||||
password_confirmation: "123456789abC",
|
||||
role: Accounts.registration_role()
|
||||
})
|
||||
############################################################################
|
||||
## INSERTING MOCK USER DATA
|
||||
|
||||
# if Mix.env() == :dev do
|
||||
this_path = Path.dirname(__ENV__.file)
|
||||
users_json = Path.join(this_path, "MOCK_DATA_users.json")
|
||||
{:ok, _admin} =
|
||||
Accounts.register_user(%{
|
||||
email: "admin@company.com",
|
||||
password: "123456789abC",
|
||||
password_confirmation: "123456789abC",
|
||||
role: Accounts.registration_role()
|
||||
})
|
||||
|
||||
count_to_take = 15
|
||||
{:ok, _user_1} =
|
||||
Accounts.register_user(%{
|
||||
email: "user1@company.com",
|
||||
password: "123456789abC",
|
||||
password_confirmation: "123456789abC",
|
||||
role: Accounts.registration_role()
|
||||
})
|
||||
|
||||
mock_users = users_json |> File.read!() |> Jason.decode!() |> Enum.take_random(count_to_take)
|
||||
{:ok, _user_2} =
|
||||
Accounts.register_user(%{
|
||||
email: "user2@company.com",
|
||||
password: "123456789abC",
|
||||
password_confirmation: "123456789abC",
|
||||
role: Accounts.registration_role()
|
||||
})
|
||||
|
||||
extra_mock_users = ~s([
|
||||
{"email":"adam@73k.us","password":"adamadamA1","role":"admin","inserted_at":"2018-12-14T01:01:01Z","confirmed_at":true},
|
||||
{"email":"kat@73k.us","password":"katkatA1","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true},
|
||||
{"email":"babka@73k.us","password":"Babka2020","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
|
||||
{"email":"malcolm@73k.us","password":"Malc2018","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
|
||||
{"email":"casio@73k.us","password":"Casio2011","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false}
|
||||
])
|
||||
# if Mix.env() == :dev do
|
||||
this_path = Path.dirname(__ENV__.file)
|
||||
users_json = Path.join(this_path, "MOCK_DATA_users.json")
|
||||
|
||||
# for random week_start_at values
|
||||
[head | tail] = Shift73k.weekdays()
|
||||
week_starts = [head | Enum.drop(tail, 4)]
|
||||
count_to_take = 15
|
||||
|
||||
mock_users =
|
||||
extra_mock_users
|
||||
|> Jason.decode!()
|
||||
|> Stream.concat(mock_users)
|
||||
|> Enum.map(fn e ->
|
||||
add_dt = NaiveDateTime.from_iso8601!(e["inserted_at"])
|
||||
mock_users = users_json |> File.read!() |> Jason.decode!() |> Enum.take_random(count_to_take)
|
||||
|
||||
%{
|
||||
email: e["email"],
|
||||
role: String.to_existing_atom(e["role"]),
|
||||
hashed_password: Bcrypt.hash_pwd_salt(e["password"]),
|
||||
week_start_at: Enum.at(week_starts, Enum.random(0..2)),
|
||||
calendar_slug: Ecto.UUID.generate(),
|
||||
inserted_at: add_dt,
|
||||
updated_at: add_dt,
|
||||
confirmed_at: (e["confirmed_at"] && NaiveDateTime.add(add_dt, 300, :second)) || nil
|
||||
}
|
||||
end)
|
||||
extra_mock_users = ~s([
|
||||
{"email":"adam@73k.us","password":"adamadamA1","role":"admin","inserted_at":"2018-12-14T01:01:01Z","confirmed_at":true},
|
||||
{"email":"kat@73k.us","password":"katkatA1","role":"manager","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":true},
|
||||
{"email":"babka@73k.us","password":"Babka2020","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
|
||||
{"email":"malcolm@73k.us","password":"Malc2018","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false},
|
||||
{"email":"casio@73k.us","password":"Casio2011","role":"user","inserted_at":"2018-12-14T01:06:01Z","confirmed_at":false}
|
||||
])
|
||||
|
||||
Repo.insert_all(User, mock_users)
|
||||
# end
|
||||
# for random week_start_at values
|
||||
[head | tail] = Shift73k.weekdays()
|
||||
week_starts = [head | Enum.drop(tail, 4)]
|
||||
|
||||
#####
|
||||
# shift tepmlates
|
||||
alias Shift73k.Shifts.Templates.ShiftTemplate
|
||||
mock_users =
|
||||
extra_mock_users
|
||||
|> Jason.decode!()
|
||||
|> Stream.concat(mock_users)
|
||||
|> Enum.map(fn e ->
|
||||
add_dt = NaiveDateTime.from_iso8601!(e["inserted_at"])
|
||||
|
||||
shifts_json = Path.join(this_path, "MOCK_DATA_shift-templates.json")
|
||||
mock_shifts = shifts_json |> File.read!() |> Jason.decode!()
|
||||
%{
|
||||
email: e["email"],
|
||||
role: String.to_existing_atom(e["role"]),
|
||||
hashed_password: Bcrypt.hash_pwd_salt(e["password"]),
|
||||
week_start_at: Enum.at(week_starts, Enum.random(0..2)),
|
||||
calendar_slug: Ecto.UUID.generate(),
|
||||
inserted_at: add_dt,
|
||||
updated_at: add_dt,
|
||||
confirmed_at: (e["confirmed_at"] && NaiveDateTime.add(add_dt, 300, :second)) || nil
|
||||
}
|
||||
end)
|
||||
|
||||
Repo.insert_all(User, mock_users)
|
||||
# end
|
||||
|
||||
#####
|
||||
# shift tepmlates
|
||||
alias Shift73k.Shifts.Templates.ShiftTemplate
|
||||
|
||||
shifts_json = Path.join(this_path, "MOCK_DATA_shift-templates.json")
|
||||
mock_shifts = shifts_json |> File.read!() |> Jason.decode!()
|
||||
|
||||
time_from_mock = fn mock_time ->
|
||||
case String.length(mock_time) do
|
||||
4 -> Time.from_iso8601!("T0#{mock_time}:00")
|
||||
5 -> Time.from_iso8601!("T#{mock_time}:00")
|
||||
end
|
||||
end
|
||||
|
||||
seconds_day = 86_400
|
||||
seconds_days_14 = seconds_day * 14
|
||||
seconds_half_day = Integer.floor_div(seconds_day, 2)
|
||||
|
||||
for user <- Accounts.list_users() do
|
||||
user_shifts =
|
||||
mock_shifts
|
||||
|> Enum.take_random(:rand.uniform(15) + 5)
|
||||
|> Enum.map(fn e ->
|
||||
seconds_to_add = :rand.uniform(seconds_days_14) + seconds_half_day
|
||||
add_dt = NaiveDateTime.add(user.inserted_at, seconds_to_add)
|
||||
time_start = time_from_mock.(e["time_start"])
|
||||
shift_len_min = e["length_minutes"] || 0
|
||||
shift_length = e["length_hours"] * 60 * 60 + shift_len_min * 60
|
||||
time_end = Time.add(time_start, shift_length) |> Time.truncate(:second)
|
||||
|
||||
%{
|
||||
subject: e["subject"],
|
||||
description: e["description"],
|
||||
location: e["location"],
|
||||
time_zone: Tzdata.zone_list() |> Enum.random(),
|
||||
time_start: time_start,
|
||||
time_end: time_end,
|
||||
user_id: user.id,
|
||||
inserted_at: add_dt,
|
||||
updated_at: add_dt
|
||||
}
|
||||
end)
|
||||
|
||||
Repo.insert_all(ShiftTemplate, user_shifts)
|
||||
end
|
||||
|
||||
#####
|
||||
# insert shifts for each user?
|
||||
alias Shift73k.Shifts
|
||||
alias Shift73k.Shifts.Templates
|
||||
|
||||
for user <- Accounts.list_users() do
|
||||
# build a date range for the time from 120 days ago to 120 days from now
|
||||
today = Date.utc_today()
|
||||
date_range = Date.range(Date.add(today, -120), Date.add(today, 120))
|
||||
|
||||
# get 3 random shift templates for user
|
||||
st_list = Templates.list_shift_templates_by_user(user.id) |> Enum.take_random(3)
|
||||
|
||||
for st <- st_list do
|
||||
days_to_schedule = Enum.take_random(date_range, 47)
|
||||
shift_data = ShiftTemplate.attrs(st)
|
||||
|
||||
days_to_schedule
|
||||
|> Stream.map(&Map.put(shift_data, :date, &1))
|
||||
|> Enum.map(&Repo.timestamp/1)
|
||||
|> Shifts.create_multiple()
|
||||
end
|
||||
end
|
||||
|
||||
time_from_mock = fn mock_time ->
|
||||
case String.length(mock_time) do
|
||||
4 -> Time.from_iso8601!("T0#{mock_time}:00")
|
||||
5 -> Time.from_iso8601!("T#{mock_time}:00")
|
||||
end
|
||||
end
|
||||
|
||||
seconds_day = 86_400
|
||||
seconds_days_14 = seconds_day * 14
|
||||
seconds_half_day = Integer.floor_div(seconds_day, 2)
|
||||
|
||||
for user <- Accounts.list_users() do
|
||||
user_shifts =
|
||||
mock_shifts
|
||||
|> Enum.take_random(:rand.uniform(15) + 5)
|
||||
|> Enum.map(fn e ->
|
||||
seconds_to_add = :rand.uniform(seconds_days_14) + seconds_half_day
|
||||
add_dt = NaiveDateTime.add(user.inserted_at, seconds_to_add)
|
||||
time_start = time_from_mock.(e["time_start"])
|
||||
shift_len_min = e["length_minutes"] || 0
|
||||
shift_length = e["length_hours"] * 60 * 60 + shift_len_min * 60
|
||||
time_end = Time.add(time_start, shift_length) |> Time.truncate(:second)
|
||||
|
||||
%{
|
||||
subject: e["subject"],
|
||||
description: e["description"],
|
||||
location: e["location"],
|
||||
time_zone: Tzdata.zone_list() |> Enum.random(),
|
||||
time_start: time_start,
|
||||
time_end: time_end,
|
||||
user_id: user.id,
|
||||
inserted_at: add_dt,
|
||||
updated_at: add_dt
|
||||
}
|
||||
end)
|
||||
|
||||
Repo.insert_all(ShiftTemplate, user_shifts)
|
||||
end
|
||||
|
||||
#####
|
||||
# insert shifts for each user?
|
||||
alias Shift73k.Shifts
|
||||
alias Shift73k.Shifts.Templates
|
||||
|
||||
for user <- Accounts.list_users() do
|
||||
# build a date range for the time from 120 days ago to 120 days from now
|
||||
today = Date.utc_today()
|
||||
date_range = Date.range(Date.add(today, -120), Date.add(today, 120))
|
||||
|
||||
# get 3 random shift templates for user
|
||||
st_list = Templates.list_shift_templates_by_user(user.id) |> Enum.take_random(3)
|
||||
|
||||
for st <- st_list do
|
||||
days_to_schedule = Enum.take_random(date_range, 47)
|
||||
shift_data = ShiftTemplate.attrs(st)
|
||||
|
||||
days_to_schedule
|
||||
|> Stream.map(&Map.put(shift_data, :date, &1))
|
||||
|> Enum.map(&Repo.timestamp/1)
|
||||
|> Shifts.create_multiple()
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue