spazio-solazzo/lib/spazio_solazzo/data/calendar_ext.ex
Víctor Martínez 69f992f8f6
feat: new booking system + admin dashboard (#12)
feat: implement a new booking system and admin dashboard
2026-02-07 19:08:39 +01:00

144 lines
4.3 KiB
Elixir

defmodule SpazioSolazzo.CalendarExt do
@moduledoc """
Extension module for Calendar with helper date and time formatting functions
"""
def format_date(date) do
Calendar.strftime(date, "%B %d, %Y")
end
def format_time_range(%{start_time: start_time, end_time: end_time}) do
start_time = Calendar.strftime(start_time, "%I:%M %p")
end_time = Calendar.strftime(end_time, "%I:%M %p")
"#{start_time} - #{end_time}"
end
@doc """
Formats a datetime as "Feb 10, 2026"
"""
def format_datetime_date(%DateTime{} = datetime) do
Calendar.strftime(datetime, "%b %d, %Y")
end
@doc """
Formats a datetime as "Monday, February 10, 2026"
"""
def format_datetime_date_long(%DateTime{} = datetime) do
Calendar.strftime(datetime, "%A, %B %d, %Y")
end
@doc """
Formats a datetime as "Monday, February 10" (for emails)
"""
def format_datetime_date_only(%DateTime{} = datetime) do
Calendar.strftime(datetime, "%A, %B %d")
end
@doc """
Formats a time or datetime as "9:00 AM"
"""
def format_time(%DateTime{} = datetime) do
datetime
|> DateTime.to_time()
|> format_time()
end
def format_time(%Time{} = time) do
Calendar.strftime(time, "%I:%M %p")
end
@doc """
Formats a time range as "9:00 AM - 5:00 PM"
Takes two Time or DateTime structs
"""
def format_time_range(%DateTime{} = start_dt, %DateTime{} = end_dt) do
start_time = DateTime.to_time(start_dt)
end_time = DateTime.to_time(end_dt)
format_time_range(start_time, end_time)
end
def format_time_range(%Time{} = start_time, %Time{} = end_time) do
"#{format_time(start_time)} - #{format_time(end_time)}"
end
@doc """
Checks if a booking spans multiple days
"""
def multi_day?(%DateTime{} = start_datetime, %DateTime{} = end_datetime) do
start_date = DateTime.to_date(start_datetime)
end_date = DateTime.to_date(end_datetime)
Date.compare(start_date, end_date) != :eq
end
@doc """
Formats a datetime range handling both single-day and multi-day bookings.
Single-day: "Feb 10, 2026 9:00 AM - 5:00 PM"
Multi-day: "Feb 10, 2026 9:00 AM - Feb 15, 2026 5:00 PM"
"""
def format_datetime_range(%DateTime{} = start_datetime, %DateTime{} = end_datetime) do
if multi_day?(start_datetime, end_datetime) do
"#{format_datetime_date(start_datetime)} #{format_time(start_datetime)} - #{format_datetime_date(end_datetime)} #{format_time(end_datetime)}"
else
"#{format_datetime_date(start_datetime)} #{format_time_range(start_datetime, end_datetime)}"
end
end
@doc """
Formats the start portion of a datetime range for table display
Single-day: "Feb 10, 2026 9:00 AM"
Multi-day: "Feb 10, 2026 9:00 AM"
"""
def format_datetime_range_start(%DateTime{} = datetime) do
"#{format_datetime_date(datetime)} #{format_time(datetime)}"
end
@doc """
Formats the end portion of a datetime range for table display
Single-day: "5:00 PM" (date not shown)
Multi-day: "Feb 15, 2026 5:00 PM"
"""
def format_datetime_range_end(%DateTime{} = start_datetime, %DateTime{} = end_datetime) do
if multi_day?(start_datetime, end_datetime) do
"#{format_datetime_date(end_datetime)} #{format_time(end_datetime)}"
else
format_time(end_datetime)
end
end
# There are 7 days displayed in the calendar
@grid_cols 7
# The calendar can show max 6 weeks for one month
@grid_rows 6
@doc """
Build a list containing all the dates to be displayed in a
Calendar grid.
6 weeks * 7 days = 42 cells
"""
def build_calendar_grid(date) do
first_day = Date.beginning_of_month(date)
# Mon=1, Sun=7
start_day_of_week = Date.day_of_week(first_day)
# Calculate days to subtract to get to the previous Monday
# If starts on Mon (1), sub 0. If Sun (7), sub 6.
days_to_sub = start_day_of_week - 1
start_date = Date.add(first_day, -days_to_sub)
# 6 weeks * 7 days = 42 grid cells
Enum.map(0..(@grid_cols * @grid_rows - 1), fn i -> Date.add(start_date, i) end)
end
@doc "Checks if a date is within a start/end range (inclusive)"
def date_in_range?(date, start_date, end_date)
when not is_nil(start_date) and not is_nil(end_date) do
Date.compare(date, start_date) != :lt and Date.compare(date, end_date) != :gt
end
def date_in_range?(_date, _start, _end), do: false
end