1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-25 08:09:38 +02:00
Maybe/app/models/period.rb
Zach Gollwitzer 90a9546f32
Pre-launch design sync with Figma spec (#2154)
* Add lookbook + viewcomponent, organize design system file

* Build menu component

* Button updates

* More button fixes

* Replace all menus with new ViewComponent

* Checkpoint: fix tests, all buttons and menus converted

* Split into Link and Button components for clarity

* Button cleanup

* Simplify custom confirmation configuration in views

* Finalize button, link component API

* Add toggle field to custom form builder + Component

* Basic tabs component

* Custom tabs, convert all menu / tab instances in app

* Gem updates

* Centralized icon helper

* Update all icon usage to central helper

* Lint fixes

* Centralize all disclosure instances

* Dialog replacements

* Consolidation of all dialog styles

* Test fixes

* Fix app layout issues, move to component with slots

* Layout simplification

* Flakey test fix

* Fix dashboard mobile issues

* Finalize homepage

* Lint fixes

* Fix shadows and borders in dark mode

* Fix tests

* Remove stale class

* Fix filled icon logic

* Move transparent? to public interface
2025-04-30 18:14:22 -04:00

170 lines
4.1 KiB
Ruby

class Period
include ActiveModel::Validations, Comparable
class InvalidKeyError < StandardError; end
attr_reader :key, :start_date, :end_date
validates :start_date, :end_date, presence: true, if: -> { PERIODS[key].nil? }
validates :key, presence: true, if: -> { start_date.nil? || end_date.nil? }
validate :must_be_valid_date_range
PERIODS = {
"last_day" => {
date_range: [ 1.day.ago.to_date, Date.current ],
label_short: "1D",
label: "Last Day",
comparison_label: "vs. yesterday"
},
"current_week" => {
date_range: [ Date.current.beginning_of_week, Date.current ],
label_short: "WTD",
label: "Current Week",
comparison_label: "vs. start of week"
},
"last_7_days" => {
date_range: [ 7.days.ago.to_date, Date.current ],
label_short: "7D",
label: "Last 7 Days",
comparison_label: "vs. last week"
},
"current_month" => {
date_range: [ Date.current.beginning_of_month, Date.current ],
label_short: "MTD",
label: "Current Month",
comparison_label: "vs. start of month"
},
"last_30_days" => {
date_range: [ 30.days.ago.to_date, Date.current ],
label_short: "30D",
label: "Last 30 Days",
comparison_label: "vs. last month"
},
"last_90_days" => {
date_range: [ 90.days.ago.to_date, Date.current ],
label_short: "90D",
label: "Last 90 Days",
comparison_label: "vs. last quarter"
},
"current_year" => {
date_range: [ Date.current.beginning_of_year, Date.current ],
label_short: "YTD",
label: "Current Year",
comparison_label: "vs. start of year"
},
"last_365_days" => {
date_range: [ 365.days.ago.to_date, Date.current ],
label_short: "365D",
label: "Last 365 Days",
comparison_label: "vs. 1 year ago"
},
"last_5_years" => {
date_range: [ 5.years.ago.to_date, Date.current ],
label_short: "5Y",
label: "Last 5 Years",
comparison_label: "vs. 5 years ago"
}
}
class << self
def from_key(key)
unless PERIODS.key?(key)
raise InvalidKeyError, "Invalid period key: #{key}"
end
start_date, end_date = PERIODS[key].fetch(:date_range)
new(key: key, start_date: start_date, end_date: end_date)
end
def custom(start_date:, end_date:)
new(start_date: start_date, end_date: end_date)
end
def all
PERIODS.map { |key, period| from_key(key) }
end
def as_options
all.map { |period| [ period.label_short, period.key ] }
end
end
PERIODS.each do |key, period|
define_singleton_method(key) do
from_key(key)
end
end
def initialize(start_date: nil, end_date: nil, key: nil, date_format: "%b %d, %Y")
@key = key
@start_date = start_date
@end_date = end_date
@date_format = date_format
validate!
end
def <=>(other)
[ start_date, end_date ] <=> [ other.start_date, other.end_date ]
end
def date_range
start_date..end_date
end
def days
(end_date - start_date).to_i + 1
end
def within?(other)
start_date >= other.start_date && end_date <= other.end_date
end
def interval
if days > 366
"1 week"
else
"1 day"
end
end
def label
if key_metadata
key_metadata.fetch(:label)
else
"Custom Period"
end
end
def label_short
if key_metadata
key_metadata.fetch(:label_short)
else
"Custom"
end
end
def comparison_label
if key_metadata
key_metadata.fetch(:comparison_label)
else
"#{start_date.strftime(@date_format)} to #{end_date.strftime(@date_format)}"
end
end
private
def key_metadata
@key_metadata ||= PERIODS[key]
end
def must_be_valid_date_range
return if start_date.nil? || end_date.nil?
unless start_date.is_a?(Date) && end_date.is_a?(Date)
errors.add(:start_date, "must be a valid date, got #{start_date.inspect}")
errors.add(:end_date, "must be a valid date, got #{end_date.inspect}")
return
end
errors.add(:start_date, "must be before end date") if start_date > end_date
end
end