main shift assign UI updated, with custom shift field fixes & shift template form field fix for time_zone
This commit is contained in:
parent
8cd984adc5
commit
24642d7c67
7 changed files with 89 additions and 71 deletions
|
@ -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
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue