From ecd4d83e3f58ca881b3a1063e8fcc5f8fb6289d7 Mon Sep 17 00:00:00 2001
From: Adam Piontek <adam@73k.us>
Date: Tue, 16 Mar 2021 11:00:33 -0400
Subject: [PATCH] saving shifts working

---
 assets/css/_bs-load.scss                      |  2 +-
 assets/css/app.scss                           | 27 ++++-
 lib/shift73k/shifts.ex                        | 10 ++
 lib/shift73k/shifts/templates.ex              |  2 +-
 .../live/shift_assign_live/index.ex           | 99 ++++++++++++-------
 .../live/shift_assign_live/index.html.leex    | 48 ++++++---
 .../live/shift_template_live/index.html.leex  |  8 +-
 .../live/user_management/index.html.leex      |  8 +-
 .../templates/layout/_navbar.html.eex         |  4 +-
 9 files changed, 141 insertions(+), 67 deletions(-)

diff --git a/assets/css/_bs-load.scss b/assets/css/_bs-load.scss
index b7c2aa2f..50153ea3 100644
--- a/assets/css/_bs-load.scss
+++ b/assets/css/_bs-load.scss
@@ -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";
diff --git a/assets/css/app.scss b/assets/css/app.scss
index 722c55f2..6bed80f0 100644
--- a/assets/css/app.scss
+++ b/assets/css/app.scss
@@ -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;
diff --git a/lib/shift73k/shifts.ex b/lib/shift73k/shifts.ex
index d1902b86..93078c87 100644
--- a/lib/shift73k/shifts.ex
+++ b/lib/shift73k/shifts.ex
@@ -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.
 
diff --git a/lib/shift73k/shifts/templates.ex b/lib/shift73k/shifts/templates.ex
index 993f73a7..286f555d 100644
--- a/lib/shift73k/shifts/templates.ex
+++ b/lib/shift73k/shifts/templates.ex
@@ -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
 
diff --git a/lib/shift73k_web/live/shift_assign_live/index.ex b/lib/shift73k_web/live/shift_assign_live/index.ex
index 01e7326c..60535bbd 100644
--- a/lib/shift73k_web/live/shift_assign_live/index.ex
+++ b/lib/shift73k_web/live/shift_assign_live/index.ex
@@ -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
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 6fe3c5f8..c5136885 100644
--- a/lib/shift73k_web/live/shift_assign_live/index.html.leex
+++ b/lib/shift73k_web/live/shift_assign_live/index.html.leex
@@ -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&hellip;</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>
 
diff --git a/lib/shift73k_web/live/shift_template_live/index.html.leex b/lib/shift73k_web/live/shift_template_live/index.html.leex
index 75e40f00..bb9bcd30 100644
--- a/lib/shift73k_web/live/shift_template_live/index.html.leex
+++ b/lib/shift73k_web/live/shift_template_live/index.html.leex
@@ -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 %>
diff --git a/lib/shift73k_web/live/user_management/index.html.leex b/lib/shift73k_web/live/user_management/index.html.leex
index d4f539ac..0279f882 100644
--- a/lib/shift73k_web/live/user_management/index.html.leex
+++ b/lib/shift73k_web/live/user_management/index.html.leex
@@ -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 %>
diff --git a/lib/shift73k_web/templates/layout/_navbar.html.eex b/lib/shift73k_web/templates/layout/_navbar.html.eex
index e4dff3bf..ccf4a72a 100644
--- a/lib/shift73k_web/templates/layout/_navbar.html.eex
+++ b/lib/shift73k_web/templates/layout/_navbar.html.eex
@@ -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 %>