diff --git a/lib/atomic_web/components/calendar/month.ex b/lib/atomic_web/components/calendar/month.ex index 27b18b971..85d4c47d8 100644 --- a/lib/atomic_web/components/calendar/month.ex +++ b/lib/atomic_web/components/calendar/month.ex @@ -72,6 +72,32 @@ defmodule AtomicWeb.Components.CalendarMonth do """ end + @doc """ + Generates a list of days for the calendar month, including days from the previous and next months to fill the 6x7 grid. + + ## Example + + iex> generate_days_list(~D[2024-09-01], ~D[2024-09-30]) + [ + ~D[2024-08-26], + ~D[2024-08-27], + ~D[2024-08-28], + ~D[2024-08-29], + ~D[2024-08-30], + ~D[2024-08-31], + ~D[2024-09-01], + ~D[2024-09-02], + ..., + ~D[2024-09-29], + ~D[2024-09-30], + ~D[2024-10-01], + ~D[2024-10-02], + ~D[2024-10-03], + ~D[2024-10-04], + ~D[2024-10-05], + ~D[2024-10-06] + ] + """ defp generate_days_list(beginning_of_month, end_of_month) do days_from_last_month = Timex.weekday(beginning_of_month, :monday) diff --git a/lib/atomic_web/components/calendar/week.ex b/lib/atomic_web/components/calendar/week.ex index 44f297791..63e173a7c 100644 --- a/lib/atomic_web/components/calendar/week.ex +++ b/lib/atomic_web/components/calendar/week.ex @@ -101,18 +101,20 @@ defmodule AtomicWeb.Components.CalendarWeek do end defp day(assigns) do + assigns = + assigns + |> assign_activities_positions(get_date_activities(assigns.activities, assigns.date)) + ~H""" - <%= for activity <- get_date_activities(@activities, @date) do %> - <% width = calc_total_overlaps(activity, @activities) + 1 %> - <% left = calc_left_amount(activity, @activities) %> + <%= for {activity, width, left} <- @activities_with_positions do %>
<%= activity.title %> @@ -127,6 +129,26 @@ defmodule AtomicWeb.Components.CalendarWeek do """ end + defp assign_activities_positions(assigns, day_activities) do + activities_with_positions = + Enum.map(day_activities, fn activity -> + # Calculate the width of the activity in percentage + width = 1 / (calc_total_overlaps(activity, day_activities) + 1) * 100 + + # Calculate the left position of the activity in percentage + left = + calc_left_amount( + activity, + day_activities, + calc_total_overlaps(activity, day_activities) + 1 + ) + + {activity, width, left} + end) + + assign(assigns, :activities_with_positions, activities_with_positions) + end + defp calc_row_start(start) do hours = start @@ -141,6 +163,15 @@ defmodule AtomicWeb.Components.CalendarWeek do hours * 12 + div(minutes, 5) + 2 end + @doc """ + Calculates the number of grid rows the activity should span based on the time difference between the start and finish. + Each row spans a 5-minute interval. + + ## Example + + iex> calc_time(~N[2024-09-11 09:00:00], ~N[2024-09-11 10:00:00]) + 12 + """ defp calc_time(start, finish) do time_diff = (NaiveDateTime.diff(finish, start) / 60) |> trunc() @@ -151,7 +182,23 @@ defmodule AtomicWeb.Components.CalendarWeek do end end - defp calc_total_overlaps(current_activity, activities) do + @doc """ + Calculates the total number of overlapping activities with the current activity on the same day. + + ## Example + + iex> activities = [ + ...> %{start: ~N[2024-09-11 09:00:00], finish: ~N[2024-09-11 10:00:00]}, + ...> %{start: ~N[2024-09-11 09:30:00], finish: ~N[2024-09-11 10:30:00]} + ...> ] + ...> + ...> calc_total_overlaps( + ...> %{start: ~N[2024-09-11 09:00:00], finish: ~N[2024-09-11 10:00:00]}, + ...> activities + ...> ) + 1 + """ + def calc_total_overlaps(current_activity, activities) do current_interval = Timex.Interval.new(from: current_activity.start, until: current_activity.finish) @@ -165,19 +212,47 @@ defmodule AtomicWeb.Components.CalendarWeek do |> length() end - defp calc_left_amount(current_activity, activities) do + @doc """ + Calculates the left offset percentage for positioning an activity on the calendar grid. + + The left offset is determined by the number of overlapping activities prior to the current one. + + + ## Example + + iex> activities = [ + ...> %{start: ~N[2024-09-11 09:00:00], finish: ~N[2024-09-11 10:00:00]}, + ...> %{start: ~N[2024-09-11 09:30:00], finish: ~N[2024-09-11 10:30:00]} + ...> ] + ...> + ...> calc_left_amount( + ...> %{start: ~N[2024-09-11 09:30:00], finish: ~N[2024-09-11 10:30:00]}, + ...> activities, + ...> 2 + ...> ) + 50.0 + """ + defp calc_left_amount(current_activity, activities, activity_total_overlaps) do current_interval = Timex.Interval.new(from: current_activity.start, until: current_activity.finish) - activities - |> Enum.take_while(fn activity -> activity != current_activity end) - |> Enum.filter(fn activity -> - activity_interval = Timex.Interval.new(from: activity.start, until: activity.finish) + # Total number of overlaps prior to the current activity + total_overlaps = + activities + |> Enum.take_while(fn activity -> activity != current_activity end) + |> Enum.filter(fn activity -> + activity_interval = Timex.Interval.new(from: activity.start, until: activity.finish) - activity != current_activity && activity.start.day == current_activity.start.day and - Timex.Interval.overlaps?(current_interval, activity_interval) - end) - |> length() + activity != current_activity && activity.start.day == current_activity.start.day and + Timex.Interval.overlaps?(current_interval, activity_interval) + end) + |> length() + + if total_overlaps > 0 do + total_overlaps / activity_total_overlaps * 100 + else + 0 + end end defp hours, diff --git a/lib/atomic_web/live/calendar_live/show.html.heex b/lib/atomic_web/live/calendar_live/show.html.heex index 756763c20..c046f2b26 100644 --- a/lib/atomic_web/live/calendar_live/show.html.heex +++ b/lib/atomic_web/live/calendar_live/show.html.heex @@ -8,10 +8,9 @@ <%= if @mode == "month" do %> <% else %> - <%= case date_to_month(@beginning_of_week) == date_to_month(@end_of_week) do %> - <% true -> %> + <%= if date_to_month(@beginning_of_week) == date_to_month(@end_of_week) do %> - <% _ -> %> + <% else %> <%= if date_to_year(@beginning_of_week) == date_to_year(@end_of_week) do %> <% else %>