defmodule MvWeb.MemberLive.Index.DatePresets do @moduledoc """ Relative date-range presets for the date value controls (§1.25/§2.8). Each preset resolves against a reference date "today" into a concrete `%{from: Date.t(), to: Date.t()}` range. Rolling "last N days/months" presets end on today; the "this month/quarter/year" presets run period-to-date from the respective period start up to today. Resolving is pure and clock-free — the caller passes `today` (defaulting to `Date.utc_today/0`), so the resolved bounds are testable and reproducible. The resolved bounds feed the same `jd_*` / `ed_*` / `cdf_*` URL params as an absolute custom range; presets are purely an input convenience and add no new persistence dimension. """ @type preset :: :last_7_days | :last_30_days | :last_3_months | :last_12_months | :this_month | :this_quarter | :this_year @type range :: %{from: Date.t(), to: Date.t()} @presets [ :last_7_days, :last_30_days, :last_3_months, :last_12_months, :this_month, :this_quarter, :this_year ] @doc """ Returns every preset key in display order (rolling windows first, then the period-to-date presets), for rendering the preset radio list. """ @spec all() :: [preset()] def all, do: @presets @doc """ Whether `key` names a known preset. """ @spec preset?(term()) :: boolean() def preset?(key), do: key in @presets @doc """ Resolves a preset to a concrete `{from, to}` range relative to `today` (default `Date.utc_today/0`). """ @spec resolve(preset(), Date.t()) :: range() def resolve(preset, today \\ Date.utc_today()) def resolve(:last_7_days, today), do: %{from: Date.add(today, -6), to: today} def resolve(:last_30_days, today), do: %{from: Date.add(today, -29), to: today} def resolve(:last_3_months, today), do: %{from: Date.shift(today, month: -3), to: today} def resolve(:last_12_months, today), do: %{from: Date.shift(today, month: -12), to: today} def resolve(:this_month, today), do: %{from: %{today | day: 1}, to: today} def resolve(:this_quarter, today) do first_month = div(today.month - 1, 3) * 3 + 1 %{from: Date.new!(today.year, first_month, 1), to: today} end def resolve(:this_year, today), do: %{from: Date.new!(today.year, 1, 1), to: today} end