much progress on shift assigning & app navigation
This commit is contained in:
parent
4541070f75
commit
8957f2d1dd
33 changed files with 363 additions and 330 deletions
|
@ -22,13 +22,26 @@ defmodule Shift73k.Shifts do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_shifts_by_user_between_dates(user_id, start_date, end_date) do
|
def list_shifts_by_user_between_dates(user_id, start_date, end_date) do
|
||||||
q = from(
|
from(s in Shift)
|
||||||
s in Shift,
|
|> select([s], %{
|
||||||
select: %{date: s.date, subject: s.subject, time_start: s.time_start, time_end: s.time_end},
|
date: s.date,
|
||||||
where: s.user_id == ^user_id and s.date >= ^start_date and s.date < ^end_date,
|
subject: s.subject,
|
||||||
order_by: [s.date, s.time_start]
|
time_start: s.time_start,
|
||||||
)
|
time_end: s.time_end
|
||||||
Repo.all(q)
|
})
|
||||||
|
|> where([s], s.user_id == ^user_id and s.date >= ^start_date and s.date <= ^end_date)
|
||||||
|
|> order_by([s], [s.date, s.time_start])
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp query_shifts_by_user_on_list_of_dates(user_id, date_list) do
|
||||||
|
from(s in Shift)
|
||||||
|
|> where([s], s.user_id == ^user_id and s.date in ^date_list)
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_shifts_by_user_on_list_of_dates(user_id, date_list) do
|
||||||
|
query_shifts_by_user_on_list_of_dates(user_id, date_list)
|
||||||
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -99,6 +112,11 @@ defmodule Shift73k.Shifts do
|
||||||
Repo.delete(shift)
|
Repo.delete(shift)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_shifts_by_user_on_list_of_dates(user_id, date_list) do
|
||||||
|
query_shifts_by_user_on_list_of_dates(user_id, date_list)
|
||||||
|
|> Repo.delete_all()
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns an `%Ecto.Changeset{}` for tracking shift changes.
|
Returns an `%Ecto.Changeset{}` for tracking shift changes.
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,11 @@ defmodule Shift73k.Shifts.Templates do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_shift_templates_by_user_id(user_id) do
|
def list_shift_templates_by_user_id(user_id) do
|
||||||
q = from s in ShiftTemplate, where: s.user_id == ^user_id, order_by: [s.subject, s.time_start]
|
q =
|
||||||
|
from s in ShiftTemplate,
|
||||||
|
where: s.user_id == ^user_id,
|
||||||
|
order_by: [fragment("lower(?)", s.subject), s.time_start]
|
||||||
|
|
||||||
Repo.all(q)
|
Repo.all(q)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
||||||
field :time_end, :time, default: ~T[17:00:00]
|
field :time_end, :time, default: ~T[17:00:00]
|
||||||
|
|
||||||
belongs_to(:user, Shift73k.Accounts.User)
|
belongs_to(:user, Shift73k.Accounts.User)
|
||||||
|
has_one(:is_fave_of_user, Shift73k.Accounts.User, foreign_key: :fave_shift_template_id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
@ -58,7 +59,9 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> validate_inclusion(:time_zone, Timex.timezones(), message: "must be a valid IANA tz database time zone")
|
|> validate_inclusion(:time_zone, Timex.timezones(),
|
||||||
|
message: "must be a valid IANA tz database time zone"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp time_start_from_attrs(%{"time_start" => time_start}), do: time_start
|
defp time_start_from_attrs(%{"time_start" => time_start}), do: time_start
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
defmodule Shift73kWeb.OtherController do
|
|
||||||
use Shift73kWeb, :controller
|
|
||||||
|
|
||||||
def index(conn, _params) do
|
|
||||||
conn
|
|
||||||
|> put_flash(:success, "Log in was a success. Good for you.")
|
|
||||||
|> put_flash(:error, "Lorem ipsum dolor sit amet consectetur adipisicing elit.")
|
|
||||||
|> put_flash(
|
|
||||||
:info,
|
|
||||||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatibus dolore sunt quia aperiam sint id reprehenderit? Dolore incidunt alias inventore accusantium nulla optio, ducimus eius aliquam hic, pariatur voluptate distinctio."
|
|
||||||
)
|
|
||||||
|> put_flash(:warning, "Oh no, there's nothing to worry about!")
|
|
||||||
|> put_flash(:primary, "Something in the brand color.")
|
|
||||||
|> render("index.html")
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -16,10 +16,10 @@ defmodule Shift73kWeb.ModalComponent do
|
||||||
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><%= Keyword.get(@opts, :title, "Modal title") %></h5>
|
<h5 class="modal-title"><%= Keyword.get(@opts, :title, "Modal title") %></h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" phx-click="hide" phx-target="<%= @myself %>" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= live_component @socket, @component, @opts %>
|
<%= live_component @socket, @component, Keyword.put(@opts, :modal_id, @id) %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +27,11 @@ defmodule Shift73kWeb.ModalComponent do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def update(assigns, socket) do
|
||||||
|
socket |> assign(assigns) |> live_okreply()
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("close", _, socket) do
|
def handle_event("close", _, socket) do
|
||||||
send(self(), {:close_modal, true})
|
send(self(), {:close_modal, true})
|
||||||
|
@ -35,6 +40,6 @@ defmodule Shift73kWeb.ModalComponent do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("hide", _, socket) do
|
def handle_event("hide", _, socket) do
|
||||||
{:noreply, push_event(socket, "modal-please-hide", %{})}
|
socket |> push_event("modal-please-hide", %{}) |> live_noreply()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
defmodule Shift73kWeb.PageLive do
|
|
||||||
use Shift73kWeb, :live_view
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def mount(_params, session, socket) do
|
|
||||||
socket = assign_defaults(socket, session)
|
|
||||||
{:ok, assign(socket, query: "", results: %{})}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("suggest", %{"q" => query}, socket) do
|
|
||||||
{:noreply, assign(socket, results: search(query), query: query)}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("search", %{"q" => query}, socket) do
|
|
||||||
case search(query) do
|
|
||||||
%{^query => vsn} ->
|
|
||||||
{:noreply, redirect(socket, external: "https://hexdocs.pm/#{query}/#{vsn}")}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> put_flash(:error, "No dependencies found matching \"#{query}\"")
|
|
||||||
|> assign(results: %{}, query: query)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp search(query) do
|
|
||||||
if not Shift73kWeb.Endpoint.config(:code_reloader) do
|
|
||||||
raise "action disabled when not in development"
|
|
||||||
end
|
|
||||||
|
|
||||||
for {app, desc, vsn} <- Application.started_applications(),
|
|
||||||
app = to_string(app),
|
|
||||||
String.starts_with?(app, query) and not List.starts_with?(desc, ~c"ERTS"),
|
|
||||||
into: %{},
|
|
||||||
do: {app, vsn}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,49 +0,0 @@
|
||||||
<section class="phx-hero">
|
|
||||||
<h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
|
|
||||||
<p>Peace of mind from prototype to production</p>
|
|
||||||
|
|
||||||
<form phx-change="suggest" phx-submit="search">
|
|
||||||
<input type="text" name="q" value="<%= @query %>" placeholder="Live dependency search" list="results" autocomplete="off"/>
|
|
||||||
<datalist id="results">
|
|
||||||
<%= for {app, _vsn} <- @results do %>
|
|
||||||
<option value="<%= app %>"><%= app %></option>
|
|
||||||
<% end %>
|
|
||||||
</datalist>
|
|
||||||
<button type="submit" phx-disable-with="Searching...">Go to Hexdocs</button>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="row align-items-start">
|
|
||||||
<article class="col">
|
|
||||||
<h2>Resources</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="https://hexdocs.pm/phoenix/overview.html">Guides & Docs</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/phoenixframework/phoenix">Source</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/phoenixframework/phoenix/blob/v1.5/CHANGELOG.md">v1.5 Changelog</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
<article class="col">
|
|
||||||
<h2>Help</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="https://elixirforum.com/c/phoenix-forum">Forum</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://webchat.freenode.net/?channels=elixir-lang">#elixir-lang on Freenode IRC</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://twitter.com/elixirphoenix">Twitter @elixirphoenix</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://elixir-slackin.herokuapp.com/">Elixir on Slack</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
</section>
|
|
42
lib/shift73k_web/live/shift_assign_live/delete_component.ex
Normal file
42
lib/shift73k_web/live/shift_assign_live/delete_component.ex
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
defmodule Shift73kWeb.ShiftAssignLive.DeleteComponent do
|
||||||
|
use Shift73kWeb, :live_component
|
||||||
|
|
||||||
|
alias Shift73k.Shifts
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def update(assigns, socket) do
|
||||||
|
socket
|
||||||
|
|> assign(assigns)
|
||||||
|
|> assign_dates()
|
||||||
|
|> live_okreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assign_dates(%{assigns: %{delete_days_shifts: daylist}} = socket) do
|
||||||
|
date_list = Enum.map(daylist, &Date.from_iso8601!/1)
|
||||||
|
year_map = Enum.group_by(date_list, fn d -> d.year end)
|
||||||
|
assign(socket, date_list: date_list, date_map: build_date_map(year_map))
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_date_map(year_map) do
|
||||||
|
year_map
|
||||||
|
|> Map.keys()
|
||||||
|
|> Enum.reduce(year_map, fn y, acc ->
|
||||||
|
Map.put(acc, y, Enum.group_by(acc[y], fn d -> d.month end))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("confirm-delete-days-shifts", _params, socket) do
|
||||||
|
user = socket.assigns.current_user
|
||||||
|
date_list = socket.assigns.date_list
|
||||||
|
{n, _} = Shifts.delete_shifts_by_user_on_list_of_dates(user.id, date_list)
|
||||||
|
s = (n > 1 && "s") || ""
|
||||||
|
flash = {:info, "Successfully deleted #{n} assigned shift#{s}"}
|
||||||
|
send(self(), {:put_flash_message, flash})
|
||||||
|
send(self(), {:clear_selected_days, true})
|
||||||
|
|
||||||
|
socket
|
||||||
|
|> push_event("modal-please-hide", %{})
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<p>Are you sure you want to delete all assigned shifts from the selected days?</p>
|
||||||
|
|
||||||
|
<%= for {y, data} <- @date_map do %>
|
||||||
|
<dt><%= y %></dt>
|
||||||
|
<% months = Map.keys(data) %>
|
||||||
|
<dd>
|
||||||
|
<%= for {m, i} <- Enum.with_index(months, 1) do %>
|
||||||
|
<%= data |> Map.get(m) |> hd() |> Timex.format!("{Mshort}") %>:
|
||||||
|
<% days = Map.get(data, m) %>
|
||||||
|
<%= for {d, i} <- Enum.with_index(days, 1) do %>
|
||||||
|
<%= d.day %><%= if i < length(days) do %>,<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= if i < length(months) do %><br /><% end %>
|
||||||
|
<% end %>
|
||||||
|
</dd>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
|
||||||
|
<%= link "Cancel", to: "#", class: "btn btn-outline-dark", phx_click: "hide", phx_target: "##{@modal_id}" %>
|
||||||
|
<%= link "Confirm Delete", to: "#",
|
||||||
|
class: "btn btn-danger",
|
||||||
|
phx_click: "confirm-delete-days-shifts",
|
||||||
|
phx_target: @myself
|
||||||
|
%>
|
||||||
|
|
||||||
|
</div>
|
|
@ -18,9 +18,10 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
socket
|
socket
|
||||||
|> assign_defaults(session)
|
|> assign_defaults(session)
|
||||||
|> assign(:custom_shift, @custom_shift)
|
|> assign(:custom_shift, @custom_shift)
|
||||||
|> assign(:show_template_btn_active, :false)
|
|> assign(:show_template_btn_active, false)
|
||||||
|> assign(:show_template_details, :false)
|
|> assign(:show_template_details, false)
|
||||||
|> assign(:selected_days, [])
|
|> assign(:selected_days, [])
|
||||||
|
|> assign(:delete_days_shifts, nil)
|
||||||
|> live_okreply()
|
|> live_okreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,12 +33,18 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
|> show_details_if_custom()
|
|> show_details_if_custom()
|
||||||
|> assign_shift_length()
|
|> assign_shift_length()
|
||||||
|> assign_shift_template_changeset()
|
|> assign_shift_template_changeset()
|
||||||
|
|> assign_modal_close_handlers()
|
||||||
|> init_today(Timex.today())
|
|> init_today(Timex.today())
|
||||||
|> init_calendar()
|
|> init_calendar()
|
||||||
|> assign_known_shifts()
|
|> assign_known_shifts()
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp assign_modal_close_handlers(socket) do
|
||||||
|
to = Routes.shift_assign_index_path(socket, :index)
|
||||||
|
assign(socket, modal_return_to: to, modal_close_action: :return)
|
||||||
|
end
|
||||||
|
|
||||||
defp get_shift_template("custom-shift"), do: @custom_shift
|
defp get_shift_template("custom-shift"), do: @custom_shift
|
||||||
defp get_shift_template(template_id), do: Templates.get_shift_template(template_id)
|
defp get_shift_template(template_id), do: Templates.get_shift_template(template_id)
|
||||||
|
|
||||||
|
@ -56,11 +63,11 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
defp init_calendar(%{assigns: %{current_user: user}} = socket) do
|
defp init_calendar(%{assigns: %{current_user: user}} = socket) do
|
||||||
days = day_names(user.week_start_at)
|
days = day_names(user.week_start_at)
|
||||||
{first, last, rows} = week_rows(socket.assigns.cursor_date, user.week_start_at)
|
{first, last, rows} = week_rows(socket.assigns.cursor_date, user.week_start_at)
|
||||||
assign(socket, [day_names: days, week_rows: rows, day_first: first, day_last: last])
|
assign(socket, day_names: days, week_rows: rows, day_first: first, day_last: last)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp init_today(socket, today) do
|
defp init_today(socket, today) do
|
||||||
assign(socket, [current_date: today, cursor_date: today])
|
assign(socket, current_date: today, cursor_date: today)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_shift_template_changeset(%{assigns: %{shift_template: shift}} = socket) do
|
defp assign_shift_template_changeset(%{assigns: %{shift_template: shift}} = socket) do
|
||||||
|
@ -71,7 +78,7 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
defp init_shift_template(socket) do
|
defp init_shift_template(socket) do
|
||||||
first_list_id = socket.assigns.shift_templates |> hd() |> elem(1)
|
first_list_id = socket.assigns.shift_templates |> hd() |> elem(1)
|
||||||
fave_id = socket.assigns.current_user.fave_shift_template_id
|
fave_id = socket.assigns.current_user.fave_shift_template_id
|
||||||
assign_shift_template(socket, (fave_id || first_list_id))
|
assign_shift_template(socket, fave_id || first_list_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_shift_template(socket, template_id) do
|
defp assign_shift_template(socket, template_id) do
|
||||||
|
@ -83,13 +90,16 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
Templates.list_shift_templates_by_user_id(user.id)
|
Templates.list_shift_templates_by_user_id(user.id)
|
||||||
|> Enum.map(fn t -> shift_template_option(t, user.fave_shift_template_id) end)
|
|> Enum.map(fn t -> shift_template_option(t, user.fave_shift_template_id) end)
|
||||||
|> Enum.concat([@custom_shift_opt])
|
|> Enum.concat([@custom_shift_opt])
|
||||||
|
|
||||||
assign(socket, :shift_templates, shift_templates)
|
assign(socket, :shift_templates, shift_templates)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp shift_template_option(template, fave_id) do
|
defp shift_template_option(template, fave_id) do
|
||||||
label =
|
label =
|
||||||
template.subject <> " (" <>
|
template.subject <>
|
||||||
format_shift_time(template.time_start) <> "—" <>
|
" (" <>
|
||||||
|
format_shift_time(template.time_start) <>
|
||||||
|
"—" <>
|
||||||
format_shift_time(template.time_end) <> ")"
|
format_shift_time(template.time_end) <> ")"
|
||||||
|
|
||||||
label =
|
label =
|
||||||
|
@ -141,11 +151,14 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
true -> "bg-triangle-white"
|
true -> "bg-triangle-white"
|
||||||
end
|
end
|
||||||
|
|
||||||
Timex.compare(day, current_date, :days) == 0 -> "bg-info text-white"
|
Timex.compare(day, current_date, :days) == 0 ->
|
||||||
|
"bg-info text-white"
|
||||||
|
|
||||||
day.month != cursor_date.month -> "bg-light text-gray"
|
day.month != cursor_date.month ->
|
||||||
|
"bg-light text-gray"
|
||||||
|
|
||||||
true -> ""
|
true ->
|
||||||
|
""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -157,11 +170,12 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp show_details_if_custom(socket) do
|
defp show_details_if_custom(socket) do
|
||||||
if (socket.assigns.shift_template.id != @custom_shift.id) || socket.assigns.show_template_details do
|
if socket.assigns.shift_template.id != @custom_shift.id ||
|
||||||
|
socket.assigns.show_template_details do
|
||||||
socket
|
socket
|
||||||
else
|
else
|
||||||
socket
|
socket
|
||||||
|> assign(:show_template_btn_active, :true)
|
|> assign(:show_template_btn_active, true)
|
||||||
|> push_event("toggle-template-details", %{targetId: "#templateDetailsCol"})
|
|> push_event("toggle-template-details", %{targetId: "#templateDetailsCol"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -197,7 +211,11 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("change-selected-template", %{"template_select" => %{"template" => template_id}}, socket) do
|
def handle_event(
|
||||||
|
"change-selected-template",
|
||||||
|
%{"template_select" => %{"template" => template_id}},
|
||||||
|
socket
|
||||||
|
) do
|
||||||
socket
|
socket
|
||||||
|> assign_shift_template(template_id)
|
|> assign_shift_template(template_id)
|
||||||
|> show_details_if_custom()
|
|> show_details_if_custom()
|
||||||
|
@ -210,9 +228,11 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
def handle_event("month-nav", %{"month" => direction}, socket) do
|
def handle_event("month-nav", %{"month" => direction}, socket) do
|
||||||
new_cursor =
|
new_cursor =
|
||||||
cond do
|
cond do
|
||||||
direction == "now" -> Timex.today()
|
direction == "now" ->
|
||||||
|
Timex.today()
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
months = direction == "prev" && -1 || 1
|
months = (direction == "prev" && -1) || 1
|
||||||
Timex.shift(socket.assigns.cursor_date, months: months)
|
Timex.shift(socket.assigns.cursor_date, months: months)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -229,12 +249,12 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("collapse-shown", %{"target_id" => _target_id}, socket) do
|
def handle_event("collapse-shown", %{"target_id" => _target_id}, socket) do
|
||||||
{:noreply, assign(socket, :show_template_details, :true)}
|
{:noreply, assign(socket, :show_template_details, true)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("collapse-hidden", %{"target_id" => _target_id}, socket) do
|
def handle_event("collapse-hidden", %{"target_id" => _target_id}, socket) do
|
||||||
{:noreply, assign(socket, :show_template_details, :false)}
|
{:noreply, assign(socket, :show_template_details, false)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -248,6 +268,14 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
{:noreply, assign(socket, :selected_days, selected_days)}
|
{:noreply, assign(socket, :selected_days, selected_days)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("delete-days-shifts", _params, socket) do
|
||||||
|
socket
|
||||||
|
|> assign(:modal_close_action, :delete_days_shifts)
|
||||||
|
|> assign(:delete_days_shifts, socket.assigns.selected_days)
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("clear-days", _params, socket) do
|
def handle_event("clear-days", _params, socket) do
|
||||||
{:noreply, assign(socket, :selected_days, [])}
|
{:noreply, assign(socket, :selected_days, [])}
|
||||||
|
@ -259,7 +287,8 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
shift_data = shift_data_from_template(socket.assigns.shift_template)
|
shift_data = shift_data_from_template(socket.assigns.shift_template)
|
||||||
|
|
||||||
# 2. create list of shift attrs to insert
|
# 2. create list of shift attrs to insert
|
||||||
to_insert = Enum.map(socket.assigns.selected_days, &shift_from_day_and_shift_data(&1, shift_data))
|
to_insert =
|
||||||
|
Enum.map(socket.assigns.selected_days, &shift_from_day_and_shift_data(&1, shift_data))
|
||||||
|
|
||||||
# 3. insert the data
|
# 3. insert the data
|
||||||
{status, msg} = insert_shifts(to_insert, length(socket.assigns.selected_days))
|
{status, msg} = insert_shifts(to_insert, length(socket.assigns.selected_days))
|
||||||
|
@ -271,10 +300,37 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:put_flash_message, {flash_type, msg}}, socket) do
|
||||||
|
socket |> put_flash(flash_type, msg) |> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:clear_selected_days, _}, socket) do
|
||||||
|
socket |> assign(:selected_days, []) |> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: :return}} = socket) do
|
||||||
|
socket
|
||||||
|
|> copy_flash()
|
||||||
|
|> push_patch(to: socket.assigns.modal_return_to)
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: assign_key}} = socket) do
|
||||||
|
socket
|
||||||
|
|> assign(assign_key, nil)
|
||||||
|
|> assign_modal_close_handlers()
|
||||||
|
|> assign_known_shifts()
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
defp shift_data_from_template(shift_template) do
|
defp shift_data_from_template(shift_template) do
|
||||||
shift_template
|
shift_template
|
||||||
|> Map.from_struct()
|
|> Map.from_struct()
|
||||||
|> Map.drop([:__meta__, :id, :inserted_at, :updated_at, :user])
|
|> Map.drop([:__meta__, :id, :inserted_at, :updated_at, :user, :is_fave_of_user])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp shift_from_day_and_shift_data(day, shift_data) do
|
defp shift_from_day_and_shift_data(day, shift_data) do
|
||||||
|
@ -289,12 +345,16 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{insert_all: {n, _}}} ->
|
{:ok, %{insert_all: {n, _}}} ->
|
||||||
|
s = (n > 1 && "s") || ""
|
||||||
|
|
||||||
if n == day_count do
|
if n == day_count do
|
||||||
{:success, "Successfully assigned shift to #{n} day(s)"}
|
{:success, "Successfully assigned shift to #{n} day#{s}"}
|
||||||
else
|
else
|
||||||
{:warning, "Some error, only #{n} day(s) inserted but #{day_count} were selected"}
|
{:warning, "Some error, only #{n} day#{s} inserted but #{day_count} were selected"}
|
||||||
end
|
end
|
||||||
_ -> {:error, "Ope, unknown error inserting shifts, page the dev"}
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Ope, unknown error inserting shifts, page the dev"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
<%= if @delete_days_shifts do %>
|
||||||
|
<%= live_modal @socket, Shift73kWeb.ShiftAssignLive.DeleteComponent,
|
||||||
|
id: "delete-days-shifts-#{@current_user.id}",
|
||||||
|
title: "Delete Shifts From Selected Days",
|
||||||
|
delete_days_shifts: @delete_days_shifts,
|
||||||
|
current_user: @current_user
|
||||||
|
%>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
<h2 class="mb-3 mb-sm-0">
|
<h2 class="mb-3 mb-sm-0">
|
||||||
<%= icon_div @socket, "bi-calendar2-plus", [class: "icon baseline"] %>
|
<%= icon_div @socket, "bi-calendar2-plus", [class: "icon baseline"] %>
|
||||||
Assign Shift To Dates
|
Assign Shift To Dates
|
||||||
|
@ -223,17 +233,22 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<div class="row justify-content-end my-4">
|
<div class="row justify-content-center justify-content-lg-end my-5">
|
||||||
<div class="col-auto">
|
<div class="col-12 col-sm-10 col-md-8 col-lg-auto d-flex flex-column-reverse flex-lg-row">
|
||||||
|
|
||||||
<button class="btn btn-outline-dark" phx-click="clear-days">
|
<button class="btn btn-outline-danger mb-1 mb-lg-0 me-lg-1" phx-click="delete-days-shifts" <%= if Enum.empty?(@selected_days), do: "disabled" %>>
|
||||||
|
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %>
|
||||||
|
Delete shifts from selected days
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn-outline-dark mb-1 mb-lg-0 me-lg-1" phx-click="clear-days" <%= if Enum.empty?(@selected_days), do: "disabled" %>>
|
||||||
<%= icon_div @socket, "bi-eraser", [class: "icon baseline"] %>
|
<%= icon_div @socket, "bi-eraser", [class: "icon baseline"] %>
|
||||||
Clear
|
De-select all selected
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn btn-primary" phx-click="save-days" <%= if (!@shift_template_changeset.valid? || Enum.empty?(@selected_days)), do: "disabled" %>>
|
<button class="btn btn-primary mb-1 mb-lg-0" phx-click="save-days" <%= if (!@shift_template_changeset.valid? || Enum.empty?(@selected_days)), do: "disabled" %>>
|
||||||
<%= icon_div @socket, "bi-save", [class: "icon baseline"] %>
|
<%= icon_div @socket, "bi-save", [class: "icon baseline"] %>
|
||||||
Save assigned shifts
|
Assign shifts to selected days
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,11 +11,6 @@ defmodule Shift73kWeb.ShiftTemplateLive.DeleteComponent do
|
||||||
|> live_okreply()
|
|> live_okreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("cancel", _, socket) do
|
|
||||||
{:noreply, push_event(socket, "modal-please-hide", %{})}
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("confirm", %{"id" => id, "subject" => subject}, socket) do
|
def handle_event("confirm", %{"id" => id, "subject" => subject}, socket) do
|
||||||
id
|
id
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
||||||
<%= link "Cancel", to: "#", class: "btn me-2", phx_click: "cancel", phx_target: @myself %>
|
<%= link "Cancel", to: "#", class: "btn btn-outline-dark", phx_click: "hide", phx_target: "##{@modal_id}" %>
|
||||||
<%= link "Confirm Delete", to: "#",
|
<%= link "Confirm Delete", to: "#",
|
||||||
class: "btn btn-danger",
|
class: "btn btn-danger",
|
||||||
phx_click: "confirm",
|
phx_click: "confirm",
|
||||||
|
|
|
@ -52,11 +52,6 @@ defmodule Shift73kWeb.ShiftTemplateLive.FormComponent do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("cancel", _, socket) do
|
|
||||||
{:noreply, push_event(socket, "modal-please-hide", %{})}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp save_shift_template(socket, :new, params) do
|
defp save_shift_template(socket, :new, params) do
|
||||||
case Templates.create_shift_template(params) do
|
case Templates.create_shift_template(params) do
|
||||||
{:ok, _shift_template} ->
|
{:ok, _shift_template} ->
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
||||||
<%= link "Cancel", to: "#", class: "btn", phx_click: "cancel", phx_target: @myself %>
|
<%= link "Cancel", to: "#", class: "btn btn-outline-dark", phx_click: "hide", phx_target: "##{@modal_id}" %>
|
||||||
<%= submit "Save",
|
<%= submit "Save",
|
||||||
class: "btn btn-primary ",
|
class: "btn btn-primary ",
|
||||||
disabled: !@changeset.valid?,
|
disabled: !@changeset.valid?,
|
||||||
|
|
|
@ -23,7 +23,7 @@ defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
||||||
if Roles.can?(current_user, shift_template, live_action) do
|
if Roles.can?(current_user, shift_template, live_action) do
|
||||||
socket
|
socket
|
||||||
|> assign_shift_templates()
|
|> assign_shift_templates()
|
||||||
|> assign(:modal_return_to, Routes.shift_template_index_path(socket, :index))
|
|> assign_modal_close_handlers()
|
||||||
|> assign(:delete_shift_template, nil)
|
|> assign(:delete_shift_template, nil)
|
||||||
|> apply_action(socket.assigns.live_action, params)
|
|> apply_action(socket.assigns.live_action, params)
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
|
@ -35,6 +35,11 @@ defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp assign_modal_close_handlers(socket) do
|
||||||
|
to = Routes.shift_template_index_path(socket, :index)
|
||||||
|
assign(socket, modal_return_to: to, modal_close_action: :return)
|
||||||
|
end
|
||||||
|
|
||||||
defp apply_action(socket, :clone, %{"id" => id}) do
|
defp apply_action(socket, :clone, %{"id" => id}) do
|
||||||
socket
|
socket
|
||||||
|> assign(:page_title, "Clone Shift Template")
|
|> assign(:page_title, "Clone Shift Template")
|
||||||
|
@ -74,9 +79,13 @@ defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete-modal", %{"id" => id}, socket) do
|
def handle_event("delete-modal", %{"id" => id}, socket) do
|
||||||
{:noreply, assign(socket, :delete_shift_template, Templates.get_shift_template!(id))}
|
socket
|
||||||
|
|> assign(:modal_close_action, :delete_shift_template)
|
||||||
|
|> assign(:delete_shift_template, Templates.get_shift_template!(id))
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
def handle_event("set-user-fave-shift-template", %{"id" => shift_template_id}, socket) do
|
def handle_event("set-user-fave-shift-template", %{"id" => shift_template_id}, socket) do
|
||||||
user_id = socket.assigns.current_user.id
|
user_id = socket.assigns.current_user.id
|
||||||
Accounts.set_user_fave_shift_template(user_id, shift_template_id)
|
Accounts.set_user_fave_shift_template(user_id, shift_template_id)
|
||||||
|
@ -87,6 +96,7 @@ defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
def handle_event("unset-user-fave-shift-template", _params, socket) do
|
def handle_event("unset-user-fave-shift-template", _params, socket) do
|
||||||
user_id = socket.assigns.current_user.id
|
user_id = socket.assigns.current_user.id
|
||||||
Accounts.unset_user_fave_shift_template(user_id)
|
Accounts.unset_user_fave_shift_template(user_id)
|
||||||
|
@ -98,13 +108,24 @@ defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:close_modal, _}, %{assigns: %{modal_return_to: to}} = socket) do
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: :return}} = socket) do
|
||||||
socket |> copy_flash() |> push_patch(to: to) |> live_noreply()
|
socket
|
||||||
|
|> copy_flash()
|
||||||
|
|> push_patch(to: socket.assigns.modal_return_to)
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: assign_key}} = socket) do
|
||||||
|
socket
|
||||||
|
|> assign(assign_key, nil)
|
||||||
|
|> assign_modal_close_handlers()
|
||||||
|
|> assign_shift_templates()
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:put_flash_message, {flash_type, msg}}, socket) do
|
def handle_info({:put_flash_message, {flash_type, msg}}, socket) do
|
||||||
socket |> put_flash(flash_type, msg) |> live_noreply()
|
socket |> put_flash(flash_type, msg) |> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
<%= live_modal @socket, Shift73kWeb.ShiftTemplateLive.DeleteComponent,
|
<%= live_modal @socket, Shift73kWeb.ShiftTemplateLive.DeleteComponent,
|
||||||
id: @delete_shift_template.id,
|
id: @delete_shift_template.id,
|
||||||
title: "Delete Shift Template",
|
title: "Delete Shift Template",
|
||||||
delete_shift_template: @delete_shift_template,
|
delete_shift_template: @delete_shift_template %>
|
||||||
current_user: @current_user %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,7 @@ defmodule Shift73kWeb.UserManagement.DeleteComponent do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def update(assigns, socket) do
|
def update(assigns, socket) do
|
||||||
socket
|
socket |> assign(assigns) |> live_okreply()
|
||||||
|> assign(assigns)
|
|
||||||
|> live_okreply()
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("cancel", _, socket) do
|
|
||||||
{:noreply, push_event(socket, "modal-please-hide", %{})}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
||||||
<%= link "Cancel", to: "#", class: "btn me-2", phx_click: "cancel", phx_target: @myself %>
|
<%= link "Cancel", to: "#", class: "btn btn-outline-dark", phx_click: "hide", phx_target: "##{@modal_id}" %>
|
||||||
<%= link "Confirm Delete", to: "#",
|
<%= link "Confirm Delete", to: "#",
|
||||||
class: "btn btn-danger",
|
class: "btn btn-danger",
|
||||||
phx_click: "confirm",
|
phx_click: "confirm",
|
||||||
|
|
|
@ -80,11 +80,6 @@ defmodule Shift73kWeb.UserManagement.FormComponent do
|
||||||
save_user(socket, user_params)
|
save_user(socket, user_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("cancel", _, socket) do
|
|
||||||
{:noreply, push_event(socket, "modal-please-hide", %{})}
|
|
||||||
end
|
|
||||||
|
|
||||||
def role_description(role) when is_atom(role) do
|
def role_description(role) when is_atom(role) do
|
||||||
Keyword.get(User.roles(), role)
|
Keyword.get(User.roles(), role)
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
||||||
<%= link "Cancel", to: "#", class: "btn", phx_click: "cancel", phx_target: @myself %>
|
<%= link "Cancel", to: "#", class: "btn btn-outline-dark", phx_click: "hide", phx_target: "##{@modal_id}" %>
|
||||||
<%= submit "Save",
|
<%= submit "Save",
|
||||||
class: "btn btn-primary ",
|
class: "btn btn-primary ",
|
||||||
disabled: !@changeset.valid?,
|
disabled: !@changeset.valid?,
|
||||||
|
|
|
@ -28,9 +28,8 @@ defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
if Roles.can?(current_user, user, live_action) do
|
if Roles.can?(current_user, user, live_action) do
|
||||||
socket
|
socket
|
||||||
|> assign(:query, query_map(params))
|
|> assign(:query, query_map(params))
|
||||||
|> assign_modal_return_to()
|
|> assign_modal_close_handlers()
|
||||||
|> assign(:delete_user, nil)
|
|> assign(:delete_user, nil)
|
||||||
|> assign(:page, nil)
|
|
||||||
|> request_page_query()
|
|> request_page_query()
|
||||||
|> apply_action(socket.assigns.live_action, params)
|
|> apply_action(socket.assigns.live_action, params)
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
|
@ -65,9 +64,9 @@ defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
|> assign(:user, Accounts.get_user!(id))
|
|> assign(:user, Accounts.get_user!(id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_modal_return_to(%{assigns: %{query: query}} = socket) do
|
defp assign_modal_close_handlers(%{assigns: %{query: query}} = socket) do
|
||||||
to = Routes.user_management_index_path(socket, :index, Enum.into(query, []))
|
to = Routes.user_management_index_path(socket, :index, Enum.into(query, []))
|
||||||
assign(socket, :modal_return_to, to)
|
assign(socket, modal_return_to: to, modal_close_action: :return)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp user_from_params(params)
|
defp user_from_params(params)
|
||||||
|
@ -98,7 +97,10 @@ defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete-modal", %{"id" => id}, socket) do
|
def handle_event("delete-modal", %{"id" => id}, socket) do
|
||||||
{:noreply, assign(socket, :delete_user, Accounts.get_user!(id))}
|
socket
|
||||||
|
|> assign(:modal_close_action, :delete_user)
|
||||||
|
|> assign(:delete_user, Accounts.get_user!(id))
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -114,17 +116,13 @@ defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event(
|
def handle_event("sort-change", %{"sort_by" => column} = params, socket) do
|
||||||
"sort-change",
|
if column == socket.assigns.query.sort_by do
|
||||||
%{"sort_by" => column} = params,
|
order = (socket.assigns.query.sort_order == "asc" && "desc") || "asc"
|
||||||
%{assigns: %{query: query}} = socket
|
send(self(), {:query_update, %{"sort_order" => order}})
|
||||||
) do
|
else
|
||||||
(column == query.sort_by &&
|
|
||||||
send(
|
|
||||||
self(),
|
|
||||||
{:query_update, %{"sort_order" => (query.sort_order == "asc" && "desc") || "asc"}}
|
|
||||||
)) ||
|
|
||||||
send(self(), {:query_update, Map.put(params, "sort_order", "asc")})
|
send(self(), {:query_update, Map.put(params, "sort_order", "asc")})
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
@ -169,8 +167,20 @@ defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:close_modal, _}, %{assigns: %{modal_return_to: to}} = socket) do
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: :return}} = socket) do
|
||||||
socket |> copy_flash() |> push_patch(to: to) |> live_noreply()
|
socket
|
||||||
|
|> copy_flash()
|
||||||
|
|> push_patch(to: socket.assigns.modal_return_to)
|
||||||
|
|> live_noreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_info({:close_modal, _}, %{assigns: %{modal_close_action: assign_key}} = socket) do
|
||||||
|
socket
|
||||||
|
|> assign(assign_key, nil)
|
||||||
|
|> assign_modal_close_handlers()
|
||||||
|
|> request_page_query()
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
<%= live_modal @socket, Shift73kWeb.UserManagement.DeleteComponent,
|
<%= live_modal @socket, Shift73kWeb.UserManagement.DeleteComponent,
|
||||||
id: @delete_user.id,
|
id: @delete_user.id,
|
||||||
title: "Delete User",
|
title: "Delete User",
|
||||||
delete_user: @delete_user,
|
delete_user: @delete_user
|
||||||
current_user: @current_user %>
|
%>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
|
36
lib/shift73k_web/plugs/redirector.ex
Normal file
36
lib/shift73k_web/plugs/redirector.ex
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule Shift73kWeb.Redirector do
|
||||||
|
import Phoenix.Controller, only: [redirect: 2]
|
||||||
|
|
||||||
|
def init([to: _] = opts), do: opts
|
||||||
|
def init([external: _] = opts), do: opts
|
||||||
|
def init(_default), do: raise("Missing required to: / external: option in redirect")
|
||||||
|
|
||||||
|
def call(conn, [to: to]) do
|
||||||
|
redirect(conn, to: append_query_string(conn, to))
|
||||||
|
end
|
||||||
|
def call(conn, [external: url]) do
|
||||||
|
external = url
|
||||||
|
|> URI.parse
|
||||||
|
|> merge_query_string(conn)
|
||||||
|
|> URI.to_string
|
||||||
|
|
||||||
|
redirect(conn, external: external)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
defp append_query_string(%Plug.Conn{query_string: ""}, path), do: path
|
||||||
|
defp append_query_string(%Plug.Conn{query_string: query}, path), do: "#{path}?#{query}"
|
||||||
|
|
||||||
|
defp merge_query_string(%URI{query: nil} = destination_uri, %Plug.Conn{query_string: source}) do
|
||||||
|
%{destination_uri | query: source}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp merge_query_string(%URI{query: destination} = destination_uri, %Plug.Conn{query_string: source}) do
|
||||||
|
merged_query = Map.merge(
|
||||||
|
URI.decode_query(destination),
|
||||||
|
URI.decode_query(source)
|
||||||
|
)
|
||||||
|
|
||||||
|
%{destination_uri | query: URI.encode_query(merged_query)}
|
||||||
|
end
|
||||||
|
end
|
|
@ -32,8 +32,7 @@ defmodule Shift73kWeb.Router do
|
||||||
scope "/", Shift73kWeb do
|
scope "/", Shift73kWeb do
|
||||||
pipe_through([:browser])
|
pipe_through([:browser])
|
||||||
|
|
||||||
live("/", PageLive, :index)
|
get("/", Redirector, to: "/assign")
|
||||||
get("/other", OtherController, :index)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Other scopes may use custom stacks.
|
# Other scopes may use custom stacks.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<h1 class="fs-4 my-0 py-0 lh-base">
|
<h1 class="fs-4 my-0 py-0 lh-base">
|
||||||
<%= link to: Routes.page_path(@conn, :index), class: "navbar-brand fs-4" do %>
|
<%= link to: "/", class: "navbar-brand fs-4" do %>
|
||||||
<%= icon_div @conn, "bi-calendar2-week", [class: "icon baseline me-1"] %>
|
<%= icon_div @conn, "bi-calendar2-week", [class: "icon baseline me-1"] %>
|
||||||
<span class="fw-light">Shift73k</span>
|
<span class="fw-light">Shift73k</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -26,14 +26,14 @@
|
||||||
<%# nav LEFT items %>
|
<%# nav LEFT items %>
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
|
||||||
<%= if @current_user do %>
|
<%#= if @current_user do %>
|
||||||
<li class="nav-item">
|
<%# <li class="nav-item"> %>
|
||||||
<%= link nav_link_opts(@conn, to: Routes.shift_template_index_path(@conn, :index), class: "nav-link") do %>
|
<%#= 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"] %>
|
<%#= icon_div @conn, "bi-clock-history", [class: "icon baseline me-1"] %>
|
||||||
Templates
|
<%# Templates %>
|
||||||
<% end %>
|
<%# end %>
|
||||||
</li>
|
<%# </li> %>
|
||||||
<% end %>
|
<%# end %>
|
||||||
|
|
||||||
<%# normal navbar link example %>
|
<%# normal navbar link example %>
|
||||||
<%# <li class="nav-item"> %>
|
<%# <li class="nav-item"> %>
|
||||||
|
@ -66,13 +66,19 @@
|
||||||
<%# nav RIGHT items %>
|
<%# nav RIGHT items %>
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
|
||||||
|
<%= if @current_user do %>
|
||||||
|
|
||||||
|
<%= render "navbar/_shifts_menu.html", assigns %>
|
||||||
|
|
||||||
<%= render "navbar/_user_menu.html", assigns %>
|
<%= render "navbar/_user_menu.html", assigns %>
|
||||||
|
|
||||||
<%= if !@current_user do %>
|
<% else %>
|
||||||
|
|
||||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light d-none d-lg-block") do %>
|
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light d-none d-lg-block") do %>
|
||||||
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
|
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
|
||||||
Log in
|
Log in
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
|
||||||
|
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<%= icon_div @conn, "bi-calendar2", [class: "icon baseline me-1"] %>
|
||||||
|
Shifts
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownUserMenu">
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<%= link nav_link_opts(@conn, to: Routes.shift_assign_index_path(@conn, :index), class: "dropdown-item") do %>
|
||||||
|
<%= icon_div @conn, "bi-calendar2-plus", [class: "icon baseline me-1"] %>
|
||||||
|
Assign Shift To Dates
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= link nav_link_opts(@conn, to: Routes.shift_template_index_path(@conn, :index), class: "dropdown-item") do %>
|
||||||
|
<%= icon_div @conn, "bi-clock-history", [class: "icon baseline me-1"] %>
|
||||||
|
My Shift Templates
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</li>
|
|
@ -1,5 +1,3 @@
|
||||||
<%= if @current_user do %>
|
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
|
||||||
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
|
<a href="#" class="nav-link dropdown-toggle" id="navbarDropdownUserMenu" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
@ -36,5 +34,3 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<% end %>
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
<h1 class="text-3xl font-bold leading-tight text-gunmetal-200">
|
|
||||||
Other Page
|
|
||||||
</h1>
|
|
||||||
<h2 class="text-xl leading-tight text-gunmetal-400">
|
|
||||||
With a subtitle no less!
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
||||||
<!-- Replace with your content -->
|
|
||||||
<%# <div class="px-4 py-6 sm:px-0">
|
|
||||||
<div class="border-4 border-dashed border-gray-200 rounded-lg h-96"></div>
|
|
||||||
</div> %>
|
|
||||||
<p>
|
|
||||||
Praesent velit justo, auctor ut nibh id, fermentum eleifend nunc. Cras sed purus dignissim, ornare elit et, ultrices elit. Sed quis neque consequat, laoreet ante a, hendrerit elit. Maecenas dapibus sed nulla vitae consectetur. Duis sollicitudin augue nisl, et rhoncus enim tempor at. Fusce scelerisque sollicitudin purus sit amet iaculis. Phasellus lacinia mi ut laoreet accumsan. Sed sagittis erat nec sem placerat, ut volutpat neque porttitor. Suspendisse tempor mauris vel mollis sagittis. In ut laoreet arcu. Duis sed felis in dui imperdiet luctus nec faucibus sem. Donec commodo urna ut enim fringilla, quis lacinia ligula malesuada. Quisque feugiat fermentum pretium. Integer sed porttitor lacus, sed bibendum diam. Aliquam dapibus neque et pharetra interdum.
|
|
||||||
</p>
|
|
||||||
<!-- /End replace -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Button trigger modal -->
|
|
||||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
|
|
||||||
Launch demo modal
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Modal -->
|
|
||||||
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
Aliquam ultrices elit purus, eget dignissim orci pulvinar id. Curabitur tincidunt, ligula eu condimentum porttitor, nibh sapien scelerisque urna, nec cursus nisi nisi a neque. Mauris hendrerit orci blandit, suscipit ante nec, porttitor neque. Nunc.
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn" data-bs-dismiss="modal">Close</button>
|
|
||||||
<button type="button" class="btn btn-primary">Save changes</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,3 +0,0 @@
|
||||||
defmodule Shift73kWeb.OtherView do
|
|
||||||
use Shift73kWeb, :view
|
|
||||||
end
|
|
|
@ -10,7 +10,7 @@ defmodule Shift73k.Repo.Migrations.CreateShiftTemplates do
|
||||||
add :time_zone, :string, null: false
|
add :time_zone, :string, null: false
|
||||||
add :time_start, :time, null: false
|
add :time_start, :time, null: false
|
||||||
add :time_end, :time, null: false
|
add :time_end, :time, null: false
|
||||||
add :user_id, references(:users, on_delete: :nothing, type: :binary_id)
|
add :user_id, references(:users, on_delete: :delete_all, type: :binary_id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ defmodule Shift73k.Repo.Migrations.AddUserDefaultShiftColumn do
|
||||||
|
|
||||||
def change do
|
def change do
|
||||||
alter table(:users) do
|
alter table(:users) do
|
||||||
add(:fave_shift_template_id, references(:shift_templates, type: :binary_id, on_delete: :delete_all))
|
add(:fave_shift_template_id, references(:shift_templates, type: :binary_id, on_delete: :nilify_all))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
defmodule Shift73kWeb.PageLiveTest do
|
|
||||||
use Shift73kWeb.ConnCase
|
|
||||||
|
|
||||||
import Phoenix.LiveViewTest
|
|
||||||
import Shift73k.AccountsFixtures
|
|
||||||
|
|
||||||
test "disconnected and connected render with authentication should redirect to index page", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
conn = conn |> log_in_user(user_fixture())
|
|
||||||
|
|
||||||
{:ok, page_live, disconnected_html} = live(conn, "/")
|
|
||||||
assert disconnected_html =~ "Welcome to Phoenix!"
|
|
||||||
assert render(page_live) =~ "Welcome to Phoenix!"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "logs out when force logout on logged user", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = user_fixture()
|
|
||||||
conn = conn |> log_in_user(user)
|
|
||||||
|
|
||||||
{:ok, page_live, disconnected_html} = live(conn, "/")
|
|
||||||
assert disconnected_html =~ "Welcome to Phoenix!"
|
|
||||||
assert render(page_live) =~ "Welcome to Phoenix!"
|
|
||||||
|
|
||||||
Shift73k.Accounts.logout_user(user)
|
|
||||||
|
|
||||||
# Assert our liveview process is down
|
|
||||||
ref = Process.monitor(page_live.pid)
|
|
||||||
assert_receive {:DOWN, ^ref, _, _, _}
|
|
||||||
refute Process.alive?(page_live.pid)
|
|
||||||
|
|
||||||
# Assert our liveview was redirected, following first to /users/force_logout, then to "/", and then to "/users/log_in"
|
|
||||||
assert_redirect(page_live, "/users/force_logout")
|
|
||||||
|
|
||||||
conn = get(conn, "/users/force_logout")
|
|
||||||
assert "/" = redir_path = redirected_to(conn, 302)
|
|
||||||
conn = get(recycle(conn), redir_path)
|
|
||||||
|
|
||||||
assert html_response(conn, 200) =~
|
|
||||||
"You were logged out. Please login again to continue using our application."
|
|
||||||
end
|
|
||||||
|
|
||||||
test "doesn't log out when force logout on another user", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user1 = user_fixture()
|
|
||||||
user2 = user_fixture()
|
|
||||||
conn = conn |> log_in_user(user2)
|
|
||||||
|
|
||||||
{:ok, page_live, disconnected_html} = live(conn, "/")
|
|
||||||
assert disconnected_html =~ "Welcome to Phoenix!"
|
|
||||||
assert render(page_live) =~ "Welcome to Phoenix!"
|
|
||||||
|
|
||||||
Shift73k.Accounts.logout_user(user1)
|
|
||||||
|
|
||||||
# Assert our liveview is alive
|
|
||||||
ref = Process.monitor(page_live.pid)
|
|
||||||
refute_receive {:DOWN, ^ref, _, _, _}
|
|
||||||
assert Process.alive?(page_live.pid)
|
|
||||||
|
|
||||||
# If we are able to rerender the page it means nothing happened
|
|
||||||
assert render(page_live) =~ "Welcome to Phoenix!"
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue