diff --git a/.iex.exs b/.iex.exs index 0cc62318..66306574 100644 --- a/.iex.exs +++ b/.iex.exs @@ -3,5 +3,7 @@ import Ecto.Query alias Shift73k.Repo alias Shift73k.Accounts alias Shift73k.Accounts.User +alias Shift73k.Shifts +alias Shift73k.Shifts.Shift alias Shift73k.Shifts.Templates alias Shift73k.Shifts.Templates.ShiftTemplate diff --git a/assets/css/_bs-colors.scss b/assets/css/_bs-colors.scss index a6648039..352e5727 100644 --- a/assets/css/_bs-colors.scss +++ b/assets/css/_bs-colors.scss @@ -3,7 +3,7 @@ $primary: #662c91; $secondary: #ee6c4d; $success: #3f784c; $info: #3f84e5; -$warning: #fcca46; +$warning: #ffec51; $light: $gray-200; $dark: $gray-800; diff --git a/assets/css/_bs-load.scss b/assets/css/_bs-load.scss index f1f0e784..b7c2aa2f 100644 --- a/assets/css/_bs-load.scss +++ b/assets/css/_bs-load.scss @@ -28,7 +28,7 @@ @import "../node_modules/bootstrap/scss/nav"; @import "../node_modules/bootstrap/scss/navbar"; @import "../node_modules/bootstrap/scss/card"; -// @import "../node_modules/bootstrap/scss/accordion"; +@import "../node_modules/bootstrap/scss/accordion"; // @import "../node_modules/bootstrap/scss/breadcrumb"; @import "../node_modules/bootstrap/scss/pagination"; // @import "../node_modules/bootstrap/scss/badge"; diff --git a/assets/css/app.scss b/assets/css/app.scss index a20a027c..722c55f2 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -63,12 +63,32 @@ } /* calendar table rounded */ -// table.table.table-rounded, -// table.table.table-rounded thead, -// table.table.table-rounded thead tr, -// table.table.table-rounded thead tr th:first-child { -// border-radius: 10px 0 0 10px; -// } +table.table.table-calendar thead tr th, +table.table.table-calendar tbody tr td { + width: 14%; +} +table.table.table-calendar tbody tr td { + font-size: $font-size-sm; + height: 3.5rem; + padding: 0.2rem 0.4rem; + @include media-breakpoint-up(md) { + height: 4.5rem; + padding: 0.2rem 0.5rem; + } + @include media-breakpoint-up(lg) { + font-size: $font-size-base; + height: 5.5rem; + padding: 0.3rem 0.5rem; + } + @include media-breakpoint-up(xl) { + height: 6.5rem; + padding: 0.35rem 0.5rem; + } + @include media-breakpoint-up(xxl) { + height: 7.5rem; + padding: 0.4rem 0.75rem; + } +} table.table.table-rounded { border-collapse: separate; @@ -117,3 +137,41 @@ table.table.table-rounded { } } } + +/* selected days background triangle color */ +.bg-triangle-primary { + background: linear-gradient( + to bottom right, + $primary 0%, + $primary 50%, + $secondary 50%, + $secondary 100% + ); +} +.bg-triangle-info { + background: linear-gradient( + to bottom right, + $info 0%, + $info 50%, + $secondary 50%, + $secondary 100% + ); +} +.bg-triangle-light { + background: linear-gradient( + to bottom right, + $light 0%, + $light 50%, + $secondary 50%, + $secondary 100% + ); +} +.bg-triangle-white { + background: linear-gradient( + to bottom right, + $white 0%, + $white 50%, + $secondary 50%, + $secondary 100% + ); +} diff --git a/assets/js/_bs_collapse.js b/assets/js/_bs_collapse.js new file mode 100644 index 00000000..c3c04074 --- /dev/null +++ b/assets/js/_bs_collapse.js @@ -0,0 +1,61 @@ +// Hook for custom liveview bootstrap collapse handling +import Collapse from "bootstrap/js/dist/collapse"; + +export const BsCollapse = { + mounted() { + // when the liveview mounts, create the BS collapse object + const collapse = new Collapse(this.el, { toggle: false }); + + this.handleEvent("toggle-template-details", ({ targetId }) => { + if (this.el.id == targetId) { + collapse.toggle(); + } + }); + + // this.el.addEventListener("show.bs.collapse", (event) => { + // this.pushEvent("collapse-show", { target_id: event.target.id }); + // }); + + this.el.addEventListener("shown.bs.collapse", (event) => { + this.pushEvent("collapse-shown", { target_id: event.target.id }); + }); + + // this.el.addEventListener("hide.bs.collapse", (event) => { + // this.pushEvent("collapse-hide", { target_id: event.target.id }); + // }); + + this.el.addEventListener("hidden.bs.collapse", (event) => { + this.pushEvent("collapse-hidden", { target_id: event.target.id }); + }); + }, +}; + +// export const BsModal = { +// mounted() { +// // when the liveview mounts, create the BS modal +// const modal = new Modal(this.el); +// // and trigger BS modal to show +// modal.show(); + +// // when the BS modal hides, send 'close' to the liveview +// this.el.addEventListener("hidden.bs.modal", (event) => { +// this.pushEventTo(`#${this.el.getAttribute("id")}`, "close", {}); +// modal.dispose(); +// }); + +// // liveview can send this event to tell BS modal to close +// // ex.: on successful form save, instead of immediate redirect +// // this event hides the BS modal, which triggers the above, +// // which sends 'close' to liveview and disposes the BS modal +// this.handleEvent("modal-please-hide", (payload) => { +// modal.hide(); +// }); +// }, + +// destroyed() { +// // when the liveview is destroyed, +// // modal-backdrop must be forcibly removed +// const backdrop = document.querySelector(".modal-backdrop"); +// if (backdrop) backdrop.parentElement.removeChild(backdrop); +// }, +// }; diff --git a/assets/js/_bs_modal.js b/assets/js/_bs_modal.js index 981b3bc9..ce2adb67 100644 --- a/assets/js/_bs_modal.js +++ b/assets/js/_bs_modal.js @@ -1,7 +1,6 @@ // Helping bootstrap modals work with liveview // preserving animations import Modal from "bootstrap/js/dist/modal"; -// import { Modal } from "bootstrap"; export const BsModal = { mounted() { diff --git a/assets/js/app.js b/assets/js/app.js index 7c7ad521..62e62df3 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -5,7 +5,7 @@ import "../css/app.scss"; // Import icons for sprite-loader // navbar brand icon -import "../node_modules/bootstrap-icons/icons/calendar3-week.svg"; // brand +import "../node_modules/bootstrap-icons/icons/calendar2-week.svg"; // brand // menus etc import "../node_modules/bootstrap-icons/icons/person-circle.svg"; // accounts menu import "../node_modules/bootstrap-icons/icons/person-plus.svg"; // new user / register @@ -34,9 +34,10 @@ 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/bootstrap-icons/icons/people.svg"; // users management // calendar/event icons -import "../node_modules/bootstrap-icons/icons/calendar3.svg"; -import "../node_modules/bootstrap-icons/icons/calendar3-event.svg"; -import "../node_modules/bootstrap-icons/icons/calendar3-range.svg"; +import "../node_modules/bootstrap-icons/icons/calendar2.svg"; +import "../node_modules/bootstrap-icons/icons/calendar2-plus.svg"; +import "../node_modules/bootstrap-icons/icons/calendar2-event.svg"; +import "../node_modules/bootstrap-icons/icons/calendar2-range.svg"; import "../node_modules/bootstrap-icons/icons/clock-history.svg"; // shift template import "../node_modules/bootstrap-icons/icons/tag.svg"; import "../node_modules/bootstrap-icons/icons/hourglass.svg"; @@ -47,6 +48,11 @@ import "../node_modules/bootstrap-icons/icons/plus-circle-dotted.svg"; import "../node_modules/bootstrap-icons/icons/clipboard-plus.svg"; import "../node_modules/bootstrap-icons/icons/star.svg"; import "../node_modules/bootstrap-icons/icons/star-fill.svg"; +import "../node_modules/bootstrap-icons/icons/binoculars.svg"; +import "../node_modules/bootstrap-icons/icons/binoculars-fill.svg"; +import "../node_modules/bootstrap-icons/icons/eraser.svg"; +import "../node_modules/bootstrap-icons/icons/save.svg"; +import "../node_modules/bootstrap-icons/icons/asterisk.svg"; // webpack automatically bundles all modules in your // entry points. Those entry points can be configured @@ -72,11 +78,13 @@ import "./_form-validity"; // Bootstrap-liveview helpers import { AlertRemover } from "./_alert-remover"; import { BsModal } from "./_bs_modal"; +import { BsCollapse } from "./_bs_collapse"; // LiveSocket setup let Hooks = {}; Hooks.AlertRemover = AlertRemover; Hooks.BsModal = BsModal; +Hooks.BsCollapse = BsCollapse; let csrfToken = document .querySelector("meta[name='csrf-token']") .getAttribute("content"); diff --git a/lib/shift73k/shifts/templates.ex b/lib/shift73k/shifts/templates.ex index 0e0c251e..993f73a7 100644 --- a/lib/shift73k/shifts/templates.ex +++ b/lib/shift73k/shifts/templates.ex @@ -40,8 +40,10 @@ defmodule Shift73k.Shifts.Templates do ** (Ecto.NoResultsError) """ + def get_shift_template!(nil), do: nil def get_shift_template!(id), do: Repo.get!(ShiftTemplate, id) + def get_shift_template(nil), do: nil def get_shift_template(id), do: Repo.get(ShiftTemplate, id) @doc """ diff --git a/lib/shift73k/shifts/templates/shift_template.ex b/lib/shift73k/shifts/templates/shift_template.ex index 5896ce7d..55ad1a56 100644 --- a/lib/shift73k/shifts/templates/shift_template.ex +++ b/lib/shift73k/shifts/templates/shift_template.ex @@ -58,7 +58,7 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do [] end end) - |> validate_inclusion(:time_zone, Timex.timezones()) + |> validate_inclusion(:time_zone, Timex.timezones(), message: "must be a valid IANA tz database time zone") end defp time_start_from_attrs(%{"time_start" => time_start}), do: time_start diff --git a/lib/shift73k_web/live/live_helpers.ex b/lib/shift73k_web/live/live_helpers.ex index 6667f05a..a0b9ba77 100644 --- a/lib/shift73k_web/live/live_helpers.ex +++ b/lib/shift73k_web/live/live_helpers.ex @@ -5,6 +5,7 @@ defmodule Shift73kWeb.LiveHelpers do alias Shift73k.Accounts alias Shift73k.Accounts.User alias Shift73kWeb.UserAuth + alias Shift73k.Shifts.Templates.ShiftTemplate @doc """ Performs the {:noreply, socket} for a given socket. @@ -66,4 +67,17 @@ defmodule Shift73kWeb.LiveHelpers do put_flash(acc, String.to_existing_atom(k), v) end) end + + def format_shift_time(time), do: Timex.format!(time, "{h12}:{m}{am}") + + def format_shift_length(shift_template) do + shift_template + |> ShiftTemplate.shift_length() + |> Timex.Duration.from_minutes() + |> Timex.format_duration() + |> String.replace("PT", "") + |> String.replace("H", "h ") + |> String.replace("M", "m") + |> String.trim() + end end diff --git a/lib/shift73k_web/live/shift_assign_live/index.ex b/lib/shift73k_web/live/shift_assign_live/index.ex index 9ab3fec3..01e7326c 100644 --- a/lib/shift73k_web/live/shift_assign_live/index.ex +++ b/lib/shift73k_web/live/shift_assign_live/index.ex @@ -3,17 +3,98 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do use Timex alias Shift73k.EctoEnums.WeekdayEnum + alias Shift73k.Shifts.Templates + alias Shift73k.Shifts.Templates.ShiftTemplate + + @custom_shift %ShiftTemplate{subject: "Custom shift", id: "custom-shift"} + @custom_shift_opt {@custom_shift.subject, @custom_shift.id} @impl true def mount(_params, session, socket) do socket |> assign_defaults(session) - |> assign_day_names() - |> assign_init_dates(Timex.today()) - |> assign_week_rows() + |> assign(:custom_shift, @custom_shift) + |> assign(:show_template_btn_active, :false) + |> assign(:show_template_details, :false) + |> assign(:selected_days, []) |> live_okreply() end + @impl true + def handle_params(_params, _url, socket) do + socket + |> init_shift_templates() + |> init_shift_template() + |> show_details_if_custom() + |> assign_shift_length() + |> assign_shift_template_changeset() + |> init_today(Timex.today()) + |> init_calendar() + |> init_known_shifts() + |> live_noreply() + end + + defp get_shift_template("custom-shift"), do: @custom_shift + defp get_shift_template(template_id), do: Templates.get_shift_template(template_id) + + defp assign_shift_length(%{assigns: %{shift_template: shift}} = socket) do + assign(socket, :shift_length, format_shift_length(shift)) + end + + defp init_known_shifts(%{assigns: %{current_user: user, week_rows: weeks}} = socket) do + days = weeks |> List.flatten() + first = weeks |> List.flatten() |> List.first() + socket + end + + defp init_calendar(%{assigns: %{current_user: user}} = socket) do + days = day_names(user.week_start_at) + {first, last, rows} = week_rows(socket.assigns.cursor_date, user.week_start_at) + assign(socket, [day_names: days, week_rows: rows, day_first: first, day_last: last]) + end + + defp init_today(socket, today) do + assign(socket, [current_date: today, cursor_date: today]) + end + + defp assign_shift_template_changeset(%{assigns: %{shift_template: shift}} = socket) do + cs = Templates.change_shift_template(shift) + assign(socket, :shift_template_changeset, cs) + end + + defp init_shift_template(socket) do + first_list_id = socket.assigns.shift_templates |> hd() |> elem(1) + fave_id = socket.assigns.current_user.fave_shift_template_id + assign_shift_template(socket, (fave_id || first_list_id)) + end + + defp assign_shift_template(socket, template_id) do + assign(socket, :shift_template, get_shift_template(template_id)) + end + + defp init_shift_templates(%{assigns: %{current_user: user}} = socket) do + shift_templates = + Templates.list_shift_templates_by_user_id(user.id) + |> Enum.map(fn t -> shift_template_option(t, user.fave_shift_template_id) end) + |> Enum.concat([@custom_shift_opt]) + assign(socket, :shift_templates, shift_templates) + end + + defp shift_template_option(template, fave_id) do + label = + template.subject <> " (" <> + format_shift_time(template.time_start) <> "—" <> + format_shift_time(template.time_end) <> ")" + + label = + case fave_id == template.id do + true -> label <> " ★" + false -> label + end + + {label, template.id} + end + defp rotate_week(week_start_at) do {a, b} = Enum.split_while(WeekdayEnum.__enum_map__(), fn {k, _v} -> k != week_start_at end) b ++ a @@ -37,46 +118,160 @@ defmodule Shift73kWeb.ShiftAssignLive.Index do |> Timex.end_of_month() |> Timex.end_of_week(week_start_at) - Interval.new(from: first, until: last, right_open: false) - |> Enum.map(& &1) - |> Enum.chunk_every(7) + week_rows = + Interval.new(from: first, until: last, right_open: false) + |> Enum.map(& NaiveDateTime.to_date(&1)) + |> Enum.chunk_every(7) + + {first, last, week_rows} end - defp assign_day_names(socket) do - day_names = day_names(socket.assigns.current_user.week_start_at) - assign(socket, :day_names, day_names) + def day_color(day, current_date, cursor_date, selected_days) do + cond do + Enum.member?(selected_days, Date.to_string(day)) -> + cond do + Timex.compare(day, current_date, :days) == 0 -> "bg-triangle-info text-white" + day.month != cursor_date.month -> "bg-triangle-light text-gray" + true -> "bg-triangle-white" + end + + Timex.compare(day, current_date, :days) == 0 -> "bg-info text-white" + + day.month != cursor_date.month -> "bg-light text-gray" + + true -> "" + end end - defp assign_init_dates(socket, today) do - assign(socket, [current_date: today, cursor_date: today]) + defp prep_template_params(params, current_user) do + params + |> Map.put("time_start", Time.from_iso8601!("T#{params["time_start"]}:00")) + |> Map.put("time_end", Time.from_iso8601!("T#{params["time_end"]}:00")) + |> Map.put("user_id", current_user.id) end - defp assign_week_rows(socket) do - cursor_date = socket.assigns.cursor_date - week_start_at = socket.assigns.current_user.week_start_at - - assign(socket, :week_rows, week_rows(cursor_date, week_start_at)) + defp show_details_if_custom(socket) do + if (socket.assigns.shift_template.id != @custom_shift.id) || socket.assigns.show_template_details do + socket + else + socket + |> assign(:show_template_btn_active, :true) + |> push_event("toggle-template-details", %{targetId: "#templateDetailsCol"}) + end end - def handle_event("prev-month", _, socket) do - cursor_date = Timex.shift(socket.assigns.cursor_date, months: -1) + defp set_month(socket, new_cursor_date) do + {first, last, rows} = week_rows(new_cursor_date, socket.assigns.current_user.week_start_at) assigns = [ - cursor_date: cursor_date, - week_rows: week_rows(cursor_date, socket.assigns.current_user.week_start_at) + cursor_date: new_cursor_date, + week_rows: rows, + day_first: first, + day_last: last ] - {:noreply, assign(socket, assigns)} + assign(socket, assigns) end - def handle_event("next-month", _, socket) do - cursor_date = Timex.shift(socket.assigns.cursor_date, months: 1) + @impl true + def handle_event("validate-shift-template", %{"shift_template" => params}, socket) do + params = prep_template_params(params, socket.assigns.current_user) + shift_template = socket.assigns.shift_template - assigns = [ - cursor_date: cursor_date, - week_rows: week_rows(cursor_date, socket.assigns.current_user.week_start_at) - ] + cs = + shift_template + |> Templates.change_shift_template(params) + |> Map.put(:action, :validate) - {:noreply, assign(socket, assigns)} + socket + |> assign(:shift_template_changeset, cs) + |> assign(:shift_template, Map.merge(shift_template, cs.changes)) + |> assign_shift_length() + |> live_noreply() + end + + @impl true + def handle_event("change-selected-template", %{"template_select" => %{"template" => template_id}}, socket) do + socket + |> assign_shift_template(template_id) + |> show_details_if_custom() + |> assign_shift_length() + |> assign_shift_template_changeset() + |> live_noreply() + end + + @impl true + def handle_event("month-nav", %{"month" => direction}, socket) do + new_cursor = + cond do + direction == "now" -> Timex.today() + true -> + months = m = direction == "prev" && -1 || 1 + Timex.shift(socket.assigns.cursor_date, months: months) + end + + {:noreply, set_month(socket, new_cursor)} + end + + # @impl true + # def handle_event("prev-month", _, socket) do + # cursor_date = Timex.shift(socket.assigns.cursor_date, months: -1) + # {first, last, rows} = week_rows(cursor_date, socket.assigns.current_user.week_start_at) + + # assigns = [ + # cursor_date: cursor_date, + # week_rows: week_rows(cursor_date, socket.assigns.current_user.week_start_at) + # ] + + # {:noreply, assign(socket, assigns)} + # end + + # @impl true + # def handle_event("next-month", _, socket) do + # cursor_date = Timex.shift(socket.assigns.cursor_date, months: 1) + # {first, last, rows} = week_rows(cursor_date, socket.assigns.current_user.week_start_at) + + # assigns = [ + # cursor_date: cursor_date, + # week_rows: rows, + # day_first: first, + # day_last: last + # ] + + # {:noreply, assign(socket, assigns)} + # end + + @impl true + def handle_event("toggle-template-details", %{"target_id" => target_id}, socket) do + socket + |> assign(:show_template_btn_active, !socket.assigns.show_template_btn_active) + |> push_event("toggle-template-details", %{targetId: target_id}) + |> live_noreply() + end + + @impl true + def handle_event("collapse-shown", %{"target_id" => _target_id}, socket) do + {:noreply, assign(socket, :show_template_details, :true)} + end + + @impl true + def handle_event("collapse-hidden", %{"target_id" => _target_id}, socket) do + {:noreply, assign(socket, :show_template_details, :false)} + end + + @impl true + def handle_event("select-day", %{"day" => day}, socket) do + selected_days = + case day_index = Enum.find_index(socket.assigns.selected_days, fn d -> d == day end) do + nil -> [day | socket.assigns.selected_days] + _ -> List.delete_at(socket.assigns.selected_days, day_index) + end + + {:noreply, assign(socket, :selected_days, selected_days)} + end + + @impl true + def handle_event("clear-days", _params, socket) do + {:noreply, assign(socket, :selected_days, [])} end end diff --git a/lib/shift73k_web/live/shift_assign_live/index.html.leex b/lib/shift73k_web/live/shift_assign_live/index.html.leex index c47db54a..6fe3c5f8 100644 --- a/lib/shift73k_web/live/shift_assign_live/index.html.leex +++ b/lib/shift73k_web/live/shift_assign_live/index.html.leex @@ -1,31 +1,192 @@

- <%= icon_div @socket, "bi-calendar3", [class: "icon baseline"] %> + <%= icon_div @socket, "bi-calendar2-plus", [class: "icon baseline"] %> Assign Shift To Dates

+
+
+ + <%= form_for :template_select, "#", [phx_change: "change-selected-template"], fn sts -> %> + <%= label sts, :template, "Select shift template to assign to dates", class: "form-label" %> + <%= select sts, :template, @shift_templates, + value: @shift_template && @shift_template.id || (@shift_template_options |> hd() |> elem(1)), + class: "form-select" + %> + <% end %> + + + +
+ +
" id="#templateDetailsCol" phx-hook="BsCollapse"> +
+
+ + <%= form_for @shift_template_changeset, "#", [phx_change: "validate-shift-template"], fn stf -> %> + +
+ +
+ <%= label stf, :subject, "Subject/Title", class: "form-label" %> +
+ <%= icon_div @socket, "bi-tag", [class: "icon is-left text-muted fs-5"] %> + <%= text_input stf, :subject, + value: input_value(stf, :subject), + class: input_class(stf, :subject, "form-control"), + phx_debounce: 250, + disabled: @shift_template.id != @custom_shift.id, + aria_describedby: error_ids(stf, :subject) + %> + <%= error_tag stf, :subject %> +
+
+ +
+
+ +
+ <%= label stf, :time_start, "Start", class: "form-label" %> + <%= time_input stf, :time_start, + precision: :minute, + value: input_value(stf, :time_start), + class: input_class(stf, :time_start, "form-control"), + disabled: @shift_template.id != @custom_shift.id, + aria_describedby: error_ids(stf, :time_start) + %> +
+ +
+ <%= label stf, :time_end, "End", class: "form-label" %> + <%= time_input stf, :time_end, + precision: :minute, + value: input_value(stf, :time_end), + class: input_class(stf, :time_end, "form-control"), + disabled: @shift_template.id != @custom_shift.id, + aria_describedby: error_ids(stf, :time_end) + %> +
+ +
+ +
Shift length: <%= @shift_length %>
+ +
+ <%= error_tag stf, :time_start %> +
+
+ <%= error_tag stf, :time_end %> +
+
+ +
+ <%= label stf, :location, class: "form-label" %> +
+ <%= icon_div @socket, "bi-geo", [class: "icon is-left text-muted fs-5"] %> + <%= text_input stf, :location, + value: input_value(stf, :location), + class: input_class(stf, :location, "form-control"), + phx_debounce: 250, + disabled: @shift_template.id != @custom_shift.id, + aria_describedby: error_ids(stf, :location) + %> + <%= error_tag stf, :location %> +
+
+ +
+ <%= label stf, :time_zone, class: "form-label" %> +
+ <%= icon_div @socket, "bi-map", [class: "icon is-left text-muted fs-5"] %> + <%= text_input stf, :time_zone, + value: input_value(stf, :time_zone), + class: input_class(stf, :time_zone, "form-control"), + disabled: @shift_template.id != @custom_shift.id, + phx_debounce: 250, + list: "tz_list" + %> + + <%= for tz_name <- Timex.timezones() do %> + + <% end %> + end + + <%= if @shift_template.id == @custom_shift.id do %> +
Type to search & select from list of known <%= link "IANA tz database", to: "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones", target: "_blank" %> time zones
+ <% end %> + <%= error_tag stf, :time_zone %> +
+
+ +
+ <%= label stf, :description, class: "form-label" %> +
+ + <%= textarea stf, :description, + value: input_value(stf, :description), + class: input_class(stf, :description, "form-control"), + disabled: @shift_template.id != @custom_shift.id, + phx_debounce: 250, + aria_describedby: error_ids(stf, :description) + %> + <%= error_tag stf, :description %> +
+
+ +
+ + + + <% end %> + +
+
+ +
+
+ + + + + + + + + + + + + + <%# month navigation %> -
-

- <%= Timex.format!(@cursor_date, "%B %Y", :strftime) %> +
+

+ <%= Timex.format!(@cursor_date, "{Mfull} {YYYY}") %>

- <%# calendar month table display %> - +
<%= for {day_name, _i} <- Enum.with_index(@day_names) do %> - <% end %> @@ -35,23 +196,29 @@ <%= for week <- @week_rows do %> <%= for day <- week do %> - <%= cond do %> - - <% Timex.compare(day, @current_date, :days) == 0 -> %> - <% end %> <% end %>
+ <%= day_name %>
- - <% day.month != @cursor_date.month -> %> - - - <% true -> %> - - - <% end %> - - <%= Timex.format!(day, "%d", :strftime) %> + <%# day |> NaiveDateTime.to_date() |> IO.inspect() %> + + <%= Timex.format!(day, "{0D}") %><%= if day.month != @cursor_date.month, do: "-#{Timex.format!(day, "{Mshort}")}" %>
+ + +
+
+ + + + + +
+
diff --git a/lib/shift73k_web/live/shift_template_live/delete_component.html.leex b/lib/shift73k_web/live/shift_template_live/delete_component.html.leex index d43ce9d1..a14f6a74 100644 --- a/lib/shift73k_web/live/shift_template_live/delete_component.html.leex +++ b/lib/shift73k_web/live/shift_template_live/delete_component.html.leex @@ -1,9 +1,9 @@ diff --git a/lib/shift73k_web/live/shift_template_live/index.ex b/lib/shift73k_web/live/shift_template_live/index.ex index 22ece55f..af877f65 100644 --- a/lib/shift73k_web/live/shift_template_live/index.ex +++ b/lib/shift73k_web/live/shift_template_live/index.ex @@ -107,14 +107,4 @@ defmodule Shift73kWeb.ShiftTemplateLive.Index do socket |> put_flash(flash_type, msg) |> live_noreply() end - def format_shift_length(shift_template) do - shift_template - |> ShiftTemplate.shift_length() - |> Timex.Duration.from_minutes() - |> Timex.format_duration() - |> String.replace("PT", "") - |> String.replace("H", "h ") - |> String.replace("M", "m") - |> String.trim() - end end diff --git a/lib/shift73k_web/live/shift_template_live/index.html.leex b/lib/shift73k_web/live/shift_template_live/index.html.leex index 56b03aaf..75e40f00 100644 --- a/lib/shift73k_web/live/shift_template_live/index.html.leex +++ b/lib/shift73k_web/live/shift_template_live/index.html.leex @@ -25,7 +25,7 @@ My Shift Templates

<%= live_patch to: Routes.shift_template_index_path(@socket, :new), class: "btn btn-primary" do %> - <%= icon_div @socket, "bi-plus-circle-dotted", [class: "icon baseline me-1"] %> + <%= icon_div @socket, "bi-plus-circle-dotted", [class: "icon baseline", style: "margin-right:0.125rem;"] %> New Shift Template <% end %>
@@ -38,13 +38,14 @@
-
-
Subject:
- <%= template.subject %> +
+ Subject: + <%= icon_div @socket, "bi-tag", [class: "icon baseline me-1"] %> +
<%= template.subject %>
<%= if template.id == @current_user.fave_shift_template_id do %> - <%= icon_div @socket, "bi-star-fill", [class: "icon baseline text-primary align-self-start ms-2", phx_click: "unset-user-fave-shift-template"] %> + <%= icon_div @socket, "bi-star-fill", [class: "icon baseline text-primary align-self-start ms-2"], [role: "img", aria_hidden: false, aria_label: "Unset as favorite", phx_click: "unset-user-fave-shift-template", class: "cursor-pointer"] %> <% else %> - <%= icon_div @socket, "bi-star", [class: "icon baseline text-primary align-self-start ms-2", phx_click: "set-user-fave-shift-template", phx_value_id: template.id] %> + <%= icon_div @socket, "bi-star", [class: "icon baseline text-primary align-self-start ms-2"], [role: "img", aria_hidden: false, aria_label: "Set as favorite", phx_click: "set-user-fave-shift-template", phx_value_id: template.id, class: "cursor-pointer"] %> <% end %>
@@ -53,13 +54,13 @@ - <%= icon_div @socket, "bi-hourglass", [class: "icon text-muted"] %> + <%= icon_div @socket, "bi-hourglass", [class: "icon baseline text-muted"] %> Hours: - <%= template.time_start |> Timex.format!("{h12}:{m}{am}") %> + <%= format_shift_time(template.time_start) %> — - <%= template.time_end |> Timex.format!("{h12}:{m}{am}") %> + <%= format_shift_time(template.time_end) %> Shift length: (<%= format_shift_length(template) %>) diff --git a/lib/shift73k_web/live/user/settings/week_start.html.leex b/lib/shift73k_web/live/user/settings/week_start.html.leex index fe95ba87..1d5221f8 100644 --- a/lib/shift73k_web/live/user/settings/week_start.html.leex +++ b/lib/shift73k_web/live/user/settings/week_start.html.leex @@ -6,7 +6,7 @@ <%= label cvf, :week_start_at, class: "form-label" %>
- <%= icon_div @socket, "bi-calendar3-range", [class: "icon is-left text-muted fs-5"] %> + <%= icon_div @socket, "bi-calendar2-range", [class: "icon is-left text-muted fs-5"] %> <%= select cvf, :week_start_at, week_start_options(), value: @current_user.week_start_at, class: "form-select" diff --git a/lib/shift73k_web/live/user_management/index.html.leex b/lib/shift73k_web/live/user_management/index.html.leex index cb38f2d6..d4f539ac 100644 --- a/lib/shift73k_web/live/user_management/index.html.leex +++ b/lib/shift73k_web/live/user_management/index.html.leex @@ -1,5 +1,5 @@ <%= if @live_action in [:new, :edit] do %> - <%= live_modal @socket, Bones73kWeb.UserManagement.FormComponent, + <%= live_modal @socket, Shift73kWeb.UserManagement.FormComponent, id: @user.id || :new, title: @page_title, action: @live_action, @@ -8,7 +8,7 @@ <% end %> <%= if @delete_user do %> - <%= live_modal @socket, Bones73kWeb.UserManagement.DeleteComponent, + <%= live_modal @socket, Shift73kWeb.UserManagement.DeleteComponent, id: @delete_user.id, title: "Delete User", delete_user: @delete_user, diff --git a/lib/shift73k_web/templates/layout/_navbar.html.eex b/lib/shift73k_web/templates/layout/_navbar.html.eex index 611fa08a..e4dff3bf 100644 --- a/lib/shift73k_web/templates/layout/_navbar.html.eex +++ b/lib/shift73k_web/templates/layout/_navbar.html.eex @@ -3,7 +3,7 @@

<%= link to: Routes.page_path(@conn, :index), class: "navbar-brand fs-4" do %> - <%= icon_div @conn, "bi-calendar3-week", [class: "icon baseline me-1"] %> + <%= icon_div @conn, "bi-calendar2-week", [class: "icon baseline me-1"] %> Shift73k <% end %>