shift template live crud working
This commit is contained in:
parent
9e8ecb8b89
commit
8aa4b8eee0
14 changed files with 9279 additions and 1123 deletions
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
/* hides the feedback field help if liveview indicates field not touched yet */
|
/* hides the feedback field help if liveview indicates field not touched yet */
|
||||||
.phx-no-feedback.invalid-feedback,
|
.phx-no-feedback.invalid-feedback,
|
||||||
.phx-no-feedback .invalid-feedback {
|
.phx-no-feedback .invalid-feedback,
|
||||||
|
.phx-orphaned-feedback.phx-no-feedback .invalid-feedback {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.phx-orphaned-feedback .invalid-feedback {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* sets default bootstrap form-control styles if field not touched yet */
|
/* sets default bootstrap form-control styles if field not touched yet */
|
||||||
.phx-no-feedback .form-control.is-valid,
|
.phx-no-feedback .form-control.is-valid,
|
||||||
|
|
|
@ -34,12 +34,14 @@ import "../node_modules/bootstrap-icons/icons/arrow-repeat.svg"; // resend confi
|
||||||
import "../node_modules/@mdi/svg/svg/head-question-outline.svg"; // forgot password
|
import "../node_modules/@mdi/svg/svg/head-question-outline.svg"; // forgot password
|
||||||
import "../node_modules/bootstrap-icons/icons/people.svg"; // users management
|
import "../node_modules/bootstrap-icons/icons/people.svg"; // users management
|
||||||
// calendar/event icons
|
// calendar/event icons
|
||||||
import "../node_modules/bootstrap-icons/icons/calendar3-event.svg"; // brand
|
import "../node_modules/bootstrap-icons/icons/calendar3-event.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/clock-history.svg"; // brand
|
import "../node_modules/bootstrap-icons/icons/clock-history.svg"; // shift template
|
||||||
import "../node_modules/bootstrap-icons/icons/hourglass.svg"; // brand
|
import "../node_modules/bootstrap-icons/icons/tag.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/geo.svg"; // brand
|
import "../node_modules/bootstrap-icons/icons/hourglass.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/justify-left.svg"; // brand
|
import "../node_modules/bootstrap-icons/icons/map.svg";
|
||||||
import "../node_modules/bootstrap-icons/icons/plus-circle-dotted.svg"; // brand
|
import "../node_modules/bootstrap-icons/icons/geo.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/justify-left.svg";
|
||||||
|
import "../node_modules/bootstrap-icons/icons/plus-circle-dotted.svg";
|
||||||
|
|
||||||
// webpack automatically bundles all modules in your
|
// webpack automatically bundles all modules in your
|
||||||
// entry points. Those entry points can be configured
|
// entry points. Those entry points can be configured
|
||||||
|
|
|
@ -22,8 +22,8 @@ defmodule Shift73k.ShiftTemplates do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_shift_templates_by_user_id(user_id) do
|
def list_shift_templates_by_user_id(user_id) do
|
||||||
from(s in ShiftTemplate, where: s.user_id == ^user_id)
|
q = from s in ShiftTemplate, where: s.user_id == ^user_id, order_by: s.subject
|
||||||
|> Repo.all()
|
Repo.all(q)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -8,13 +8,12 @@ defmodule Shift73k.ShiftTemplates.ShiftTemplate do
|
||||||
@primary_key {:id, :binary_id, autogenerate: true}
|
@primary_key {:id, :binary_id, autogenerate: true}
|
||||||
@foreign_key_type :binary_id
|
@foreign_key_type :binary_id
|
||||||
schema "shift_templates" do
|
schema "shift_templates" do
|
||||||
field :subject, :string, default: "My Work Shift"
|
field :subject, :string
|
||||||
field :description, :string
|
field :description, :string
|
||||||
field :location, :string
|
field :location, :string
|
||||||
field :timezone, :string, default: @time_zone
|
field :time_zone, :string, default: @time_zone
|
||||||
field :start_time, :time, default: ~T[09:00:00]
|
field :time_start, :time, default: ~T[09:00:00]
|
||||||
field :length_hours, :integer, default: 8
|
field :time_end, :time, default: ~T[17:00:00]
|
||||||
field :length_minutes, :integer, default: 0
|
|
||||||
|
|
||||||
belongs_to(:user, Shift73k.Accounts.User)
|
belongs_to(:user, Shift73k.Accounts.User)
|
||||||
|
|
||||||
|
@ -26,22 +25,61 @@ defmodule Shift73k.ShiftTemplates.ShiftTemplate do
|
||||||
shift_template
|
shift_template
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:subject,
|
:subject,
|
||||||
:description,
|
|
||||||
:location,
|
:location,
|
||||||
:timezone,
|
:description,
|
||||||
:start_time,
|
:time_zone,
|
||||||
:length_hours,
|
:time_start,
|
||||||
:length_minutes,
|
:time_end,
|
||||||
:user_id
|
:user_id
|
||||||
])
|
])
|
||||||
|> validate_required([
|
|> validate_required([
|
||||||
:subject,
|
:subject,
|
||||||
:timezone,
|
:time_zone,
|
||||||
:start_time,
|
:time_start,
|
||||||
:length_hours
|
:time_end
|
||||||
])
|
])
|
||||||
|> validate_number(:length_hours, greater_than_or_equal_to: 0, less_than_or_equal_to: 24)
|
|> validate_length(:subject, count: :codepoints, max: 280)
|
||||||
|> validate_number(:length_minutes, greater_than_or_equal_to: 0, less_than: 60)
|
|> validate_length(:location, count: :codepoints, max: 280)
|
||||||
|> validate_inclusion(:timezone, Tzdata.zone_list())
|
|> validate_change(:time_end, fn :time_end, time_end ->
|
||||||
|
shift_length = shift_length(get_time_start(attrs), time_end)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
shift_length == 0 ->
|
||||||
|
[time_end: "end time cannot equal start time"]
|
||||||
|
|
||||||
|
shift_length >= 16 * 3600 ->
|
||||||
|
[time_end: "you don't want to work 16 or more hours!"]
|
||||||
|
|
||||||
|
true ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> validate_inclusion(:time_zone, Tzdata.zone_list())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_time_start(%{"time_start" => time_start}), do: time_start
|
||||||
|
defp get_time_start(%{time_start: time_start}), do: time_start
|
||||||
|
defp get_time_start(_), do: nil
|
||||||
|
|
||||||
|
def shift_length(time_start, time_end) do
|
||||||
|
cond do
|
||||||
|
time_end > time_start ->
|
||||||
|
Time.diff(time_end, time_start)
|
||||||
|
|
||||||
|
time_start > time_end ->
|
||||||
|
len1 = Time.diff(~T[23:59:59], time_start) + 1
|
||||||
|
len2 = Time.diff(time_end, ~T[00:00:00])
|
||||||
|
len1 + len2
|
||||||
|
|
||||||
|
true ->
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def shift_length_h_m_tuple(time_start, time_end) do
|
||||||
|
shift_length_seconds = shift_length(time_start, time_end)
|
||||||
|
h = shift_length_seconds |> Integer.floor_div(3600)
|
||||||
|
m = shift_length_seconds |> rem(3600) |> Integer.floor_div(60)
|
||||||
|
{h, m}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
Are you sure you want to delete "<%= @delete_shift_template.subject %>
|
Are you sure you want to delete "<%= @delete_shift_template.subject %>
|
||||||
(<%= @delete_shift_template.start_time |> Calendar.strftime("%I:%M%P") %>
|
(<%= @delete_shift_template.time_start |> Calendar.strftime("%I:%M%P") %>
|
||||||
—
|
—
|
||||||
<%=
|
<%= @delete_shift_template.time_end |> Calendar.strftime("%I:%M%P") %>)"?
|
||||||
@delete_shift_template.start_time
|
|
||||||
|> Time.add((60 * 60 * @delete_shift_template.length_hours) + ((@delete_shift_template.length_minutes || 0) * 60))
|
|
||||||
|> Calendar.strftime("%I:%M%P")
|
|
||||||
%>)"?
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
|
@ -2,54 +2,96 @@ defmodule Shift73kWeb.ShiftTemplateLive.FormComponent do
|
||||||
use Shift73kWeb, :live_component
|
use Shift73kWeb, :live_component
|
||||||
|
|
||||||
alias Shift73k.ShiftTemplates
|
alias Shift73k.ShiftTemplates
|
||||||
|
alias Shift73k.ShiftTemplates.ShiftTemplate
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def update(%{shift_template: shift_template} = assigns, socket) do
|
def update(%{shift_template: shift_template} = assigns, socket) do
|
||||||
changeset = ShiftTemplates.change_shift_template(shift_template)
|
changeset = ShiftTemplates.change_shift_template(shift_template)
|
||||||
|
|
||||||
{:ok,
|
socket
|
||||||
socket
|
|> assign(assigns)
|
||||||
|> assign(assigns)
|
|> assign(:changeset, changeset)
|
||||||
|> assign(:changeset, changeset)}
|
|> assign_shift_length(shift_template.time_start, shift_template.time_end)
|
||||||
|
|> live_okreply()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assign_shift_length(socket, time_start, time_end) do
|
||||||
|
shift_length = ShiftTemplate.shift_length_h_m_tuple(time_start, time_end)
|
||||||
|
assign(socket, :shift_length, shift_length)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prep_shift_template_params(shift_template_params, current_user) do
|
||||||
|
time_start = Time.from_iso8601!("T#{shift_template_params["time_start"]}:00")
|
||||||
|
time_end = Time.from_iso8601!("T#{shift_template_params["time_end"]}:00")
|
||||||
|
|
||||||
|
shift_template_params
|
||||||
|
|> Map.put("time_start", time_start)
|
||||||
|
|> Map.put("time_end", time_end)
|
||||||
|
|> Map.put("user_id", current_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("validate", %{"shift_template" => shift_template_params}, socket) do
|
def handle_event("validate", %{"shift_template" => shift_template_params}, socket) do
|
||||||
|
shift_template_params =
|
||||||
|
prep_shift_template_params(shift_template_params, socket.assigns.current_user)
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
socket.assigns.shift_template
|
socket.assigns.shift_template
|
||||||
|> ShiftTemplates.change_shift_template(shift_template_params)
|
|> ShiftTemplates.change_shift_template(shift_template_params)
|
||||||
|> Map.put(:action, :validate)
|
|> Map.put(:action, :validate)
|
||||||
|
|
||||||
{:noreply, assign(socket, :changeset, changeset)}
|
socket
|
||||||
|
|> assign(:changeset, changeset)
|
||||||
|
|> assign_shift_length(
|
||||||
|
shift_template_params["time_start"],
|
||||||
|
shift_template_params["time_end"]
|
||||||
|
)
|
||||||
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("save", %{"shift_template" => shift_template_params}, socket) do
|
def handle_event("save", %{"shift_template" => shift_template_params}, socket) do
|
||||||
save_shift_template(socket, socket.assigns.action, shift_template_params)
|
save_shift_template(
|
||||||
|
socket,
|
||||||
|
socket.assigns.action,
|
||||||
|
prep_shift_template_params(shift_template_params, socket.assigns.current_user)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp save_shift_template(socket, :edit, shift_template_params) do
|
@impl true
|
||||||
case ShiftTemplates.update_shift_template(socket.assigns.shift_template, shift_template_params) do
|
def handle_event("cancel", _, socket) do
|
||||||
{:ok, _shift_template} ->
|
{:noreply, push_event(socket, "modal-please-hide", %{})}
|
||||||
{:noreply,
|
|
||||||
socket
|
|
||||||
|> put_flash(:info, "Shift template updated successfully")
|
|
||||||
|> push_redirect(to: socket.assigns.return_to)}
|
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
|
||||||
{:noreply, assign(socket, :changeset, changeset)}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp save_shift_template(socket, :new, shift_template_params) do
|
defp save_shift_template(socket, :new, shift_template_params) do
|
||||||
case ShiftTemplates.create_shift_template(shift_template_params) do
|
case ShiftTemplates.create_shift_template(shift_template_params) do
|
||||||
{:ok, _shift_template} ->
|
{:ok, _shift_template} ->
|
||||||
{:noreply,
|
flash = {:info, "Shift template created successfully"}
|
||||||
socket
|
send(self(), {:put_flash_message, flash})
|
||||||
|> put_flash(:info, "Shift template created successfully")
|
|
||||||
|> push_redirect(to: socket.assigns.return_to)}
|
socket
|
||||||
|
|> push_event("modal-please-hide", %{})
|
||||||
|
|> live_noreply()
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
{:noreply, assign(socket, changeset: changeset)}
|
{:noreply, assign(socket, changeset: changeset)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp save_shift_template(socket, :edit, shift_template_params) do
|
||||||
|
case ShiftTemplates.update_shift_template(
|
||||||
|
socket.assigns.shift_template,
|
||||||
|
shift_template_params
|
||||||
|
) do
|
||||||
|
{:ok, _shift_template} ->
|
||||||
|
flash = {:info, "Shift template updated successfully"}
|
||||||
|
send(self(), {:put_flash_message, flash})
|
||||||
|
|
||||||
|
socket
|
||||||
|
|> push_event("modal-please-hide", %{})
|
||||||
|
|> live_noreply()
|
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
|
{:noreply, assign(socket, :changeset, changeset)}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,39 +6,107 @@
|
||||||
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
|
<%= label f, :subject, "Subject/Title", class: "form-label" %>
|
||||||
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :subject) %>">
|
||||||
|
<%= icon_div @socket, "bi-tag", [class: "icon is-left text-muted fs-5"] %>
|
||||||
|
<%= text_input f, :subject,
|
||||||
|
value: input_value(f, :subject),
|
||||||
|
class: input_class(f, :subject, "form-control"),
|
||||||
|
autofocus: true,
|
||||||
|
aria_describedby: error_ids(f, :subject)
|
||||||
|
%>
|
||||||
|
<%= error_tag f, :subject %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= label f, :subject %>
|
<div class="row gx-2 gx-sm-3">
|
||||||
<%= text_input f, :subject %>
|
|
||||||
<%= error_tag f, :subject %>
|
|
||||||
|
|
||||||
<%= label f, :description %>
|
<div class="col-6" phx-feedback-for="<%= input_id(f, :time_start) %>">
|
||||||
<%= text_input f, :description %>
|
<%= label f, :time_start, "Start", class: "form-label" %>
|
||||||
<%= error_tag f, :description %>
|
<%= time_input f, :time_start,
|
||||||
|
precision: :minute,
|
||||||
|
value: input_value(f, :time_start),
|
||||||
|
class: input_class(f, :time_start, "form-control"),
|
||||||
|
aria_describedby: error_ids(f, :time_start)
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= label f, :location %>
|
<div class="col-6" phx-feedback-for="<%= input_id(f, :time_end) %>">
|
||||||
<%= text_input f, :location %>
|
<%= label f, :time_end, "End", class: "form-label" %>
|
||||||
<%= error_tag f, :location %>
|
<%= time_input f, :time_end,
|
||||||
|
precision: :minute,
|
||||||
|
value: input_value(f, :time_end),
|
||||||
|
class: input_class(f, :time_end, "form-control"),
|
||||||
|
aria_describedby: error_ids(f, :time_end)
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= label f, :timezone %>
|
</div>
|
||||||
<%= text_input f, :timezone %>
|
|
||||||
<%= error_tag f, :timezone %>
|
|
||||||
|
|
||||||
<%= label f, :start_time %>
|
<div class="valid-feedback d-block text-primary">Shift length: <%= @shift_length |> elem(0) %>h <%= @shift_length |> elem(1) %>m</div>
|
||||||
<%= time_select f, :start_time %>
|
|
||||||
<%= error_tag f, :start_time %>
|
<div class="phx-orphaned-feedback" phx-feedback-for="<%= input_id(f, :time_start) %>">
|
||||||
|
<%= error_tag f, :time_start %>
|
||||||
|
</div>
|
||||||
|
<div class="phx-orphaned-feedback" phx-feedback-for="<%= input_id(f, :time_end) %>">
|
||||||
|
<%= error_tag f, :time_end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<%= label f, :location, class: "form-label mt-3" %>
|
||||||
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :location) %>">
|
||||||
|
<%= icon_div @socket, "bi-geo", [class: "icon is-left text-muted fs-5"] %>
|
||||||
|
<%= text_input f, :location,
|
||||||
|
value: input_value(f, :location),
|
||||||
|
class: input_class(f, :location, "form-control"),
|
||||||
|
aria_describedby: error_ids(f, :location)
|
||||||
|
%>
|
||||||
|
<%= error_tag f, :location %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<%= label f, :description, class: "form-label" %>
|
||||||
|
<div class="mb-3" phx-feedback-for="<%= input_id(f, :description) %>">
|
||||||
|
|
||||||
|
<%= textarea f, :description,
|
||||||
|
value: input_value(f, :description),
|
||||||
|
class: input_class(f, :description, "form-control"),
|
||||||
|
aria_describedby: error_ids(f, :description)
|
||||||
|
%>
|
||||||
|
<%= error_tag f, :description %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<%= label f, :time_zone, class: "form-label" %>
|
||||||
|
<div class="inner-addon left-addon mb-3" phx-feedback-for="<%= input_id(f, :time_zone) %>">
|
||||||
|
<%= icon_div @socket, "bi-map", [class: "icon is-left text-muted fs-5"] %>
|
||||||
|
<%= text_input f, :time_zone,
|
||||||
|
value: input_value(f, :time_zone),
|
||||||
|
class: "form-control",
|
||||||
|
list: "tz_list"
|
||||||
|
%>
|
||||||
|
<datalist id="tz_list">
|
||||||
|
<%= for tz_name <- Tzdata.zone_list() do %>
|
||||||
|
<option value="<%= tz_name %>"></option>
|
||||||
|
<% end %>
|
||||||
|
end
|
||||||
|
</datalist>
|
||||||
|
<div class="valid-feedback d-block text-primary">Type to search & select from list of known time zones (<%= link "formatted per IANA", to: "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones", target: "_blank" %>)</div>
|
||||||
|
<%= error_tag f, :time_zone %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= label f, :length_hours %>
|
|
||||||
<%= number_input f, :length_hours %>
|
|
||||||
<%= error_tag f, :length_hours %>
|
|
||||||
|
|
||||||
<%= label f, :length_minutes %>
|
|
||||||
<%= number_input f, :length_minutes %>
|
|
||||||
<%= error_tag f, :length_minutes %>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
||||||
<%= submit "Save", phx_disable_with: "Saving..." %>
|
<%= link "Cancel", to: "#", class: "btn", phx_click: "cancel", phx_target: @myself %>
|
||||||
|
<%= submit "Save",
|
||||||
|
class: "btn btn-primary ",
|
||||||
|
disabled: !@changeset.valid?,
|
||||||
|
aria_disabled: !@changeset.valid? && "true" || false,
|
||||||
|
phx_disable_with: "Saving..."
|
||||||
|
%>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="row justify-content-start justify-content-sm-center">
|
<div class="row justify-content-start justify-content-sm-center">
|
||||||
<div class="col-md-12 col-lg-10 col-xxl-8">
|
<div class="col-md-10 col-xl-10">
|
||||||
|
|
||||||
<div class="d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center">
|
<div class="d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center">
|
||||||
<h2 class="mb-3 mb-sm-0">
|
<h2 class="mb-3 mb-sm-0">
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
<%= for shift <- @shift_templates do %>
|
<%= for shift <- @shift_templates do %>
|
||||||
|
|
||||||
<div class="col-12 col-md-6 col-xl-4 ">
|
<div class="col-12 col-lg-6">
|
||||||
|
|
||||||
<div class="card mt-4">
|
<div class="card mt-4">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">
|
||||||
|
@ -49,23 +49,25 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" class="text-end">
|
<th scope="row" class="text-end">
|
||||||
<%= icon_div @socket, "bi-hourglass", [class: "icon baseline text-muted"] %>
|
<%= icon_div @socket, "bi-hourglass", [class: "icon baseline text-muted"] %>
|
||||||
<span class="visually-hidden">Hours</span>
|
<span class="visually-hidden">Hours:</span>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<%= shift.start_time |> Calendar.strftime("%I:%M%P") %>
|
<%= shift.time_start |> Calendar.strftime("%I:%M%P") %>
|
||||||
—
|
—
|
||||||
<%=
|
<%= shift.time_end |> Calendar.strftime("%I:%M%P") %>
|
||||||
shift.start_time
|
<span class="text-muted">
|
||||||
|> Time.add((60 * 60 * shift.length_hours) + ((shift.length_minutes || 0) * 60))
|
<span class="visually-hidden">Shift length:</span>
|
||||||
|> Calendar.strftime("%I:%M%P")
|
<% shift_length = ShiftTemplate.shift_length_h_m_tuple(shift.time_start, shift.time_end) %>
|
||||||
%>
|
(<%= shift_length |> elem(0) %>h <%= shift_length |> elem(1) %>m)
|
||||||
|
</span>
|
||||||
|
<span class="valid-feedback d-block text-muted mt-n1">TZ: <%= shift.time_zone %></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" class="text-end">
|
<th scope="row" class="text-end">
|
||||||
<%= icon_div @socket, "bi-geo", [class: "icon baseline text-muted"] %>
|
<%= icon_div @socket, "bi-geo", [class: "icon baseline text-muted"] %>
|
||||||
<span class="visually-hidden">Location</span>
|
<span class="visually-hidden">Location:</span>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<%= if shift.location do %>
|
<%= if shift.location do %>
|
||||||
|
@ -78,11 +80,11 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" class="text-end">
|
<th scope="row" class="text-end">
|
||||||
<%= icon_div @socket, "bi-justify-left", [class: "icon baseline text-muted"] %>
|
<%= icon_div @socket, "bi-justify-left", [class: "icon baseline text-muted"] %>
|
||||||
<span class="visually-hidden">Description</span>
|
<span class="visually-hidden">Description:</span>
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<%= if shift.description do %>
|
<%= if shift.description do %>
|
||||||
<%= shift.description %>
|
<%= text_to_html shift.description %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<span class="text-muted fst-italic">none</span>
|
<span class="text-muted fst-italic">none</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= link nav_link_opts(@conn, to: Routes.user_session_path(@conn, :new), class: "btn btn-outline-light") do %>
|
<%= 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", style: "margin-right:0.125rem;"] %>
|
||||||
Log in
|
Log in
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -68,6 +68,13 @@
|
||||||
|
|
||||||
<%= render "navbar/_user_menu.html", assigns %>
|
<%= render "navbar/_user_menu.html", assigns %>
|
||||||
|
|
||||||
|
<%= 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;"] %>
|
||||||
|
Log in
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,13 +4,12 @@ defmodule Shift73k.Repo.Migrations.CreateShiftTemplates do
|
||||||
def change do
|
def change do
|
||||||
create table(:shift_templates, primary_key: false) do
|
create table(:shift_templates, primary_key: false) do
|
||||||
add :id, :binary_id, primary_key: true
|
add :id, :binary_id, primary_key: true
|
||||||
add :subject, :string, null: false
|
add :subject, :string, size: 280, null: false
|
||||||
add :description, :string
|
add :location, :string, size: 280
|
||||||
add :location, :string
|
add :description, :text
|
||||||
add :timezone, :string, null: false
|
add :time_zone, :string, null: false
|
||||||
add :start_time, :time, null: false
|
add :time_start, :time, null: false
|
||||||
add :length_hours, :integer, null: false
|
add :time_end, :time, null: false
|
||||||
add :length_minutes, :integer
|
|
||||||
add :user_id, references(:users, on_delete: :nothing, type: :binary_id)
|
add :user_id, references(:users, on_delete: :nothing, type: :binary_id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
|
|
|
@ -75,7 +75,6 @@ mock_users =
|
||||||
Repo.insert_all(User, mock_users)
|
Repo.insert_all(User, mock_users)
|
||||||
# end
|
# end
|
||||||
|
|
||||||
|
|
||||||
#####
|
#####
|
||||||
# shift tepmlates
|
# shift tepmlates
|
||||||
alias Shift73k.ShiftTemplates.ShiftTemplate
|
alias Shift73k.ShiftTemplates.ShiftTemplate
|
||||||
|
@ -101,15 +100,18 @@ for user <- Accounts.list_users() do
|
||||||
|> Enum.map(fn e ->
|
|> Enum.map(fn e ->
|
||||||
seconds_to_add = :rand.uniform(seconds_days_14) + seconds_half_day
|
seconds_to_add = :rand.uniform(seconds_days_14) + seconds_half_day
|
||||||
add_dt = NaiveDateTime.add(user.inserted_at, seconds_to_add)
|
add_dt = NaiveDateTime.add(user.inserted_at, seconds_to_add)
|
||||||
|
time_start = time_from_mock.(e["time_start"])
|
||||||
|
shift_len_min = e["length_minutes"] || 0
|
||||||
|
shift_length = e["length_hours"] * 60 * 60 + shift_len_min * 60
|
||||||
|
time_end = Time.add(time_start, shift_length) |> Time.truncate(:second)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
subject: e["subject"],
|
subject: e["subject"],
|
||||||
description: e["description"],
|
description: e["description"],
|
||||||
location: e["location"],
|
location: e["location"],
|
||||||
timezone: (Tzdata.zone_list() |> Enum.random()),
|
time_zone: Tzdata.zone_list() |> Enum.random(),
|
||||||
start_time: time_from_mock.(e["start_time"]),
|
time_start: time_start,
|
||||||
length_hours: e["length_hours"],
|
time_end: time_end,
|
||||||
length_minutes: e["length_minutes"],
|
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
inserted_at: add_dt,
|
inserted_at: add_dt,
|
||||||
updated_at: add_dt
|
updated_at: add_dt
|
||||||
|
@ -117,5 +119,4 @@ for user <- Accounts.list_users() do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Repo.insert_all(ShiftTemplate, user_shifts)
|
Repo.insert_all(ShiftTemplate, user_shifts)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,9 @@ defmodule Shift73k.ShiftTemplatesTest do
|
||||||
describe "shift_templates" do
|
describe "shift_templates" do
|
||||||
alias Shift73k.ShiftTemplates.ShiftTemplate
|
alias Shift73k.ShiftTemplates.ShiftTemplate
|
||||||
|
|
||||||
@valid_attrs %{description: "some description", label: "some label", length_hours: 42, length_minutes: 42, location: "some location", start_time: ~T[14:00:00], subject: "some subject", timezone: "some timezone"}
|
@valid_attrs %{description: "some description", location: "some location", time_start: ~T[08:00:00], time_end: ~T[16:00:00], subject: "some subject", time_zone: "some time_zone"}
|
||||||
@update_attrs %{description: "some updated description", label: "some updated label", length_hours: 43, length_minutes: 43, location: "some updated location", start_time: ~T[15:01:01], subject: "some updated subject", timezone: "some updated timezone"}
|
@update_attrs %{description: "some updated description", location: "some updated location", time_start: ~T[13:00:00], time_end: ~T[19:30:00], subject: "some updated subject", time_zone: "some updated time_zone"}
|
||||||
@invalid_attrs %{description: nil, label: nil, length_hours: nil, length_minutes: nil, location: nil, start_time: nil, subject: nil, timezone: nil}
|
@invalid_attrs %{description: nil, location: nil, time_start: nil, time_end: nil, subject: nil, time_zone: nil}
|
||||||
|
|
||||||
def shift_template_fixture(attrs \\ %{}) do
|
def shift_template_fixture(attrs \\ %{}) do
|
||||||
{:ok, shift_template} =
|
{:ok, shift_template} =
|
||||||
|
@ -32,12 +32,11 @@ defmodule Shift73k.ShiftTemplatesTest do
|
||||||
test "create_shift_template/1 with valid data creates a shift_template" do
|
test "create_shift_template/1 with valid data creates a shift_template" do
|
||||||
assert {:ok, %ShiftTemplate{} = shift_template} = ShiftTemplates.create_shift_template(@valid_attrs)
|
assert {:ok, %ShiftTemplate{} = shift_template} = ShiftTemplates.create_shift_template(@valid_attrs)
|
||||||
assert shift_template.description == "some description"
|
assert shift_template.description == "some description"
|
||||||
assert shift_template.length_hours == 42
|
|
||||||
assert shift_template.length_minutes == 42
|
|
||||||
assert shift_template.location == "some location"
|
assert shift_template.location == "some location"
|
||||||
assert shift_template.start_time == ~T[14:00:00]
|
assert shift_template.time_start == ~T[07:00:00]
|
||||||
|
assert shift_template.time_end == ~T[15:00:00]
|
||||||
assert shift_template.subject == "some subject"
|
assert shift_template.subject == "some subject"
|
||||||
assert shift_template.timezone == "some timezone"
|
assert shift_template.time_zone == "some time_zone"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_shift_template/1 with invalid data returns error changeset" do
|
test "create_shift_template/1 with invalid data returns error changeset" do
|
||||||
|
@ -48,12 +47,11 @@ defmodule Shift73k.ShiftTemplatesTest do
|
||||||
shift_template = shift_template_fixture()
|
shift_template = shift_template_fixture()
|
||||||
assert {:ok, %ShiftTemplate{} = shift_template} = ShiftTemplates.update_shift_template(shift_template, @update_attrs)
|
assert {:ok, %ShiftTemplate{} = shift_template} = ShiftTemplates.update_shift_template(shift_template, @update_attrs)
|
||||||
assert shift_template.description == "some updated description"
|
assert shift_template.description == "some updated description"
|
||||||
assert shift_template.length_hours == 43
|
|
||||||
assert shift_template.length_minutes == 43
|
|
||||||
assert shift_template.location == "some updated location"
|
assert shift_template.location == "some updated location"
|
||||||
assert shift_template.start_time == ~T[15:01:01]
|
assert shift_template.time_start == ~T[15:00:00]
|
||||||
|
assert shift_template.time_end == ~T[19:30:00]
|
||||||
assert shift_template.subject == "some updated subject"
|
assert shift_template.subject == "some updated subject"
|
||||||
assert shift_template.timezone == "some updated timezone"
|
assert shift_template.time_zone == "some updated time_zone"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_shift_template/2 with invalid data returns error changeset" do
|
test "update_shift_template/2 with invalid data returns error changeset" do
|
||||||
|
|
|
@ -7,30 +7,27 @@ defmodule Shift73kWeb.ShiftTemplateLiveTest do
|
||||||
|
|
||||||
@create_attrs %{
|
@create_attrs %{
|
||||||
description: "some description",
|
description: "some description",
|
||||||
length_hours: 12,
|
|
||||||
length_minutes: 42,
|
|
||||||
location: "some location",
|
location: "some location",
|
||||||
start_time: ~T[14:00:00],
|
time_start: ~T[08:00:00],
|
||||||
|
time_end: ~T[16:00:00],
|
||||||
subject: "some subject",
|
subject: "some subject",
|
||||||
timezone: "some timezone"
|
time_zone: "some time_zone"
|
||||||
}
|
}
|
||||||
@update_attrs %{
|
@update_attrs %{
|
||||||
description: "some updated description",
|
description: "some updated description",
|
||||||
length_hours: 12,
|
|
||||||
length_minutes: 43,
|
|
||||||
location: "some updated location",
|
location: "some updated location",
|
||||||
start_time: ~T[15:01:01],
|
time_start: ~T[15:00:00],
|
||||||
|
time_end: ~T[19:30:00],
|
||||||
subject: "some updated subject",
|
subject: "some updated subject",
|
||||||
timezone: "some updated timezone"
|
time_zone: "some updated time_zone"
|
||||||
}
|
}
|
||||||
@invalid_attrs %{
|
@invalid_attrs %{
|
||||||
description: nil,
|
description: nil,
|
||||||
length_hours: nil,
|
|
||||||
length_minutes: nil,
|
|
||||||
location: nil,
|
location: nil,
|
||||||
start_time: nil,
|
time_start: nil,
|
||||||
|
time_end: nil,
|
||||||
subject: nil,
|
subject: nil,
|
||||||
timezone: nil
|
time_zone: nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defp fixture(:shift_template) do
|
defp fixture(:shift_template) do
|
||||||
|
|
Loading…
Reference in a new issue