From 8a1b7a48520946465a1b857bb30eb07b2f12c0d9 Mon Sep 17 00:00:00 2001 From: Adam Piontek Date: Sat, 20 Mar 2021 12:45:50 -0400 Subject: [PATCH] basic shift display working --- assets/js/app.js | 2 + lib/shift73k/ecto_enums.ex | 3 +- lib/shift73k/shifts.ex | 30 ++--- .../shift_assign_live/delete_component.ex | 2 +- .../live/shift_assign_live/index.ex | 79 ++++++------ .../live/shift_assign_live/index.html.leex | 2 +- lib/shift73k_web/live/shift_live/index.ex | 81 +++++++++++- .../live/shift_live/index.html.leex | 116 ++++++++++++------ .../live/shift_live/show.html.leex | 5 - lib/shift73k_web/roles.ex | 11 +- .../layout/navbar/_shifts_menu.html.eex | 8 +- priv/repo/seeds.exs | 2 +- 12 files changed, 233 insertions(+), 108 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 62e62df3..6d274010 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -36,6 +36,7 @@ import "../node_modules/bootstrap-icons/icons/people.svg"; // users management // calendar/event icons import "../node_modules/bootstrap-icons/icons/calendar2.svg"; import "../node_modules/bootstrap-icons/icons/calendar2-plus.svg"; +import "../node_modules/bootstrap-icons/icons/calendar2-date.svg"; import "../node_modules/bootstrap-icons/icons/calendar2-event.svg"; import "../node_modules/bootstrap-icons/icons/calendar2-range.svg"; import "../node_modules/bootstrap-icons/icons/clock-history.svg"; // shift template @@ -53,6 +54,7 @@ import "../node_modules/bootstrap-icons/icons/binoculars-fill.svg"; import "../node_modules/bootstrap-icons/icons/eraser.svg"; import "../node_modules/bootstrap-icons/icons/save.svg"; import "../node_modules/bootstrap-icons/icons/asterisk.svg"; +import "../node_modules/bootstrap-icons/icons/card-list.svg"; // webpack automatically bundles all modules in your // entry points. Those entry points can be configured diff --git a/lib/shift73k/ecto_enums.ex b/lib/shift73k/ecto_enums.ex index e00ed388..18e08674 100644 --- a/lib/shift73k/ecto_enums.ex +++ b/lib/shift73k/ecto_enums.ex @@ -1,7 +1,8 @@ defmodule Shift73k.EctoEnums do import EctoEnum - @weekdays [:mon, :tue, :wed, :thu, :fri, :sat, :sun] |> Enum.with_index(1) + @weekdays [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday] + |> Enum.with_index(1) defenum(WeekdayEnum, @weekdays) end diff --git a/lib/shift73k/shifts.ex b/lib/shift73k/shifts.ex index 8f2aefaa..dcb55b5c 100644 --- a/lib/shift73k/shifts.ex +++ b/lib/shift73k/shifts.ex @@ -21,26 +21,26 @@ defmodule Shift73k.Shifts do Repo.all(Shift) end - def list_shifts_by_user_between_dates(user_id, start_date, end_date) do + defp query_shifts_by_user(user_id) do from(s in Shift) - |> select([s], %{ - date: s.date, - subject: s.subject, - time_start: s.time_start, - time_end: s.time_end - }) - |> where([s], s.user_id == ^user_id and s.date >= ^start_date and s.date <= ^end_date) + |> where([s], s.user_id == ^user_id) + end + + def list_shifts_by_user_in_date_range(user_id, %Date.Range{} = date_range) do + query_shifts_by_user(user_id) + |> where([s], s.date >= ^date_range.first) + |> where([s], s.date <= ^date_range.last) |> 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) + defp query_shifts_by_user_from_list_of_dates(user_id, date_list) do + query_shifts_by_user(user_id) + |> where([s], 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) + def list_shifts_by_user_from_list_of_dates(user_id, date_list) do + query_shifts_by_user_from_list_of_dates(user_id, date_list) |> Repo.all() end @@ -112,8 +112,8 @@ defmodule Shift73k.Shifts do Repo.delete(shift) 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) + def delete_shifts_by_user_from_list_of_dates(user_id, date_list) do + query_shifts_by_user_from_list_of_dates(user_id, date_list) |> Repo.delete_all() end diff --git a/lib/shift73k_web/live/shift_assign_live/delete_component.ex b/lib/shift73k_web/live/shift_assign_live/delete_component.ex index 8dc279cb..69f40f97 100644 --- a/lib/shift73k_web/live/shift_assign_live/delete_component.ex +++ b/lib/shift73k_web/live/shift_assign_live/delete_component.ex @@ -29,7 +29,7 @@ defmodule Shift73kWeb.ShiftAssignLive.DeleteComponent do 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) + {n, _} = Shifts.delete_shifts_by_user_from_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}) diff --git a/lib/shift73k_web/live/shift_assign_live/index.ex b/lib/shift73k_web/live/shift_assign_live/index.ex index 91640d52..38c2db9c 100644 --- a/lib/shift73k_web/live/shift_assign_live/index.ex +++ b/lib/shift73k_web/live/shift_assign_live/index.ex @@ -27,6 +27,8 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do @impl true def handle_params(_params, _url, socket) do + user = socket.assigns.current_user + socket |> init_shift_templates() |> init_shift_template() @@ -34,9 +36,9 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do |> assign_shift_length() |> assign_shift_template_changeset() |> assign_modal_close_handlers() + |> assign(:day_names, day_names(user.week_start_at)) |> init_today(Timex.today()) - |> init_calendar() - |> assign_known_shifts() + |> update_calendar() |> live_noreply() end @@ -52,20 +54,12 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do assign(socket, :shift_length, format_shift_length(shift)) end - defp assign_known_shifts(socket) do - user = socket.assigns.current_user - first = socket.assigns.day_first - last = socket.assigns.day_last - known_shifts = Shifts.list_shifts_by_user_between_dates(user.id, first, last) + defp assign_known_shifts(%{assigns: %{current_user: user}} = socket) do + date_range = socket.assigns.date_range + known_shifts = Shifts.list_shifts_by_user_in_date_range(user.id, date_range) assign(socket, :known_shifts, known_shifts) end - defp init_calendar(%{assigns: %{current_user: user}} = socket) do - days = day_names(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) - end - defp init_today(socket, today) do assign(socket, current_date: today, cursor_date: today) end @@ -88,7 +82,7 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do defp init_shift_templates(%{assigns: %{current_user: user}} = socket) do shift_templates = Templates.list_shift_templates_by_user_id(user.id) - |> Enum.map(fn t -> shift_template_option(t, user.fave_shift_template_id) end) + |> Stream.map(fn t -> shift_template_option(t, user.fave_shift_template_id) end) |> Enum.concat([@custom_shift_opt]) assign(socket, :shift_templates, shift_templates) @@ -123,23 +117,31 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do |> Enum.map(&Timex.day_shortname/1) end - defp week_rows(cursor_date, week_start_at) do - first = - cursor_date - |> Timex.beginning_of_month() - |> Timex.beginning_of_week(week_start_at) - + defp date_range(cursor_date, week_start_at) do last = cursor_date |> Timex.end_of_month() |> Timex.end_of_week(week_start_at) - week_rows = - Interval.new(from: first, until: last, right_open: false) - |> Enum.map(&NaiveDateTime.to_date(&1)) - |> Enum.chunk_every(7) + cursor_date + |> Timex.beginning_of_month() + |> Timex.beginning_of_week(week_start_at) + |> Date.range(last) + end - {first, last, week_rows} + defp assign_date_range(%{assigns: %{current_user: user}} = socket) do + date_range = date_range(socket.assigns.cursor_date, user.week_start_at) + assign(socket, :date_range, date_range) + end + + defp week_rows(%Date.Range{} = date_range) do + Interval.new(from: date_range.first, until: date_range.last, right_open: false) + |> Stream.map(&NaiveDateTime.to_date(&1)) + |> Enum.chunk_every(7) + end + + defp assign_week_rows(%{assigns: %{date_range: date_range}} = socket) do + assign(socket, :week_rows, week_rows(date_range)) end def day_color(day, current_date, cursor_date, selected_days) do @@ -180,17 +182,11 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do end end - defp set_month(socket, new_cursor_date) do - {first, last, rows} = week_rows(new_cursor_date, socket.assigns.current_user.week_start_at) - - assigns = [ - cursor_date: new_cursor_date, - week_rows: rows, - day_first: first, - day_last: last - ] - - assign(socket, assigns) |> assign_known_shifts() + defp update_calendar(socket) do + socket + |> assign_date_range() + |> assign_week_rows() + |> assign_known_shifts() end @impl true @@ -236,7 +232,10 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do Timex.shift(socket.assigns.cursor_date, months: months) end - {:noreply, set_month(socket, new_cursor)} + socket + |> assign(:cursor_date, new_cursor) + |> update_calendar() + |> live_noreply() end @impl true @@ -260,9 +259,9 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do @impl true def handle_event("select-day", %{"day" => day}, socket) do selected_days = - case day_index = Enum.find_index(socket.assigns.selected_days, fn d -> d == day end) do - nil -> [day | socket.assigns.selected_days] - _ -> List.delete_at(socket.assigns.selected_days, day_index) + case Enum.member?(socket.assigns.selected_days, day) do + false -> [day | socket.assigns.selected_days] + true -> Enum.reject(socket.assigns.selected_days, fn d -> d == day end) end {:noreply, assign(socket, :selected_days, selected_days)} diff --git a/lib/shift73k_web/live/shift_assign_live/index.html.leex b/lib/shift73k_web/live/shift_assign_live/index.html.leex index a8160bfa..81b7e550 100644 --- a/lib/shift73k_web/live/shift_assign_live/index.html.leex +++ b/lib/shift73k_web/live/shift_assign_live/index.html.leex @@ -10,7 +10,7 @@

<%= icon_div @socket, "bi-calendar2-plus", [class: "icon baseline"] %> - Assign Shift To Dates + Schedule Shifts

diff --git a/lib/shift73k_web/live/shift_live/index.ex b/lib/shift73k_web/live/shift_live/index.ex index 59c7777e..7a03e55b 100644 --- a/lib/shift73k_web/live/shift_live/index.ex +++ b/lib/shift73k_web/live/shift_live/index.ex @@ -1,17 +1,44 @@ defmodule Shift73kWeb.ShiftLive.Index do use Shift73kWeb, :live_view + use Timex alias Shift73k.Shifts alias Shift73k.Shifts.Shift + alias Shift73kWeb.Roles @impl true - def mount(_params, _session, socket) do - {:ok, assign(socket, :shifts, list_shifts())} + def mount(_params, session, socket) do + socket + |> assign_defaults(session) + |> live_okreply() + + # {:ok, assign(socket, :shifts, list_shifts())} end @impl true def handle_params(params, _url, socket) do - {:noreply, apply_action(socket, socket.assigns.live_action, params)} + current_user = socket.assigns.current_user + live_action = socket.assigns.live_action + shift = shift_from_params(params) + + if Roles.can?(current_user, shift, live_action) do + socket + # |> assign_shift_templates() + # |> assign_modal_close_handlers() + |> init_today(Timex.today()) + |> assign_date_range() + |> assign_known_shifts() + |> assign(:delete_shift, nil) + |> apply_action(socket.assigns.live_action, params) + |> live_noreply() + else + socket + |> put_flash(:error, "Unauthorised") + |> redirect(to: "/") + |> live_noreply() + end + + # {:noreply, apply_action(socket, socket.assigns.live_action, params)} end defp apply_action(socket, :edit, %{"id" => id}) do @@ -32,15 +59,57 @@ defmodule Shift73kWeb.ShiftLive.Index do |> assign(:shift, nil) end + defp shift_from_params(params) + + defp shift_from_params(%{"id" => id}), + do: Shifts.get_shift!(id) + + defp shift_from_params(_params), do: %Shift{} + + defp init_today(socket, today) do + assign(socket, current_date: today, cursor_date: today) + end + + defp assign_date_range(%{assigns: %{cursor_date: cursor_date}} = socket) do + assign(socket, date_range: date_range(cursor_date)) + end + + defp date_range(cursor_date) do + cursor_date + |> Timex.beginning_of_month() + |> Date.range(Timex.end_of_month(cursor_date)) + end + + defp assign_known_shifts(socket) do + user = socket.assigns.current_user + shifts = Shifts.list_shifts_by_user_in_date_range(user.id, socket.assigns.date_range) + assign(socket, :shifts, shifts) + end + @impl true def handle_event("delete", %{"id" => id}, socket) do shift = Shifts.get_shift!(id) {:ok, _} = Shifts.delete_shift(shift) - {:noreply, assign(socket, :shifts, list_shifts())} + {:noreply, assign_known_shifts(socket)} end - defp list_shifts do - Shifts.list_shifts() + @impl true + def handle_event("month-nav", %{"month" => direction}, socket) do + new_cursor = + cond do + direction == "now" -> + Timex.today() + + true -> + months = (direction == "prev" && -1) || 1 + Timex.shift(socket.assigns.cursor_date, months: months) + end + + socket + |> assign(:cursor_date, new_cursor) + |> assign_date_range() + |> assign_known_shifts() + |> live_noreply() end end diff --git a/lib/shift73k_web/live/shift_live/index.html.leex b/lib/shift73k_web/live/shift_live/index.html.leex index de61437e..6e427687 100644 --- a/lib/shift73k_web/live/shift_live/index.html.leex +++ b/lib/shift73k_web/live/shift_live/index.html.leex @@ -1,5 +1,3 @@ -

Listing Shifts

- <%= if @live_action in [:new, :edit] do %> <%= live_modal @socket, Shift73kWeb.ShiftLive.FormComponent, id: @shift.id || :new, @@ -9,41 +7,87 @@ return_to: Routes.shift_index_path(@socket, :index) %> <% end %> - - - - - - - - - - - - - - - - <%= for shift <- @shifts do %> - - - - - - - - - +
+
+ +

+ <%= icon_div @socket, "bi-calendar2-date", [class: "icon baseline"] %> + My Shifts +

+ +<%# month navigation %> +
+

+ <%= Timex.format!(@cursor_date, "{Mfull} {YYYY}") %> +

+
+ + + +
+
+ + +
+<%= for day <- Enum.to_list(@date_range) do %> + <% Date.day_of_week(day, @current_user.week_start_at) |> IO.inspect(label: "day in date_range") %> + <%= if Date.day_of_week(day, @current_user.week_start_at) == 1 do %> +
+ <% end %> +
+ <%= Timex.format!(day, "{WDfull}, {Mshort} {D}") %> +
+ <% day_shifts = Enum.filter(@shifts, fn s -> s.date == day end) %> + <%= if !Enum.empty?(day_shifts) do %> +
+ <%= for shift <- day_shifts do %> +
+
+
+ <%= format_shift_time(shift.time_start) |> String.trim_trailing("m") %> + — + <%= format_shift_time(shift.time_end) |> String.trim_trailing("m") %> +
+
<%= shift.time_zone %>
+
+
+
+ <%= shift.subject %> + <%= if shift.location do %> + (<%= shift.location %>) + <% end %> +
+
<%= shift.description %>
+
+ <%= live_redirect "Show", to: Routes.shift_show_path(@socket, :show, shift) %> + <%= live_patch "Edit", to: Routes.shift_index_path(@socket, :edit, shift) %> + <%= link "Delete", to: "#", phx_click: "delete", phx_value_id: shift.id, data: [confirm: "Are you sure?"] %> +
+
+
+ <% end %> + +
+ + <% else %> +
No shift scheduled
+ <% end %> +<% end %> +
-
- - <% end %> - -
SubjectLocationDescriptionTime zoneDateTime zoneTime startTime end
<%= shift.subject %><%= shift.location %><%= shift.description %><%= shift.time_zone %><%= shift.date %><%= shift.time_zone %><%= shift.time_start %><%= shift.time_end %> - <%= live_redirect "Show", to: Routes.shift_show_path(@socket, :show, shift) %> - <%= live_patch "Edit", to: Routes.shift_index_path(@socket, :edit, shift) %> - <%= link "Delete", to: "#", phx_click: "delete", phx_value_id: shift.id, data: [confirm: "Are you sure?"] %> -
<%= live_patch "New Shift", to: Routes.shift_index_path(@socket, :new) %> + + +
+ diff --git a/lib/shift73k_web/live/shift_live/show.html.leex b/lib/shift73k_web/live/shift_live/show.html.leex index 2529e09f..c30c705f 100644 --- a/lib/shift73k_web/live/shift_live/show.html.leex +++ b/lib/shift73k_web/live/shift_live/show.html.leex @@ -26,11 +26,6 @@ <%= @shift.description %> -
  • - Time zone: - <%= @shift.time_zone %> -
  • -
  • Date: <%= @shift.date %> diff --git a/lib/shift73k_web/roles.ex b/lib/shift73k_web/roles.ex index f020aa17..cfacedbb 100644 --- a/lib/shift73k_web/roles.ex +++ b/lib/shift73k_web/roles.ex @@ -5,6 +5,7 @@ defmodule Shift73kWeb.Roles do alias Shift73k.Accounts.User alias Shift73k.Shifts.Templates.ShiftTemplate + alias Shift73k.Shifts.Shift @type entity :: struct() @type action :: :new | :index | :edit | :show | :delete | :edit_role @@ -16,11 +17,19 @@ defmodule Shift73kWeb.Roles do def can?(%User{role: :admin}, %ShiftTemplate{}, _any), do: true def can?(%User{}, %ShiftTemplate{}, :index), do: true def can?(%User{}, %ShiftTemplate{}, :new), do: true - # def can?(%User{}, %ShiftTemplate{}, :show), do: true + # def can?(%User{id: id}, %ShiftTemplate{user_id: id}, :show), do: true def can?(%User{id: id}, %ShiftTemplate{user_id: id}, :edit), do: true def can?(%User{id: id}, %ShiftTemplate{user_id: id}, :clone), do: true def can?(%User{id: id}, %ShiftTemplate{user_id: id}, :delete), do: true + # Shifts / Shift + def can?(%User{role: :admin}, %Shift{}, _any), do: true + def can?(%User{}, %Shift{}, :index), do: true + def can?(%User{}, %Shift{}, :new), do: true + def can?(%User{id: id}, %Shift{user_id: id}, :show), do: true + def can?(%User{id: id}, %Shift{user_id: id}, :edit), do: true + def can?(%User{id: id}, %Shift{user_id: id}, :delete), do: true + # Accounts / User def can?(%User{role: :admin}, %User{}, _any), do: true def can?(%User{role: :manager}, %User{}, :index), do: true diff --git a/lib/shift73k_web/templates/layout/navbar/_shifts_menu.html.eex b/lib/shift73k_web/templates/layout/navbar/_shifts_menu.html.eex index f5bc55ba..17c6870d 100644 --- a/lib/shift73k_web/templates/layout/navbar/_shifts_menu.html.eex +++ b/lib/shift73k_web/templates/layout/navbar/_shifts_menu.html.eex @@ -10,7 +10,13 @@
  • <%= 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 + Schedule Shifts + <% end %> +
  • +
  • + <%= link nav_link_opts(@conn, to: Routes.shift_index_path(@conn, :index), class: "dropdown-item") do %> + <%= icon_div @conn, "bi-card-list", [class: "icon baseline me-1"] %> + My Scheduled Shifts <% end %>
  • diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 5d61641f..deacba92 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -58,7 +58,7 @@ extra_mock_users = ~s([ mock_users = extra_mock_users |> Jason.decode!() - |> Enum.concat(mock_users) + |> Stream.concat(mock_users) |> Enum.map(fn e -> add_dt = NaiveDateTime.from_iso8601!(e["inserted_at"])