saving shifts working
This commit is contained in:
parent
4276399c20
commit
ecd4d83e3f
9 changed files with 141 additions and 67 deletions
|
@ -31,7 +31,7 @@
|
|||
@import "../node_modules/bootstrap/scss/accordion";
|
||||
// @import "../node_modules/bootstrap/scss/breadcrumb";
|
||||
@import "../node_modules/bootstrap/scss/pagination";
|
||||
// @import "../node_modules/bootstrap/scss/badge";
|
||||
@import "../node_modules/bootstrap/scss/badge";
|
||||
@import "../node_modules/bootstrap/scss/alert";
|
||||
@import "../node_modules/bootstrap/scss/progress";
|
||||
// @import "../node_modules/bootstrap/scss/list-group";
|
||||
|
|
|
@ -63,16 +63,32 @@
|
|||
}
|
||||
|
||||
/* calendar table rounded */
|
||||
table.table.table-calendar span.badge {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
white-space: nowrap;
|
||||
padding-right: 0.7em;
|
||||
border-right: 0.7em solid transparent;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
table.table.table-calendar thead tr th,
|
||||
table.table.table-calendar tbody tr td {
|
||||
width: 14%;
|
||||
width: 2.5rem;
|
||||
max-width: 2.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.table.table-calendar tbody tr td {
|
||||
font-size: $font-size-sm;
|
||||
height: 3.5rem;
|
||||
height: 4.1rem;
|
||||
padding: 0.2rem 0.4rem;
|
||||
@include media-breakpoint-up(sm) {
|
||||
height: 4.3rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
}
|
||||
@include media-breakpoint-up(md) {
|
||||
height: 4.5rem;
|
||||
height: 4.7rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
}
|
||||
@include media-breakpoint-up(lg) {
|
||||
|
@ -90,6 +106,9 @@ table.table.table-calendar tbody tr td {
|
|||
}
|
||||
}
|
||||
|
||||
table.table.table-rounded > :not(:last-child) > :last-child > * {
|
||||
border-bottom-color: $dark;
|
||||
}
|
||||
table.table.table-rounded {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
|
@ -103,7 +122,7 @@ table.table.table-rounded {
|
|||
th {
|
||||
border-top: 1px solid $table-border-color;
|
||||
border-right: 1px solid $table-border-color;
|
||||
border-bottom: 2px solid $black !important;
|
||||
// border-bottom: 2px solid $dark !important;
|
||||
border-left: none;
|
||||
&:first-child {
|
||||
border-top-left-radius: $border-radius;
|
||||
|
|
|
@ -21,6 +21,16 @@ defmodule Shift73k.Shifts do
|
|||
Repo.all(Shift)
|
||||
end
|
||||
|
||||
def list_shifts_by_user_between_dates(user_id, start_date, end_date) do
|
||||
q = from(
|
||||
s in Shift,
|
||||
select: %{date: s.date, subject: s.subject, time_start: s.time_start, time_end: s.time_end},
|
||||
where: s.user_id == ^user_id and s.date >= ^start_date and s.date < ^end_date,
|
||||
order_by: [s.date, s.time_start]
|
||||
)
|
||||
Repo.all(q)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single shift.
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ defmodule Shift73k.Shifts.Templates do
|
|||
end
|
||||
|
||||
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
|
||||
q = from s in ShiftTemplate, where: s.user_id == ^user_id, order_by: [s.subject, s.time_start]
|
||||
Repo.all(q)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
|||
use Shift73kWeb, :live_view
|
||||
use Timex
|
||||
|
||||
alias Ecto.Multi
|
||||
alias Shift73k.Repo
|
||||
alias Shift73k.EctoEnums.WeekdayEnum
|
||||
alias Shift73k.Shifts
|
||||
alias Shift73k.Shifts.Shift
|
||||
alias Shift73k.Shifts.Templates
|
||||
alias Shift73k.Shifts.Templates.ShiftTemplate
|
||||
|
||||
|
@ -30,7 +34,7 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
|||
|> assign_shift_template_changeset()
|
||||
|> init_today(Timex.today())
|
||||
|> init_calendar()
|
||||
|> init_known_shifts()
|
||||
|> assign_known_shifts()
|
||||
|> live_noreply()
|
||||
end
|
||||
|
||||
|
@ -41,10 +45,12 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
|||
assign(socket, :shift_length, format_shift_length(shift))
|
||||
end
|
||||
|
||||
defp init_known_shifts(%{assigns: %{current_user: user, week_rows: weeks}} = socket) do
|
||||
days = weeks |> List.flatten()
|
||||
first = weeks |> List.flatten() |> List.first()
|
||||
socket
|
||||
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)
|
||||
assign(socket, :known_shifts, known_shifts)
|
||||
end
|
||||
|
||||
defp init_calendar(%{assigns: %{current_user: user}} = socket) do
|
||||
|
@ -170,7 +176,7 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
|||
day_last: last
|
||||
]
|
||||
|
||||
assign(socket, assigns)
|
||||
assign(socket, assigns) |> assign_known_shifts()
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
@ -206,41 +212,13 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
|||
cond do
|
||||
direction == "now" -> Timex.today()
|
||||
true ->
|
||||
months = m = direction == "prev" && -1 || 1
|
||||
months = direction == "prev" && -1 || 1
|
||||
Timex.shift(socket.assigns.cursor_date, months: months)
|
||||
end
|
||||
|
||||
{:noreply, set_month(socket, new_cursor)}
|
||||
end
|
||||
|
||||
# @impl true
|
||||
# def handle_event("prev-month", _, socket) do
|
||||
# cursor_date = Timex.shift(socket.assigns.cursor_date, months: -1)
|
||||
# {first, last, rows} = week_rows(cursor_date, socket.assigns.current_user.week_start_at)
|
||||
|
||||
# assigns = [
|
||||
# cursor_date: cursor_date,
|
||||
# week_rows: week_rows(cursor_date, socket.assigns.current_user.week_start_at)
|
||||
# ]
|
||||
|
||||
# {:noreply, assign(socket, assigns)}
|
||||
# end
|
||||
|
||||
# @impl true
|
||||
# def handle_event("next-month", _, socket) do
|
||||
# cursor_date = Timex.shift(socket.assigns.cursor_date, months: 1)
|
||||
# {first, last, rows} = week_rows(cursor_date, socket.assigns.current_user.week_start_at)
|
||||
|
||||
# assigns = [
|
||||
# cursor_date: cursor_date,
|
||||
# week_rows: rows,
|
||||
# day_first: first,
|
||||
# day_last: last
|
||||
# ]
|
||||
|
||||
# {:noreply, assign(socket, assigns)}
|
||||
# end
|
||||
|
||||
@impl true
|
||||
def handle_event("toggle-template-details", %{"target_id" => target_id}, socket) do
|
||||
socket
|
||||
|
@ -274,4 +252,55 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
|||
def handle_event("clear-days", _params, socket) do
|
||||
{:noreply, assign(socket, :selected_days, [])}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("save-days", _params, socket) do
|
||||
# 1. collect attrs from loaded shift template
|
||||
shift_data = shift_data_from_template(socket.assigns.shift_template)
|
||||
|
||||
# 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))
|
||||
|
||||
# 3. insert the data
|
||||
{status, msg} = insert_shifts(to_insert, length(socket.assigns.selected_days))
|
||||
|
||||
socket
|
||||
|> put_flash(status, msg)
|
||||
|> assign(:selected_days, [])
|
||||
|> assign_known_shifts()
|
||||
|> live_noreply()
|
||||
end
|
||||
|
||||
defp shift_data_from_template(shift_template) do
|
||||
shift_template
|
||||
|> Map.from_struct()
|
||||
|> Map.drop([:__meta__, :id, :inserted_at, :updated_at, :user])
|
||||
end
|
||||
|
||||
defp shift_from_day_and_shift_data(day, shift_data) do
|
||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||
date_data = %{date: Date.from_iso8601!(day), inserted_at: now, updated_at: now}
|
||||
Map.merge(shift_data, date_data)
|
||||
end
|
||||
|
||||
defp insert_shifts(to_insert, day_count) do
|
||||
Multi.new()
|
||||
|> Multi.insert_all(:insert_all, Shift, to_insert)
|
||||
|> Repo.transaction()
|
||||
|> case do
|
||||
{:ok, %{insert_all: {n, _}}} ->
|
||||
if n == day_count do
|
||||
{:success, "Successfully assigned shift to #{n} day(s)"}
|
||||
else
|
||||
{:warning, "Some error, only #{n} day(s) inserted but #{day_count} were selected"}
|
||||
end
|
||||
_ -> {:error, "Ope, unknown error inserting shifts, page the dev"}
|
||||
end
|
||||
end
|
||||
|
||||
def shifts_to_show(day_shifts) do
|
||||
if length(day_shifts) == 1 || length(day_shifts) > 2,
|
||||
do: Enum.take(day_shifts, 1),
|
||||
else: day_shifts
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</h2>
|
||||
|
||||
<div class="row justify-content-center mt-4">
|
||||
<div class="col-12 col-lg-9 col-xl-8 col-xxl-7 d-flex justify-content-start align-items-end">
|
||||
<div class="col-12 col-lg-9 col-xl-8 col-xxl-7 d-flex justify-content-center align-items-end">
|
||||
|
||||
<%= form_for :template_select, "#", [phx_change: "change-selected-template"], fn sts -> %>
|
||||
<%= label sts, :template, "Select shift template to assign to dates", class: "form-label" %>
|
||||
|
@ -15,8 +15,8 @@
|
|||
<% end %>
|
||||
|
||||
<button type="button" class="ms-2 btn btn-primary text-nowrap <%= if @show_template_btn_active, do: "active" %>" id="#templateDetailsBtn" phx-click="toggle-template-details" phx-value-target_id="#templateDetailsCol">
|
||||
<%= icon_div @socket, (@show_template_btn_active && "bi-binoculars-fill" || "bi-binoculars"), [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
Details
|
||||
<%= icon_div @socket, (@show_template_btn_active && "bi-binoculars-fill" || "bi-binoculars"), [class: "icon baseline"] %>
|
||||
<span class="d-none d-sm-inline">Details</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
@ -137,8 +137,6 @@
|
|||
|
||||
</div>
|
||||
|
||||
<button type="submit" class="d-hidden"></button>
|
||||
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
@ -167,15 +165,15 @@
|
|||
</h3>
|
||||
<div>
|
||||
<button type="button" phx-click="month-nav" phx-value-month="now" class="btn btn-info text-white" <%= if Map.get(@cursor_date, :month) == Map.get(Timex.today(), :month), do: "disabled" %>>
|
||||
<%= icon_div @socket, "bi-asterisk", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
Today
|
||||
<%= icon_div @socket, "bi-asterisk", [class: "icon baseline"] %>
|
||||
<span class="d-none d-sm-inline">Today</span>
|
||||
</button>
|
||||
<button type="button" phx-click="month-nav" phx-value-month="prev" class="btn btn-primary">
|
||||
<%= icon_div @socket, "bi-chevron-left", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
Prev
|
||||
<%= icon_div @socket, "bi-chevron-left", [class: "icon baseline"] %>
|
||||
<span class="d-none d-sm-inline">Prev</span>
|
||||
</button>
|
||||
<button type="button" phx-click="month-nav" phx-value-month="next" class="btn btn-primary">
|
||||
Next
|
||||
<span class="d-none d-sm-inline">Next</span>
|
||||
<%= icon_div @socket, "bi-chevron-right", [class: "icon baseline", style: "margin-left:0.125rem;"] %>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -196,9 +194,27 @@
|
|||
<%= for week <- @week_rows do %>
|
||||
<tr>
|
||||
<%= for day <- week do %>
|
||||
<%# day |> NaiveDateTime.to_date() |> IO.inspect() %>
|
||||
<td class="<%= day_color(day, @current_date, @cursor_date, @selected_days) %>" phx-click="select-day" phx-value-day="<%= day %>">
|
||||
|
||||
<%= Timex.format!(day, "{0D}") %><%= if day.month != @cursor_date.month, do: "-#{Timex.format!(day, "{Mshort}")}" %>
|
||||
|
||||
<% day_shifts = Enum.filter(@known_shifts, fn s -> s.date == day end) %>
|
||||
<% shifts_to_show = shifts_to_show(day_shifts) %>
|
||||
|
||||
<%= for shift <- shifts_to_show do %>
|
||||
<span class="badge bg-primary text-start d-block">
|
||||
<span>
|
||||
<%= shift.time_start |> Timex.format!("{h12}:{m}{am}") |> String.trim_trailing("m") %>
|
||||
<%= shift.subject %>
|
||||
</span>
|
||||
</span>
|
||||
<% end %>
|
||||
|
||||
<%= if length(day_shifts) > 2 do %>
|
||||
<span class="badge bg-primary text-start d-block"><span><%= length(day_shifts) - 1 %> more…</span></span>
|
||||
<% end %>
|
||||
|
||||
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
|
@ -207,16 +223,16 @@
|
|||
</table>
|
||||
|
||||
|
||||
<div class="row justify-content-end mt-4">
|
||||
<div class="row justify-content-end my-4">
|
||||
<div class="col-auto">
|
||||
|
||||
<button class="btn btn-warning" phx-click="clear-days">
|
||||
<%= icon_div @socket, "bi-eraser", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<button class="btn btn-outline-dark" phx-click="clear-days">
|
||||
<%= icon_div @socket, "bi-eraser", [class: "icon baseline"] %>
|
||||
Clear
|
||||
</button>
|
||||
|
||||
<button class="btn btn-primary" phx-click="save-days">
|
||||
<%= icon_div @socket, "bi-save", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<button class="btn btn-primary" phx-click="save-days" <%= if (!@shift_template_changeset.valid? || Enum.empty?(@selected_days)), do: "disabled" %>>
|
||||
<%= icon_div @socket, "bi-save", [class: "icon baseline"] %>
|
||||
Save assigned shifts
|
||||
</button>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
My Shift Templates
|
||||
</h2>
|
||||
<%= live_patch to: Routes.shift_template_index_path(@socket, :new), class: "btn btn-primary" do %>
|
||||
<%= icon_div @socket, "bi-plus-circle-dotted", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-plus-circle-dotted", [class: "icon baseline"] %>
|
||||
New Shift Template
|
||||
<% end %>
|
||||
</div>
|
||||
|
@ -100,14 +100,14 @@
|
|||
|
||||
<%= if Roles.can?(@current_user, template, :edit) do %>
|
||||
<%= live_patch to: Routes.shift_template_index_path(@socket, :edit, template), class: "btn btn-primary btn-sm text-nowrap" do %>
|
||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline"] %>
|
||||
Edit
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= if Roles.can?(@current_user, template, :clone) do %>
|
||||
<%= live_patch to: Routes.shift_template_index_path(@socket, :clone, template), class: "btn btn-outline-primary btn-sm text-nowrap" do %>
|
||||
<%= icon_div @socket, "bi-clipboard-plus", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-clipboard-plus", [class: "icon baseline"] %>
|
||||
Clone
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
@ -116,7 +116,7 @@
|
|||
|
||||
<%= if Roles.can?(@current_user, template, :delete) do %>
|
||||
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= template.id %>">
|
||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %>
|
||||
Delete
|
||||
</button>
|
||||
<% end %>
|
||||
|
|
|
@ -98,14 +98,14 @@
|
|||
|
||||
<%= if Roles.can?(@current_user, user, :edit) do %>
|
||||
<%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-primary btn-sm text-nowrap" do %>
|
||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline"] %>
|
||||
Edit
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= if Roles.can?(@current_user, user, :delete) do %>
|
||||
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>">
|
||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %>
|
||||
Delete
|
||||
</button>
|
||||
<% end %>
|
||||
|
@ -180,14 +180,14 @@
|
|||
|
||||
<%= if Roles.can?(@current_user, user, :edit) do %>
|
||||
<%= live_patch to: Routes.user_management_index_path(@socket, :edit, user.id, Enum.into(@query, [])), class: "btn btn-outline-primary btn-sm text-nowrap" do %>
|
||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-pencil", [class: "icon baseline"] %>
|
||||
Edit
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= if Roles.can?(@current_user, user, :delete) do %>
|
||||
<button class="btn btn-outline-danger btn-sm text-nowrap" phx-click="delete-modal" phx-value-id="<%= user.id %>">
|
||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %>
|
||||
Delete
|
||||
</button>
|
||||
<% end %>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</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 %>
|
||||
<%= icon_div @conn, "bi-door-open", [class: "icon baseline", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
|
||||
Log in
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
@ -70,7 +70,7 @@
|
|||
|
||||
<%= if !@current_user 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", style: "margin-right:0.125rem;"] %>
|
||||
<%= icon_div @conn, "bi-door-open", [class: "icon baseline"] %>
|
||||
Log in
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
Loading…
Reference in a new issue