improvements including removing Timex library
This commit is contained in:
parent
ab11131df6
commit
686db55e8b
18 changed files with 179 additions and 149 deletions
|
@ -7,6 +7,8 @@
|
||||||
# General application configuration
|
# General application configuration
|
||||||
use Mix.Config
|
use Mix.Config
|
||||||
|
|
||||||
|
config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
|
||||||
|
|
||||||
config :shift73k,
|
config :shift73k,
|
||||||
ecto_repos: [Shift73k.Repo]
|
ecto_repos: [Shift73k.Repo]
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,15 @@ defmodule Shift73k.Shifts do
|
||||||
|> where([s], s.user_id == ^user_id)
|
|> where([s], s.user_id == ^user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list_shifts_by_user(user_id) do
|
||||||
|
user_id
|
||||||
|
|> query_shifts_by_user()
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
def list_shifts_by_user_in_date_range(user_id, %Date.Range{} = date_range) do
|
def list_shifts_by_user_in_date_range(user_id, %Date.Range{} = date_range) do
|
||||||
query_shifts_by_user(user_id)
|
user_id
|
||||||
|
|> query_shifts_by_user()
|
||||||
|> where([s], s.date >= ^date_range.first)
|
|> where([s], s.date >= ^date_range.first)
|
||||||
|> where([s], s.date <= ^date_range.last)
|
|> where([s], s.date <= ^date_range.last)
|
||||||
|> order_by([s], [s.date, s.time_start])
|
|> order_by([s], [s.date, s.time_start])
|
||||||
|
@ -35,12 +42,14 @@ defmodule Shift73k.Shifts do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp query_shifts_by_user_from_list_of_dates(user_id, date_list) do
|
defp query_shifts_by_user_from_list_of_dates(user_id, date_list) do
|
||||||
query_shifts_by_user(user_id)
|
user_id
|
||||||
|
|> query_shifts_by_user()
|
||||||
|> where([s], s.date in ^date_list)
|
|> where([s], s.date in ^date_list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_shifts_by_user_from_list_of_dates(user_id, date_list) do
|
def list_shifts_by_user_from_list_of_dates(user_id, date_list) do
|
||||||
query_shifts_by_user_from_list_of_dates(user_id, date_list)
|
user_id
|
||||||
|
|> query_shifts_by_user_from_list_of_dates(date_list)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
||||||
use Timex
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> validate_inclusion(:time_zone, Timex.timezones(),
|
|> 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"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -70,7 +69,8 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
||||||
|
|
||||||
def shift_length(%ShiftTemplate{time_end: time_end, time_start: time_start}) do
|
def shift_length(%ShiftTemplate{time_end: time_end, time_start: time_start}) do
|
||||||
time_end
|
time_end
|
||||||
|> Timex.diff(time_start, :minute)
|
|> Time.diff(time_start)
|
||||||
|
|> Integer.floor_div(60)
|
||||||
|> shift_length()
|
|> shift_length()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -79,11 +79,4 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
|
||||||
|
|
||||||
def shift_length(time_end, time_start),
|
def shift_length(time_end, time_start),
|
||||||
do: shift_length(%ShiftTemplate{time_end: time_end, time_start: time_start})
|
do: shift_length(%ShiftTemplate{time_end: time_end, time_start: time_start})
|
||||||
|
|
||||||
def shift_length_h_m(%ShiftTemplate{time_end: _, time_start: _} = template) do
|
|
||||||
shift_length_seconds = shift_length(template)
|
|
||||||
h = shift_length_seconds |> Integer.floor_div(60)
|
|
||||||
m = shift_length_seconds |> rem(60)
|
|
||||||
{h, m}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,16 +68,27 @@ defmodule Shift73kWeb.LiveHelpers do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_shift_time(time), do: Timex.format!(time, "{h12}:{m}{am}")
|
def format_shift_time(time) do
|
||||||
|
time
|
||||||
|
|> Calendar.strftime("%-I:%M%P")
|
||||||
|
|> String.trim_trailing("m")
|
||||||
|
end
|
||||||
|
|
||||||
def format_shift_length(shift_template) do
|
def format_shift_length(%ShiftTemplate{} = shift_template) do
|
||||||
shift_template
|
shift_template
|
||||||
|> ShiftTemplate.shift_length()
|
|> ShiftTemplate.shift_length()
|
||||||
|> Timex.Duration.from_minutes()
|
|> format_shift_length()
|
||||||
|> Timex.format_duration()
|
end
|
||||||
|> String.replace("PT", "")
|
|
||||||
|> String.replace("H", "h ")
|
def format_shift_length(minutes) when is_integer(minutes) do
|
||||||
|> String.replace("M", "m")
|
h = Integer.floor_div(minutes, 60)
|
||||||
|> String.trim()
|
m = rem(minutes, 60)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
h > 0 && m > 0 -> "#{h}h #{m}m"
|
||||||
|
h > 0 -> "#{h}h"
|
||||||
|
m > 0 -> "#{m}m"
|
||||||
|
true -> "0m"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<% months = Map.keys(data) %>
|
<% months = Map.keys(data) %>
|
||||||
<dd>
|
<dd>
|
||||||
<%= for {m, i} <- Enum.with_index(months, 1) do %>
|
<%= for {m, i} <- Enum.with_index(months, 1) do %>
|
||||||
<%= data |> Map.get(m) |> hd() |> Timex.format!("{Mshort}") %>:
|
<%= data |> Map.get(m) |> hd() |> Calendar.strftime("%b") %>:
|
||||||
<% days = Map.get(data, m) %>
|
<% days = Map.get(data, m) %>
|
||||||
<%= for {d, i} <- Enum.with_index(days, 1) do %>
|
<%= for {d, i} <- Enum.with_index(days, 1) do %>
|
||||||
<%= d.day %><%= if i < length(days) do %>,<% end %>
|
<%= d.day %><%= if i < length(days) do %>,<% end %>
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
defmodule Shift73kWeb.ShiftAssignLive.Index do
|
defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
use Shift73kWeb, :live_view
|
use Shift73kWeb, :live_view
|
||||||
use Timex
|
|
||||||
|
|
||||||
alias Ecto.Multi
|
alias Ecto.Multi
|
||||||
alias Shift73k.Repo
|
alias Shift73k.Repo
|
||||||
alias Shift73k.EctoEnums.WeekdayEnum
|
|
||||||
alias Shift73k.Shifts
|
alias Shift73k.Shifts
|
||||||
alias Shift73k.Shifts.Shift
|
alias Shift73k.Shifts.Shift
|
||||||
alias Shift73k.Shifts.Templates
|
alias Shift73k.Shifts.Templates
|
||||||
|
@ -27,8 +25,6 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_params(_params, _url, socket) do
|
def handle_params(_params, _url, socket) do
|
||||||
user = socket.assigns.current_user
|
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> init_shift_templates()
|
|> init_shift_templates()
|
||||||
|> init_shift_template()
|
|> init_shift_template()
|
||||||
|
@ -36,8 +32,8 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
|> assign_shift_length()
|
|> assign_shift_length()
|
||||||
|> assign_shift_template_changeset()
|
|> assign_shift_template_changeset()
|
||||||
|> assign_modal_close_handlers()
|
|> assign_modal_close_handlers()
|
||||||
|> assign(:day_names, day_names(user.week_start_at))
|
|> init_today(Date.utc_today())
|
||||||
|> init_today(Timex.today())
|
|> init_day_names()
|
||||||
|> update_calendar()
|
|> update_calendar()
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
|
@ -61,7 +57,11 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp init_today(socket, today) do
|
defp init_today(socket, today) do
|
||||||
assign(socket, current_date: today, cursor_date: today)
|
assign(socket, current_date: today, cursor_date: cursor_date(today))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cursor_date(%Date{} = date) do
|
||||||
|
date |> Date.beginning_of_month() |> Date.add(4)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_shift_template_changeset(%{assigns: %{shift_template: shift}} = socket) do
|
defp assign_shift_template_changeset(%{assigns: %{shift_template: shift}} = socket) do
|
||||||
|
@ -105,27 +105,26 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
{label, template.id}
|
{label, template.id}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp rotate_week(week_start_at) do
|
defp init_day_names(%{assigns: %{current_user: user, current_date: today}} = socket) do
|
||||||
{a, b} = Enum.split_while(WeekdayEnum.__enum_map__(), fn {k, _v} -> k != week_start_at end)
|
week_start = Date.beginning_of_week(today, user.week_start_at)
|
||||||
b ++ a
|
|
||||||
end
|
|
||||||
|
|
||||||
defp day_names(week_start_at) do
|
day_names =
|
||||||
week_start_at
|
week_start
|
||||||
|> rotate_week()
|
|> Date.range(Date.add(week_start, 6))
|
||||||
|> Keyword.values()
|
|> Enum.map(&Calendar.strftime(&1, "%a"))
|
||||||
|> Enum.map(&Timex.day_shortname/1)
|
|
||||||
|
assign(socket, :day_names, day_names)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp date_range(cursor_date, week_start_at) do
|
defp date_range(cursor_date, week_start_at) do
|
||||||
last =
|
last =
|
||||||
cursor_date
|
cursor_date
|
||||||
|> Timex.end_of_month()
|
|> Date.end_of_month()
|
||||||
|> Timex.end_of_week(week_start_at)
|
|> Date.end_of_week(week_start_at)
|
||||||
|
|
||||||
cursor_date
|
cursor_date
|
||||||
|> Timex.beginning_of_month()
|
|> Date.beginning_of_month()
|
||||||
|> Timex.beginning_of_week(week_start_at)
|
|> Date.beginning_of_week(week_start_at)
|
||||||
|> Date.range(last)
|
|> Date.range(last)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -134,26 +133,20 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
assign(socket, :date_range, date_range)
|
assign(socket, :date_range, date_range)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp week_rows(%Date.Range{} = date_range) do
|
|
||||||
Interval.new(from: date_range.first, until: date_range.last, right_open: false)
|
|
||||||
|> Stream.map(&NaiveDateTime.to_date(&1))
|
|
||||||
|> Enum.chunk_every(7)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp assign_week_rows(%{assigns: %{date_range: date_range}} = socket) do
|
defp assign_week_rows(%{assigns: %{date_range: date_range}} = socket) do
|
||||||
assign(socket, :week_rows, week_rows(date_range))
|
assign(socket, :week_rows, Enum.chunk_every(date_range, 7))
|
||||||
end
|
end
|
||||||
|
|
||||||
def day_color(day, current_date, cursor_date, selected_days) do
|
def day_color(day, current_date, cursor_date, selected_days) do
|
||||||
cond do
|
cond do
|
||||||
Enum.member?(selected_days, Date.to_string(day)) ->
|
Enum.member?(selected_days, Date.to_string(day)) ->
|
||||||
cond do
|
cond do
|
||||||
Timex.compare(day, current_date, :days) == 0 -> "bg-triangle-info text-white"
|
Date.compare(day, current_date) == :eq -> "bg-triangle-info text-white"
|
||||||
day.month != cursor_date.month -> "bg-triangle-light text-gray"
|
day.month != cursor_date.month -> "bg-triangle-light text-gray"
|
||||||
true -> "bg-triangle-white"
|
true -> "bg-triangle-white"
|
||||||
end
|
end
|
||||||
|
|
||||||
Timex.compare(day, current_date, :days) == 0 ->
|
Date.compare(day, current_date) == :eq ->
|
||||||
"bg-info text-white"
|
"bg-info text-white"
|
||||||
|
|
||||||
day.month != cursor_date.month ->
|
day.month != cursor_date.month ->
|
||||||
|
@ -221,15 +214,16 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("month-nav", %{"month" => direction}, socket) do
|
def handle_event("month-nav", %{"month" => nav}, socket) do
|
||||||
new_cursor =
|
new_cursor =
|
||||||
cond do
|
cond do
|
||||||
direction == "now" ->
|
nav == "now" ->
|
||||||
Timex.today()
|
Date.utc_today()
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
months = (direction == "prev" && -1) || 1
|
socket.assigns.cursor_date
|
||||||
Timex.shift(socket.assigns.cursor_date, months: months)
|
|> Date.add((nav == "prev" && -30) || 30)
|
||||||
|
|> cursor_date()
|
||||||
end
|
end
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
list: "tz_list"
|
list: "tz_list"
|
||||||
%>
|
%>
|
||||||
<datalist id="tz_list">
|
<datalist id="tz_list">
|
||||||
<%= for tz_name <- Timex.timezones() do %>
|
<%= for tz_name <- Tzdata.zone_list() do %>
|
||||||
<option value="<%= tz_name %>"></option>
|
<option value="<%= tz_name %>"></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
end
|
end
|
||||||
|
@ -171,10 +171,10 @@
|
||||||
<%# month navigation %>
|
<%# month navigation %>
|
||||||
<div class="d-flex justify-content-between align-items-end mt-4">
|
<div class="d-flex justify-content-between align-items-end mt-4">
|
||||||
<h3 class="text-muted mb-0">
|
<h3 class="text-muted mb-0">
|
||||||
<%= Timex.format!(@cursor_date, "{Mfull} {YYYY}") %>
|
<%= 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(Timex.today(), :month), do: "disabled" %>>
|
<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" %>>
|
||||||
<%= icon_div @socket, "bi-asterisk", [class: "icon baseline"] %>
|
<%= icon_div @socket, "bi-asterisk", [class: "icon baseline"] %>
|
||||||
<span class="d-none d-sm-inline">Today</span>
|
<span class="d-none d-sm-inline">Today</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -206,7 +206,7 @@
|
||||||
<%= 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 %>">
|
||||||
|
|
||||||
<%= Timex.format!(day, "{0D}") %><%= if day.month != @cursor_date.month, do: "-#{Timex.format!(day, "{Mshort}")}" %>
|
<%= Calendar.strftime(day, "%d") %><%= if day.month != @cursor_date.month, do: "-#{Calendar.strftime(day, "%b")}" %>
|
||||||
|
|
||||||
<% day_shifts = Enum.filter(@known_shifts, fn s -> s.date == day end) %>
|
<% day_shifts = Enum.filter(@known_shifts, fn s -> s.date == day end) %>
|
||||||
<% shifts_to_show = shifts_to_show(day_shifts) %>
|
<% shifts_to_show = shifts_to_show(day_shifts) %>
|
||||||
|
@ -214,7 +214,7 @@
|
||||||
<%= for shift <- shifts_to_show do %>
|
<%= for shift <- shifts_to_show do %>
|
||||||
<span class="badge bg-primary text-start d-block">
|
<span class="badge bg-primary text-start d-block">
|
||||||
<span>
|
<span>
|
||||||
<%= shift.time_start |> Timex.format!("{h12}:{m}{am}") |> String.trim_trailing("m") %>
|
<%= format_shift_time(shift.time_start) %>
|
||||||
<%= shift.subject %>
|
<%= shift.subject %>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
defmodule Shift73kWeb.ShiftLive.Index do
|
defmodule Shift73kWeb.ShiftLive.Index do
|
||||||
use Shift73kWeb, :live_view
|
use Shift73kWeb, :live_view
|
||||||
use Timex
|
|
||||||
|
|
||||||
alias Shift73k.Shifts
|
alias Shift73k.Shifts
|
||||||
alias Shift73k.Shifts.Shift
|
alias Shift73k.Shifts.Shift
|
||||||
|
@ -21,9 +20,8 @@ defmodule Shift73kWeb.ShiftLive.Index do
|
||||||
|
|
||||||
if Roles.can?(current_user, shift, live_action) do
|
if Roles.can?(current_user, shift, live_action) do
|
||||||
socket
|
socket
|
||||||
|> init_today(Timex.today())
|
|> init_today(Date.utc_today())
|
||||||
|> assign_date_range()
|
|> update_agenda()
|
||||||
|> assign_known_shifts()
|
|
||||||
|> assign(:delete_shift, nil)
|
|> assign(:delete_shift, nil)
|
||||||
|> apply_action(socket.assigns.live_action, params)
|
|> apply_action(socket.assigns.live_action, params)
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
|
@ -49,7 +47,11 @@ defmodule Shift73kWeb.ShiftLive.Index do
|
||||||
defp shift_from_params(_params), do: %Shift{}
|
defp shift_from_params(_params), do: %Shift{}
|
||||||
|
|
||||||
defp init_today(socket, today) do
|
defp init_today(socket, today) do
|
||||||
assign(socket, current_date: today, cursor_date: today)
|
assign(socket, current_date: today, cursor_date: cursor_date(today))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cursor_date(%Date{} = date) do
|
||||||
|
date |> Date.beginning_of_month() |> Date.add(4)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_date_range(%{assigns: %{cursor_date: cursor_date}} = socket) do
|
defp assign_date_range(%{assigns: %{cursor_date: cursor_date}} = socket) do
|
||||||
|
@ -58,8 +60,8 @@ defmodule Shift73kWeb.ShiftLive.Index do
|
||||||
|
|
||||||
defp date_range(cursor_date) do
|
defp date_range(cursor_date) do
|
||||||
cursor_date
|
cursor_date
|
||||||
|> Timex.beginning_of_month()
|
|> Date.beginning_of_month()
|
||||||
|> Date.range(Timex.end_of_month(cursor_date))
|
|> Date.range(Date.end_of_month(cursor_date))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_known_shifts(socket) do
|
defp assign_known_shifts(socket) do
|
||||||
|
@ -68,6 +70,12 @@ defmodule Shift73kWeb.ShiftLive.Index do
|
||||||
assign(socket, :shifts, shifts)
|
assign(socket, :shifts, shifts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp update_agenda(socket) do
|
||||||
|
socket
|
||||||
|
|> assign_date_range()
|
||||||
|
|> assign_known_shifts()
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete", %{"id" => id}, socket) do
|
def handle_event("delete", %{"id" => id}, socket) do
|
||||||
shift = Shifts.get_shift!(id)
|
shift = Shifts.get_shift!(id)
|
||||||
|
@ -77,21 +85,21 @@ defmodule Shift73kWeb.ShiftLive.Index do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("month-nav", %{"month" => direction}, socket) do
|
def handle_event("month-nav", %{"month" => nav}, socket) do
|
||||||
new_cursor =
|
new_cursor =
|
||||||
cond do
|
cond do
|
||||||
direction == "now" ->
|
nav == "now" ->
|
||||||
Timex.today()
|
Date.utc_today()
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
months = (direction == "prev" && -1) || 1
|
socket.assigns.cursor_date
|
||||||
Timex.shift(socket.assigns.cursor_date, months: months)
|
|> Date.add((nav == "prev" && -30) || 30)
|
||||||
|
|> cursor_date()
|
||||||
end
|
end
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> assign(:cursor_date, new_cursor)
|
|> assign(:cursor_date, new_cursor)
|
||||||
|> assign_date_range()
|
|> update_agenda()
|
||||||
|> assign_known_shifts()
|
|
||||||
|> live_noreply()
|
|> live_noreply()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,76 +1,84 @@
|
||||||
<div class="row justify-content-start justify-content-sm-center">
|
<div class="row justify-content-start justify-content-sm-center">
|
||||||
<div class="col-md-10 col-xl-10">
|
<div class="col-md-10 col-xl-10">
|
||||||
|
|
||||||
<h2 class="mb-3 mb-sm-0">
|
<h2 class="mb-3 mb-sm-0">
|
||||||
<%= icon_div @socket, "bi-card-list", [class: "icon baseline"] %>
|
<%= icon_div @socket, "bi-card-list", [class: "icon baseline"] %>
|
||||||
My Shifts
|
My Shifts
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<%# month navigation %>
|
<div class="row justify-content-start justify-content-sm-center">
|
||||||
<div class="d-flex justify-content-between align-items-end mt-4">
|
<div class="col-md-10 col-xl-10">
|
||||||
<h3 class="text-muted mb-0">
|
|
||||||
<%= Timex.format!(@cursor_date, "{Mfull} {YYYY}") %>
|
|
||||||
</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"] %>
|
|
||||||
<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"] %>
|
|
||||||
<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">
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<dl>
|
<%# month navigation %>
|
||||||
<%= for day <- Enum.to_list(@date_range) do %>
|
<div class="d-flex justify-content-between align-items-end my-4">
|
||||||
<%= if Date.day_of_week(day, @current_user.week_start_at) == 1 do %>
|
<h3 class="text-muted mb-0">
|
||||||
<div class="border-top mt-4 mb-4"></div>
|
<%= Calendar.strftime(@cursor_date, "%B %Y") %>
|
||||||
<% end %>
|
</h3>
|
||||||
<dt>
|
<div>
|
||||||
<%= Timex.format!(day, "{WDfull}, {Mshort} {D}") %>
|
<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" %>>
|
||||||
</dt>
|
<%= icon_div @socket, "bi-asterisk", [class: "icon baseline"] %>
|
||||||
<% day_shifts = Enum.filter(@shifts, fn s -> s.date == day end) %>
|
<span class="d-none d-sm-inline">Today</span>
|
||||||
<%= if !Enum.empty?(day_shifts) do %>
|
</button>
|
||||||
<dd id="day-<%= day.day %>">
|
<button type="button" phx-click="month-nav" phx-value-month="prev" class="btn btn-primary">
|
||||||
<%= for shift <- day_shifts do %>
|
<%= icon_div @socket, "bi-chevron-left", [class: "icon baseline"] %>
|
||||||
<div class="row gx-2" id="shift-<%= shift.id %>">
|
<span class="d-none d-sm-inline">Prev</span>
|
||||||
<div class="col-4 col-md-3 col-lg-2 text-end">
|
</button>
|
||||||
<div>
|
<button type="button" phx-click="month-nav" phx-value-month="next" class="btn btn-primary">
|
||||||
<%= format_shift_time(shift.time_start) |> String.trim_trailing("m") %>
|
<span class="d-none d-sm-inline">Next</span>
|
||||||
—
|
<%= icon_div @socket, "bi-chevron-right", [class: "icon baseline", style: "margin-left:0.125rem;"] %>
|
||||||
<%= format_shift_time(shift.time_end) |> String.trim_trailing("m") %>
|
</button>
|
||||||
</div>
|
|
||||||
<div style="font-size: smaller;"><%= shift.time_zone %></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-md-9 col-lg-10">
|
|
||||||
<div>
|
|
||||||
<%= shift.subject %>
|
|
||||||
<%= if shift.location do %>
|
|
||||||
<span class="text-muted">(<%= shift.location %>)</span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<div style="font-size: smaller;"><%= shift.description %></div>
|
|
||||||
<div style="font-size: smaller;">
|
|
||||||
<span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: shift.id, data: [confirm: "Are you sure?"] %></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
|
||||||
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
<% else %>
|
<dl>
|
||||||
<dd><em>Nothing scheduled</em></dd>
|
<%= for day <- Enum.to_list(@date_range) do %>
|
||||||
<% end %>
|
<%= if Date.day_of_week(day, @current_user.week_start_at) == 1 do %>
|
||||||
<% end %>
|
<div class="border-top mt-4 mb-4"></div>
|
||||||
</dl>
|
<% end %>
|
||||||
|
<dt>
|
||||||
|
<%= Calendar.strftime(day, "%A, %b %-d") %>
|
||||||
|
</dt>
|
||||||
|
<% day_shifts = Enum.filter(@shifts, fn s -> s.date == day end) %>
|
||||||
|
<%= if !Enum.empty?(day_shifts) do %>
|
||||||
|
<dd id="day-<%= day.day %>">
|
||||||
|
<%= for shift <- day_shifts do %>
|
||||||
|
<div class="row gx-2" id="shift-<%= shift.id %>">
|
||||||
|
<div class="col-4 col-md-3 col-xl-2 text-end">
|
||||||
|
<div>
|
||||||
|
<%= format_shift_time(shift.time_start) %>
|
||||||
|
—
|
||||||
|
<%= format_shift_time(shift.time_end) %>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: smaller;"><%= shift.time_zone %></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-md-9 col-xl-10">
|
||||||
|
<div>
|
||||||
|
<%= shift.subject %>
|
||||||
|
<%= if shift.location do %>
|
||||||
|
<span class="text-muted">(<%= shift.location %>)</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: smaller;"><%= shift.description %></div>
|
||||||
|
<div style="font-size: smaller;">
|
||||||
|
<span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: shift.id, data: [confirm: "Are you sure?"] %></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
|
||||||
|
<% else %>
|
||||||
|
<dd><em>Nothing scheduled</em></dd>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
defmodule Shift73kWeb.ShiftTemplateLive.DeleteComponent do
|
defmodule Shift73kWeb.ShiftTemplateLive.DeleteComponent do
|
||||||
use Shift73kWeb, :live_component
|
use Shift73kWeb, :live_component
|
||||||
use Timex
|
|
||||||
|
|
||||||
alias Shift73k.Shifts.Templates
|
alias Shift73k.Shifts.Templates
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@
|
||||||
list: "tz_list"
|
list: "tz_list"
|
||||||
%>
|
%>
|
||||||
<datalist id="tz_list">
|
<datalist id="tz_list">
|
||||||
<%= for tz_name <- Timex.timezones() do %>
|
<%= for tz_name <- Tzdata.zone_list() do %>
|
||||||
<option value="<%= tz_name %>"></option>
|
<option value="<%= tz_name %>"></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
defmodule Shift73kWeb.ShiftTemplateLive.Index do
|
||||||
use Shift73kWeb, :live_view
|
use Shift73kWeb, :live_view
|
||||||
use Timex
|
|
||||||
|
|
||||||
alias Shift73k.Accounts
|
alias Shift73k.Accounts
|
||||||
alias Shift73k.Shifts.Templates
|
alias Shift73k.Shifts.Templates
|
||||||
|
|
|
@ -14,7 +14,13 @@ defmodule Shift73kWeb.UserLive.Settings.WeekStart do
|
||||||
end
|
end
|
||||||
|
|
||||||
def week_start_options do
|
def week_start_options do
|
||||||
WeekdayEnum.__enum_map__() |> Enum.map(fn {d, n} -> {Timex.day_name(n), d} end)
|
{week_start_at, _} = WeekdayEnum.__enum_map__() |> hd()
|
||||||
|
week_start = Date.beginning_of_week(Date.utc_today(), week_start_at)
|
||||||
|
|
||||||
|
week_start
|
||||||
|
|> Date.range(Date.add(week_start, 6))
|
||||||
|
|> Enum.map(&Calendar.strftime(&1, "%A"))
|
||||||
|
|> Enum.zip(Keyword.keys(WeekdayEnum.__enum_map__()))
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
defmodule Shift73kWeb.UserManagementLive.Index do
|
defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
use Shift73kWeb, :live_view
|
use Shift73kWeb, :live_view
|
||||||
use Timex
|
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Shift73k.Util.Dt
|
import Shift73k.Util.Dt
|
||||||
|
@ -200,7 +199,7 @@ defmodule Shift73kWeb.UserManagementLive.Index do
|
||||||
|
|
||||||
def dt_out(ndt) do
|
def dt_out(ndt) do
|
||||||
ndt
|
ndt
|
||||||
|> Timex.to_datetime(app_time_zone())
|
|> DateTime.from_naive!(app_time_zone())
|
||||||
|> Timex.format!("{YYYY} {Mshort} {D}, {h12}:{m} {AM}")
|
|> Calendar.strftime("%Y %b %-d, %-I:%M %p")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="d-inline d-sm-block col-auto">
|
<dd class="d-inline d-sm-block col-auto">
|
||||||
<span class="visually-hidden"><%= user.confirmed_at && "Yes" || "No" %></span>
|
<span class="visually-hidden"><%= user.confirmed_at && "Yes" || "No" %></span>
|
||||||
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %>>
|
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %> disabled>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@
|
||||||
<td class="align-middle" style="white-space: nowrap;"><%= dt_out(user.inserted_at) %></td>
|
<td class="align-middle" style="white-space: nowrap;"><%= dt_out(user.inserted_at) %></td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<span class="visually-hidden"><%= user.confirmed_at && "Confirmed" || "Not confirmed" %></span>
|
<span class="visually-hidden"><%= user.confirmed_at && "Confirmed" || "Not confirmed" %></span>
|
||||||
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %>>
|
<input type="checkbox" class="form-check-input" aria-hidden="true" <%= user.confirmed_at && "checked" || "" %> disabled>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle text-end text-nowrap">
|
<td class="align-middle text-end text-nowrap">
|
||||||
|
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -52,7 +52,8 @@ defmodule Shift73k.MixProject do
|
||||||
{:bamboo, "~> 2.0"},
|
{:bamboo, "~> 2.0"},
|
||||||
{:bamboo_smtp, "~> 4.0"},
|
{:bamboo_smtp, "~> 4.0"},
|
||||||
{:scrivener_ecto, "~> 2.0"},
|
{:scrivener_ecto, "~> 2.0"},
|
||||||
{:timex, "~> 3.6"}
|
{:tzdata, "~> 1.1"},
|
||||||
|
{:nimble_csv, "~> 1.0"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -27,6 +27,7 @@
|
||||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||||
"mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
|
"mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
|
||||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||||
|
"nimble_csv": {:hex, :nimble_csv, "1.1.0", "b1dba4a86be9e03065c9de829050468e591f569100332db949e7ce71be0afc25", [:mix], [], "hexpm", "e986755bc302832cac429be6deda0fc9d82d3c82b47abefb68b3c17c9d949a3f"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
|
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
|
||||||
"phoenix": {:hex, :phoenix, "1.5.8", "71cfa7a9bb9a37af4df98939790642f210e35f696b935ca6d9d9c55a884621a4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "35ded0a32f4836168c7ab6c33b88822eccd201bcd9492125a9bea4c54332d955"},
|
"phoenix": {:hex, :phoenix, "1.5.8", "71cfa7a9bb9a37af4df98939790642f210e35f696b935ca6d9d9c55a884621a4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "35ded0a32f4836168c7ab6c33b88822eccd201bcd9492125a9bea4c54332d955"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
|
||||||
|
|
|
@ -110,7 +110,7 @@ for user <- Accounts.list_users() do
|
||||||
subject: e["subject"],
|
subject: e["subject"],
|
||||||
description: e["description"],
|
description: e["description"],
|
||||||
location: e["location"],
|
location: e["location"],
|
||||||
time_zone: Timex.timezones() |> Enum.random(),
|
time_zone: Tzdata.zone_list() |> Enum.random(),
|
||||||
time_start: time_start,
|
time_start: time_start,
|
||||||
time_end: time_end,
|
time_end: time_end,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
|
|
Loading…
Reference in a new issue