main shift assign UI updated, with custom shift field fixes & shift template form field fix for time_zone

This commit is contained in:
Adam Piontek 2022-08-14 11:25:28 -04:00
parent 8cd984adc5
commit 24642d7c67
7 changed files with 89 additions and 71 deletions

View file

@ -57,6 +57,7 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
[] []
end end
end) end)
|> validate_not_nil([:time_zone])
|> validate_inclusion(:time_zone, Tzdata.zone_list(), |> validate_inclusion(:time_zone, Tzdata.zone_list(),
message: "must be a valid IANA tz database time zone" message: "must be a valid IANA tz database time zone"
) )
@ -72,4 +73,14 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|> Map.from_struct() |> Map.from_struct()
|> Map.drop([:__meta__, :id, :inserted_at, :updated_at, :user, :is_fave_of_user]) |> Map.drop([:__meta__, :id, :inserted_at, :updated_at, :user, :is_fave_of_user])
end end
def validate_not_nil(changeset, fields) do
Enum.reduce(fields, changeset, fn field, changeset ->
if get_field(changeset, field) == nil do
add_error(changeset, field, "nil")
else
changeset
end
end)
end
end end

View file

@ -0,0 +1,35 @@
<div>
<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() |> Calendar.strftime("%b") %>:
<% 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>
</div>

View file

@ -1,31 +0,0 @@
<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() |> Calendar.strftime("%b") %>:
<% 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>

View file

@ -1,5 +1,6 @@
defmodule Shift73kWeb.ShiftAssignLive.Index do defmodule Shift73kWeb.ShiftAssignLive.Index do
use Shift73kWeb, :live_view use Shift73kWeb, :live_view
import Shift73k, only: [app_time_zone: 0]
alias Shift73k.Repo alias Shift73k.Repo
alias Shift73k.Shifts alias Shift73k.Shifts

View file

@ -23,14 +23,19 @@
%> %>
<% end %> <% 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"> <% details_button_class = "ms-2 btn btn-primary text-nowrap"
<%= icon_div @socket, (@show_template_btn_active && "bi-binoculars-fill" || "bi-binoculars"), [class: "icon baseline"] %> details_button_class = if @show_template_btn_active, do: "#{details_button_class} active", else: details_button_class %>
<button type="button" class={details_button_class} id="#templateDetailsBtn" phx-click="toggle-template-details" phx-value-target_id="#templateDetailsCol">
<i class={@show_template_btn_active && "bi bi-binoculars-fill me-sm-1" || "bi bi-binoculars me-sm-1"}></i>
<span class="d-none d-sm-inline">Details</span> <span class="d-none d-sm-inline">Details</span>
</button> </button>
</div> </div>
<div class="col-12 col-lg-9 col-xl-8 col-xxl-7 <%= @show_template_details && "collapse show" || "collapse" %>" id="#templateDetailsCol" phx-hook="BsCollapse">
<% template_details_div_class = "col-12 col-lg-9 col-xl-8 col-xxl-7 collapse"
template_details_div_class = if @show_template_details, do: "#{template_details_div_class} show", else: template_details_div_class %>
<div class={template_details_div_class} id="#templateDetailsCol" phx-hook="BsCollapse">
<div class="card mt-4"> <div class="card mt-4">
<div class="card-body"> <div class="card-body">
@ -40,8 +45,8 @@
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<%= label stf, :subject, "Subject/Title", class: "form-label" %> <%= label stf, :subject, "Subject/Title", class: "form-label" %>
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(stf, :subject) %>"> <div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(stf, :subject)}>
<%= icon_div @socket, "bi-tag", [class: "icon is-left text-muted fs-5"] %> <i class="bi bi-tag icon is-left text-muted fs-5"></i>
<%= text_input stf, :subject, <%= text_input stf, :subject,
value: input_value(stf, :subject), value: input_value(stf, :subject),
class: input_class(stf, :subject, "form-control"), class: input_class(stf, :subject, "form-control"),
@ -56,25 +61,27 @@
<div class="col-12 col-md-6 mb-3"> <div class="col-12 col-md-6 mb-3">
<div class="row gx-2 gx-sm-3"> <div class="row gx-2 gx-sm-3">
<div class="col-6" phx-feedback-for="<%= input_id(stf, :time_start) %>"> <div class="col-6" phx-feedback-for={input_id(stf, :time_start)}>
<%= label stf, :time_start, "Start", class: "form-label" %> <%= label stf, :time_start, "Start", class: "form-label" %>
<%= time_input stf, :time_start, <%= time_input stf, :time_start,
precision: :minute, precision: :minute,
value: input_value(stf, :time_start), value: input_value(stf, :time_start),
class: input_class(stf, :time_start, "form-control"), class: input_class(stf, :time_start, "form-control"),
disabled: @shift_template.id != @custom_shift.id, disabled: @shift_template.id != @custom_shift.id,
aria_describedby: error_ids(stf, :time_start) aria_describedby: error_ids(stf, :time_start),
required: true
%> %>
</div> </div>
<div class="col-6" phx-feedback-for="<%= input_id(stf, :time_end) %>"> <div class="col-6" phx-feedback-for={input_id(stf, :time_end)}>
<%= label stf, :time_end, "End", class: "form-label" %> <%= label stf, :time_end, "End", class: "form-label" %>
<%= time_input stf, :time_end, <%= time_input stf, :time_end,
precision: :minute, precision: :minute,
value: input_value(stf, :time_end), value: input_value(stf, :time_end),
class: input_class(stf, :time_end, "form-control"), class: input_class(stf, :time_end, "form-control"),
disabled: @shift_template.id != @custom_shift.id, disabled: @shift_template.id != @custom_shift.id,
aria_describedby: error_ids(stf, :time_end) aria_describedby: error_ids(stf, :time_end),
required: true
%> %>
</div> </div>
@ -82,18 +89,18 @@
<div class="valid-feedback d-block text-primary">Shift length: <%= @shift_length %></div> <div class="valid-feedback d-block text-primary">Shift length: <%= @shift_length %></div>
<div class="phx-orphaned-feedback" phx-feedback-for="<%= input_id(stf, :time_start) %>"> <div class="phx-orphaned-feedback" phx-feedback-for={input_id(stf, :time_start)}>
<%= error_tag stf, :time_start %> <%= error_tag stf, :time_start %>
</div> </div>
<div class="phx-orphaned-feedback" phx-feedback-for="<%= input_id(stf, :time_end) %>"> <div class="phx-orphaned-feedback" phx-feedback-for={input_id(stf, :time_end)}>
<%= error_tag stf, :time_end %> <%= error_tag stf, :time_end %>
</div> </div>
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<%= label stf, :location, class: "form-label" %> <%= label stf, :location, class: "form-label" %>
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(stf, :location) %>"> <div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(stf, :location)}>
<%= icon_div @socket, "bi-geo", [class: "icon is-left text-muted fs-5"] %> <i class="bi bi-geo icon is-left text-muted fs-5"></i>
<%= text_input stf, :location, <%= text_input stf, :location,
value: input_value(stf, :location), value: input_value(stf, :location),
class: input_class(stf, :location, "form-control"), class: input_class(stf, :location, "form-control"),
@ -107,18 +114,19 @@
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<%= label stf, :time_zone, class: "form-label" %> <%= label stf, :time_zone, class: "form-label" %>
<div class="inner-addon left-addon mb-3 mb-md-0" phx-feedback-for="<%= input_id(stf, :time_zone) %>"> <div class="inner-addon left-addon mb-3 mb-md-0" phx-feedback-for={input_id(stf, :time_zone)}>
<%= icon_div @socket, "bi-map", [class: "icon is-left text-muted fs-5"] %> <i class="bi bi-map icon is-left text-muted fs-5"></i>
<%= text_input stf, :time_zone, <%= text_input stf, :time_zone,
value: input_value(stf, :time_zone), value: input_value(stf, :time_zone),
class: input_class(stf, :time_zone, "form-control"), class: input_class(stf, :time_zone, "form-control"),
disabled: @shift_template.id != @custom_shift.id, disabled: @shift_template.id != @custom_shift.id,
phx_debounce: 250, phx_debounce: 250,
list: "tz_list" list: "tz_list",
placeholder: "Default: #{app_time_zone()}"
%> %>
<datalist id="tz_list"> <datalist id="tz_list">
<%= for tz_name <- Tzdata.zone_list() do %> <%= for tz_name <- Tzdata.zone_list() do %>
<option value="<%= tz_name %>"></option> <option value={tz_name}></option>
<% end %> <% end %>
end end
</datalist> </datalist>
@ -131,7 +139,7 @@
<div class="col-12"> <div class="col-12">
<%= label stf, :description, class: "form-label" %> <%= label stf, :description, class: "form-label" %>
<div phx-feedback-for="<%= input_id(stf, :description) %>"> <div phx-feedback-for={input_id(stf, :description)}>
<%= textarea stf, :description, <%= textarea stf, :description,
value: input_value(stf, :description), value: input_value(stf, :description),
@ -173,17 +181,17 @@
<%= Calendar.strftime(@cursor_date, "%B %Y") %> <%= Calendar.strftime(@cursor_date, "%B %Y") %>
</h3> </h3>
<div> <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(Date.utc_today(), :month), do: "disabled" %>> <button type="button" phx-click="month-nav" phx-value-month="now" class="btn btn-info text-white" disabled={if Map.get(@cursor_date, :month) == Map.get(Date.utc_today(), :month), do: :true, else: :false}>
<%= icon_div @socket, "bi-asterisk", [class: "icon baseline"] %> <i class="bi bi-asterisk me-sm-1"></i>
<span class="d-none d-sm-inline">Today</span> <span class="d-none d-sm-inline">Today</span>
</button> </button>
<button type="button" phx-click="month-nav" phx-value-month="prev" class="btn btn-primary"> <button type="button" phx-click="month-nav" phx-value-month="prev" class="btn btn-primary">
<%= icon_div @socket, "bi-chevron-left", [class: "icon baseline"] %> <i class="bi bi-chevron-left me-sm-1"></i>
<span class="d-none d-sm-inline">Prev</span> <span class="d-none d-sm-inline">Prev</span>
</button> </button>
<button type="button" phx-click="month-nav" phx-value-month="next" class="btn btn-primary"> <button type="button" phx-click="month-nav" phx-value-month="next" class="btn btn-primary">
<span class="d-none d-sm-inline">Next</span> <span class="d-none d-sm-inline">Next</span>
<%= icon_div @socket, "bi-chevron-right", [class: "icon baseline", style: "margin-left:0.125rem;"] %> <i class="bi bi-chevron-right ms-sm-1"></i>
</button> </button>
</div> </div>
</div> </div>
@ -203,7 +211,7 @@
<%= for week <- @week_rows do %> <%= for week <- @week_rows do %>
<tr> <tr>
<%= for day <- week do %> <%= for day <- week do %>
<td class="<%= day_color(day, @current_date, @cursor_date, @selected_days) %>" phx-click="select-day" phx-value-day="<%= day %>"> <td class={day_color(day, @current_date, @cursor_date, @selected_days)} phx-click="select-day" phx-value-day={day}>
<%= Calendar.strftime(day, "%d") %><%= if day.month != @cursor_date.month, do: "-#{Calendar.strftime(day, "%b")}" %> <%= Calendar.strftime(day, "%d") %><%= if day.month != @cursor_date.month, do: "-#{Calendar.strftime(day, "%b")}" %>
@ -235,19 +243,16 @@
<div class="row justify-content-center justify-content-lg-end my-5"> <div class="row justify-content-center justify-content-lg-end my-5">
<div class="col-12 col-sm-10 col-md-8 col-lg-auto d-flex flex-column-reverse flex-lg-row"> <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-danger mb-1 mb-lg-0 me-lg-1" phx-click="delete-days-shifts" <%= if Enum.empty?(@selected_days), do: "disabled" %>> <button class="btn btn-outline-danger mb-1 mb-lg-0 me-lg-1" phx-click="delete-days-shifts" disabled={if Enum.empty?(@selected_days), do: :true, else: :false}>
<%= icon_div @socket, "bi-trash", [class: "icon baseline"] %> <i class="bi bi-trash me-1"></i> Delete shifts from selected days
Delete shifts from selected days
</button> </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" %>> <button class="btn btn-outline-dark mb-1 mb-lg-0 me-lg-1" phx-click="clear-days" disabled={if Enum.empty?(@selected_days), do: :true, else: :false}>
<%= icon_div @socket, "bi-eraser", [class: "icon baseline"] %> <i class="bi bi-eraser me-1"></i> De-select all selected
De-select all selected
</button> </button>
<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" %>> <button class="btn btn-primary mb-1 mb-lg-0" phx-click="save-days" disabled={if (!@shift_template_changeset.valid? || Enum.empty?(@selected_days)), do: :true, else: :false}>
<%= icon_div @socket, "bi-save", [class: "icon baseline"] %> <i class="bi bi-save me-1"></i> Assign shifts to selected days
Assign shifts to selected days
</button> </button>
</div> </div>

View file

@ -1,5 +1,6 @@
defmodule Shift73kWeb.ShiftTemplateLive.FormComponent do defmodule Shift73kWeb.ShiftTemplateLive.FormComponent do
use Shift73kWeb, :live_component use Shift73kWeb, :live_component
import Shift73k, only: [app_time_zone: 0]
alias Shift73k.Shifts.Templates alias Shift73k.Shifts.Templates
alias Shift73k.Shifts.Templates.ShiftTemplate alias Shift73k.Shifts.Templates.ShiftTemplate

View file

@ -1,11 +1,6 @@
<div> <div>
<%= form_for @changeset, "#", [ <.form let={f} for={@changeset} phx-change="validate" phx-submit="save" phx-target={@myself} id="shift_template-form">
id: "shift_template-form",
phx_target: @myself,
phx_change: "validate",
phx_submit: "save"
], fn f -> %>
<div class="modal-body"> <div class="modal-body">
@ -92,7 +87,8 @@
value: input_value(f, :time_zone), value: input_value(f, :time_zone),
class: input_class(f, :time_zone, "form-control"), class: input_class(f, :time_zone, "form-control"),
phx_debounce: 250, phx_debounce: 250,
list: "tz_list" list: "tz_list",
placeholder: "Default: #{app_time_zone()}"
%> %>
<datalist id="tz_list"> <datalist id="tz_list">
<%= for tz_name <- Tzdata.zone_list() do %> <%= for tz_name <- Tzdata.zone_list() do %>
@ -118,6 +114,6 @@
</div> </div>
<% end %> </.form>
</div> </div>