2024-02-02 09:05:04 -06:00
|
|
|
module ApplicationHelper
|
2024-03-08 15:11:58 -05:00
|
|
|
include Pagy::Frontend
|
|
|
|
|
2024-10-23 11:20:55 -04:00
|
|
|
def date_format_options
|
|
|
|
[
|
|
|
|
[ "DD-MM-YYYY", "%d-%m-%Y" ],
|
2024-11-11 15:57:50 +01:00
|
|
|
[ "DD.MM.YY", "%d.%m.%Y" ],
|
2024-10-23 11:20:55 -04:00
|
|
|
[ "MM-DD-YYYY", "%m-%d-%Y" ],
|
|
|
|
[ "YYYY-MM-DD", "%Y-%m-%d" ],
|
|
|
|
[ "DD/MM/YYYY", "%d/%m/%Y" ],
|
|
|
|
[ "YYYY/MM/DD", "%Y/%m/%d" ],
|
|
|
|
[ "MM/DD/YYYY", "%m/%d/%Y" ],
|
|
|
|
[ "D/MM/YYYY", "%e/%m/%Y" ],
|
|
|
|
[ "YYYY.MM.DD", "%Y.%m.%d" ]
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2024-02-02 09:05:04 -06:00
|
|
|
def title(page_title)
|
|
|
|
content_for(:title) { page_title }
|
|
|
|
end
|
|
|
|
|
|
|
|
def header_title(page_title)
|
|
|
|
content_for(:header_title) { page_title }
|
|
|
|
end
|
2024-02-02 23:41:58 +00:00
|
|
|
|
2024-07-18 14:39:38 -04:00
|
|
|
def family_notifications_stream
|
|
|
|
turbo_stream_from [ Current.family, :notifications ] if Current.family
|
|
|
|
end
|
|
|
|
|
2024-07-25 16:46:04 -04:00
|
|
|
def family_stream
|
|
|
|
turbo_stream_from Current.family if Current.family
|
|
|
|
end
|
|
|
|
|
2024-07-18 14:39:38 -04:00
|
|
|
def render_flash_notifications
|
|
|
|
notifications = flash.flat_map do |type, message_or_messages|
|
|
|
|
Array(message_or_messages).map do |message|
|
|
|
|
render partial: "shared/notification", locals: { type: type, message: message }
|
|
|
|
end
|
|
|
|
end
|
2024-02-13 15:19:11 +00:00
|
|
|
|
2024-07-18 14:39:38 -04:00
|
|
|
safe_join(notifications)
|
2024-02-13 15:19:11 +00:00
|
|
|
end
|
|
|
|
|
2024-05-30 20:55:18 -04:00
|
|
|
##
|
|
|
|
# Helper to open a centered and overlayed modal with custom contents
|
|
|
|
#
|
|
|
|
# @example Basic usage
|
|
|
|
# <%= modal classes: "custom-class" do %>
|
|
|
|
# <div>Content here</div>
|
|
|
|
# <% end %>
|
|
|
|
#
|
2024-04-29 21:17:28 +02:00
|
|
|
def modal(options = {}, &block)
|
2024-02-09 14:25:59 +00:00
|
|
|
content = capture &block
|
2024-04-29 21:17:28 +02:00
|
|
|
render partial: "shared/modal", locals: { content:, classes: options[:classes] }
|
2024-02-08 10:46:05 -06:00
|
|
|
end
|
2024-02-10 16:18:56 -06:00
|
|
|
|
2024-05-30 20:55:18 -04:00
|
|
|
##
|
|
|
|
# Helper to open a drawer on the right side of the screen with custom contents
|
|
|
|
#
|
|
|
|
# @example Basic usage
|
|
|
|
# <%= drawer do %>
|
|
|
|
# <div>Content here</div>
|
|
|
|
# <% end %>
|
|
|
|
#
|
|
|
|
def drawer(&block)
|
|
|
|
content = capture &block
|
|
|
|
render partial: "shared/drawer", locals: { content: content }
|
|
|
|
end
|
|
|
|
|
2024-09-20 08:38:19 -04:00
|
|
|
def disclosure(title, &block)
|
|
|
|
content = capture &block
|
|
|
|
render partial: "shared/disclosure", locals: { title: title, content: content }
|
|
|
|
end
|
|
|
|
|
2024-02-11 18:47:58 +00:00
|
|
|
def sidebar_link_to(name, path, options = {})
|
2024-04-18 07:56:51 -04:00
|
|
|
is_current = current_page?(path) || (request.path.start_with?(path) && path != "/")
|
2024-02-11 12:56:23 -06:00
|
|
|
|
2024-04-18 07:56:51 -04:00
|
|
|
classes = [
|
|
|
|
"flex items-center gap-2 px-3 py-2 rounded-xl border text-sm font-medium text-gray-500",
|
|
|
|
(is_current ? "bg-white text-gray-900 shadow-xs border-alpha-black-50" : "hover:bg-gray-100 border-transparent")
|
|
|
|
].compact.join(" ")
|
2024-02-11 18:47:58 +00:00
|
|
|
|
2024-04-18 07:56:51 -04:00
|
|
|
link_to path, **options.merge(class: classes), aria: { current: ("page" if current_page?(path)) } do
|
|
|
|
concat(lucide_icon(options[:icon], class: "w-5 h-5")) if options[:icon]
|
|
|
|
concat(name)
|
2024-02-11 18:47:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-01 10:49:43 -04:00
|
|
|
def mixed_hex_styles(hex)
|
|
|
|
color = hex || "#1570EF" # blue-600
|
|
|
|
|
|
|
|
<<-STYLE.strip
|
2024-10-18 14:37:42 -04:00
|
|
|
background-color: color-mix(in srgb, #{color} 10%, white);
|
|
|
|
border-color: color-mix(in srgb, #{color} 30%, white);
|
2024-07-01 10:49:43 -04:00
|
|
|
color: #{color};
|
|
|
|
STYLE
|
|
|
|
end
|
|
|
|
|
|
|
|
def circle_logo(name, hex: nil, size: "md")
|
|
|
|
render partial: "shared/circle_logo", locals: { name: name, hex: hex, size: size }
|
|
|
|
end
|
|
|
|
|
2024-05-17 09:09:32 -04:00
|
|
|
def return_to_path(params, fallback = root_path)
|
|
|
|
uri = URI.parse(params[:return_to] || fallback)
|
|
|
|
uri.relative? ? uri.path : root_path
|
|
|
|
end
|
|
|
|
|
2024-03-01 17:33:54 -05:00
|
|
|
def trend_styles(trend)
|
2024-03-06 09:56:59 -05:00
|
|
|
fallback = { bg_class: "bg-gray-500/5", text_class: "text-gray-500", symbol: "", icon: "minus" }
|
2024-04-22 06:30:42 -06:00
|
|
|
return fallback if trend.nil? || trend.direction.flat?
|
2024-03-06 09:56:59 -05:00
|
|
|
|
2024-02-22 11:35:06 -05:00
|
|
|
bg_class, text_class, symbol, icon = case trend.direction
|
2024-02-20 09:07:55 -05:00
|
|
|
when "up"
|
2024-04-22 06:30:42 -06:00
|
|
|
trend.favorable_direction.down? ? [ "bg-red-500/5", "text-red-500", "+", "arrow-up" ] : [ "bg-green-500/5", "text-green-500", "+", "arrow-up" ]
|
2024-02-20 09:07:55 -05:00
|
|
|
when "down"
|
2024-04-22 06:30:42 -06:00
|
|
|
trend.favorable_direction.down? ? [ "bg-green-500/5", "text-green-500", "-", "arrow-down" ] : [ "bg-red-500/5", "text-red-500", "-", "arrow-down" ]
|
2024-02-20 09:07:55 -05:00
|
|
|
when "flat"
|
|
|
|
[ "bg-gray-500/5", "text-gray-500", "", "minus" ]
|
|
|
|
else
|
2024-02-22 11:35:06 -05:00
|
|
|
raise ArgumentError, "Invalid trend direction: #{trend.direction}"
|
2024-02-20 09:07:55 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
{ bg_class: bg_class, text_class: text_class, symbol: symbol, icon: icon }
|
|
|
|
end
|
|
|
|
|
2024-10-23 11:20:55 -04:00
|
|
|
# Wrapper around I18n.l to support custom date formats
|
|
|
|
def format_date(object, format = :default, options = {})
|
|
|
|
date = object.to_date
|
|
|
|
|
|
|
|
format_code = options[:format_code] || Current.family&.date_format
|
|
|
|
|
|
|
|
if format_code.present?
|
|
|
|
date.strftime(format_code)
|
|
|
|
else
|
|
|
|
I18n.l(date, format: format, **options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-03-18 11:21:00 -04:00
|
|
|
def format_money(number_or_money, options = {})
|
2024-10-08 17:16:37 -04:00
|
|
|
return nil unless number_or_money
|
|
|
|
|
2024-03-18 11:21:00 -04:00
|
|
|
money = Money.new(number_or_money)
|
2024-10-03 10:25:38 -04:00
|
|
|
options.reverse_merge!(money.format_options(I18n.locale))
|
2024-03-18 11:21:00 -04:00
|
|
|
number_to_currency(money.amount, options)
|
|
|
|
end
|
2024-02-10 16:18:56 -06:00
|
|
|
|
2024-03-18 11:21:00 -04:00
|
|
|
def format_money_without_symbol(number_or_money, options = {})
|
2024-10-08 17:16:37 -04:00
|
|
|
return nil unless number_or_money
|
|
|
|
|
2024-03-18 11:21:00 -04:00
|
|
|
money = Money.new(number_or_money)
|
2024-10-03 10:25:38 -04:00
|
|
|
options.reverse_merge!(money.format_options(I18n.locale))
|
2024-03-18 11:21:00 -04:00
|
|
|
ActiveSupport::NumberHelper.number_to_delimited(money.amount.round(options[:precision] || 0), { delimiter: options[:delimiter], separator: options[:separator] })
|
2024-02-10 16:18:56 -06:00
|
|
|
end
|
2024-06-24 16:56:44 +02:00
|
|
|
|
2024-07-01 10:49:43 -04:00
|
|
|
def totals_by_currency(collection:, money_method:, separator: " | ", negate: false)
|
2024-06-24 16:56:44 +02:00
|
|
|
collection.group_by(&:currency)
|
|
|
|
.transform_values { |item| negate ? item.sum(&money_method) * -1 : item.sum(&money_method) }
|
|
|
|
.map { |_currency, money| format_money(money) }
|
|
|
|
.join(separator)
|
|
|
|
end
|
2024-02-02 09:05:04 -06:00
|
|
|
end
|