mirror of
https://github.com/we-promise/sure.git
synced 2026-05-12 23:25:00 +00:00
Budget page refactor: split into(All - Over Budget - On Track) (#1195)
* Optimize UI in budget * update locales * Optimize UI * optimize suggested_daily_spending * try over_budget and on_track * update locale * optimize * add budgets_helper.rb * fix * hide no buget and no expense sub-catogory * Optimize * Optimize button on phone * Fix Pipelock CI noise * using section to render both overbudget and onTrack * hide last ruler * fix * update test --------- Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
@@ -1,44 +1,34 @@
|
||||
<%# locals: (budget:) %>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase">
|
||||
<p>Categories</p>
|
||||
<span class="text-subdued">·</span>
|
||||
<p><%= budget.budget_categories.count %></p>
|
||||
<% categories_state = budget_categories_view_state(budget) %>
|
||||
<% uncategorized_budget_category = categories_state[:uncategorized_budget_category] %>
|
||||
<% visible_expenses_empty = categories_state[:visible_expenses_empty] %>
|
||||
<% over_budget_groups = categories_state[:over_budget_groups] %>
|
||||
<% show_over_budget_uncategorized = categories_state[:show_over_budget_uncategorized] %>
|
||||
<% over_budget_count = categories_state[:over_budget_count] %>
|
||||
<% on_track_groups = categories_state[:on_track_groups] %>
|
||||
<% show_on_track_uncategorized = categories_state[:show_on_track_uncategorized] %>
|
||||
<% on_track_count = categories_state[:on_track_count] %>
|
||||
|
||||
<p class="ml-auto">Amount</p>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
|
||||
<div class="bg-container py-1 shadow-border-xs rounded-md">
|
||||
<% if budget.family.categories.expenses.empty? %>
|
||||
<div class="py-8">
|
||||
<%= render "budget_categories/no_categories" %>
|
||||
</div>
|
||||
<% else %>
|
||||
<% category_groups = BudgetCategory::Group.for(budget.budget_categories) %>
|
||||
<% if over_budget_count.positive? %>
|
||||
<%= render "budgets/category_section",
|
||||
budget: budget,
|
||||
count: over_budget_count,
|
||||
groups: over_budget_groups,
|
||||
uncategorized: uncategorized_budget_category,
|
||||
show_uncategorized: show_over_budget_uncategorized,
|
||||
over_budget_mode: true %>
|
||||
<% end %>
|
||||
|
||||
<% category_groups.each_with_index do |group, index| %>
|
||||
<div class="py-2">
|
||||
<%= render "budget_categories/budget_category", budget_category: group.budget_category %>
|
||||
<%= render "budgets/category_section",
|
||||
budget: budget,
|
||||
count: on_track_count,
|
||||
groups: on_track_groups,
|
||||
uncategorized: uncategorized_budget_category,
|
||||
show_uncategorized: show_on_track_uncategorized,
|
||||
over_budget_mode: false
|
||||
%>
|
||||
|
||||
<div>
|
||||
<% group.budget_subcategories.each do |budget_subcategory| %>
|
||||
<div class="w-full flex items-start">
|
||||
<div class="ml-8 pt-4 flex items-center justify-center text-subdued">
|
||||
<%= icon "corner-down-right" %>
|
||||
</div>
|
||||
|
||||
<%= render "budget_categories/budget_category", budget_category: budget_subcategory %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "shared/ruler" %>
|
||||
<% end %>
|
||||
<div class="py-2">
|
||||
<%= render "budget_categories/budget_category", budget_category: budget.uncategorized_budget_category %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
28
app/views/budgets/_category_group.html.erb
Normal file
28
app/views/budgets/_category_group.html.erb
Normal file
@@ -0,0 +1,28 @@
|
||||
<%# locals: (group:, parent_visible:, over_budget_mode: false) %>
|
||||
|
||||
<% if parent_visible %>
|
||||
<div class="py-2">
|
||||
<%= render "budget_categories/budget_category",
|
||||
budget_category: group.budget_category,
|
||||
show_budget_meta: (over_budget_mode ? group.budget_category.over_budget_with_budget? : true) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% group.budget_subcategories.each do |budget_subcategory| %>
|
||||
<% if parent_visible %>
|
||||
<div class="py-2 w-full flex items-start">
|
||||
<div class="ml-8 pt-4 flex items-center justify-center text-subdued">
|
||||
<%= icon "corner-down-right" %>
|
||||
</div>
|
||||
<%= render "budget_categories/budget_category",
|
||||
budget_category: budget_subcategory,
|
||||
show_budget_meta: (over_budget_mode ? budget_subcategory.over_budget_with_budget? : true) %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="py-2">
|
||||
<%= render "budget_categories/budget_category",
|
||||
budget_category: budget_subcategory,
|
||||
show_budget_meta: (over_budget_mode ? budget_subcategory.over_budget_with_budget? : true) %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
56
app/views/budgets/_category_section.html.erb
Normal file
56
app/views/budgets/_category_section.html.erb
Normal file
@@ -0,0 +1,56 @@
|
||||
<%# locals: (budget:, count:, groups:, uncategorized:, show_uncategorized:, over_budget_mode:) %>
|
||||
|
||||
<%# derive display config from over_budget_mode %>
|
||||
<%
|
||||
if over_budget_mode
|
||||
target = "overBudget"
|
||||
title = t("budgets.show.over_budget_categories.short_title")
|
||||
else
|
||||
target = "onTrack"
|
||||
title = t("budgets.show.on_track_categories.short_title")
|
||||
end
|
||||
%>
|
||||
|
||||
<div data-budget-filter-target="<%= target %>">
|
||||
|
||||
<!-- Section Header -->
|
||||
<div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase">
|
||||
<p><%= title %></p>
|
||||
<span class="text-subdued">·</span>
|
||||
<p><%= count %></p>
|
||||
<p class="ml-auto"><%= t("budgets.show.categories.amount") %></p>
|
||||
</div>
|
||||
|
||||
<!-- Section Body -->
|
||||
<div class="bg-container py-1 shadow-border-xs rounded-md">
|
||||
|
||||
<% groups.each_with_index do |group, index| %>
|
||||
|
||||
<%# derive parent visibility based on mode %>
|
||||
<%
|
||||
parent_visible =
|
||||
if over_budget_mode
|
||||
group.budget_category.any_over_budget?
|
||||
else
|
||||
budget.initialized? ? group.budget_category.visible_on_track? : true
|
||||
end
|
||||
%>
|
||||
<%= render "shared/ruler" unless index == 0 %>
|
||||
<%= render "budgets/category_group",
|
||||
group: group,
|
||||
parent_visible: parent_visible,
|
||||
over_budget_mode: over_budget_mode %>
|
||||
|
||||
<% end %>
|
||||
|
||||
<% if show_uncategorized %>
|
||||
<%= render "shared/ruler" unless groups.size == 0 %>
|
||||
<div class="py-2">
|
||||
<%= render "budget_categories/budget_category",
|
||||
budget_category: uncategorized,
|
||||
show_budget_meta: (over_budget_mode ? uncategorized.over_budget_with_budget? : true) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,23 +47,66 @@
|
||||
</div>
|
||||
|
||||
<%# Bottom Section: Categories full width %>
|
||||
<div class="w-full bg-container rounded-xl shadow-border-xs p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-lg font-medium">Categories</h2>
|
||||
<% has_over_budget = budget_has_over_budget?(@budget) %>
|
||||
<%= content_tag :div,
|
||||
class: "w-full bg-container rounded-xl shadow-border-xs p-4",
|
||||
data: (has_over_budget ? { controller: "budget-filter" } : {}) do %>
|
||||
<div class="flex items-center mb-4 flex-nowrap">
|
||||
<h2 class="text-lg font-medium shrink-0">
|
||||
<%= t("budgets.show.categories.title") %>
|
||||
</h2>
|
||||
|
||||
<% if @budget.initialized? %>
|
||||
<%= render DS::Link.new(
|
||||
text: "Edit",
|
||||
variant: "secondary",
|
||||
icon: "settings-2",
|
||||
href: budget_budget_categories_path(@budget)
|
||||
) %>
|
||||
<% if has_over_budget %>
|
||||
<div class="flex-1 min-w-0 px-1">
|
||||
<div class="w-full flex justify-center">
|
||||
<div class="max-w-full overflow-x-auto no-scrollbar">
|
||||
<div class="inline-flex whitespace-nowrap bg-container-inset rounded-lg p-1 text-sm font-medium gap-0.5">
|
||||
|
||||
<button
|
||||
data-action="click->budget-filter#setFilter"
|
||||
data-budget-filter-filter-param="all"
|
||||
data-budget-filter-target="tab"
|
||||
class="px-3 py-1.5 rounded-md transition-colors bg-container text-primary shadow-sm">
|
||||
<%= t("budgets.show.filter.all") %>
|
||||
</button>
|
||||
|
||||
<button
|
||||
data-action="click->budget-filter#setFilter"
|
||||
data-budget-filter-filter-param="over_budget"
|
||||
data-budget-filter-target="tab"
|
||||
class="px-3 py-1.5 rounded-md transition-colors text-secondary">
|
||||
<%= t("budgets.show.filter.over_budget") %>
|
||||
</button>
|
||||
|
||||
<button
|
||||
data-action="click->budget-filter#setFilter"
|
||||
data-budget-filter-filter-param="on_track"
|
||||
data-budget-filter-target="tab"
|
||||
class="px-3 py-1.5 rounded-md transition-colors text-secondary">
|
||||
<%= t("budgets.show.filter.on_track") %>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="<%= has_over_budget ? "shrink-0 flex justify-end whitespace-nowrap" : "ml-auto" %>">
|
||||
<% if @budget.initialized? %>
|
||||
<%= render DS::Link.new(
|
||||
text: t("budgets.show.categories.edit"),
|
||||
variant: "secondary",
|
||||
icon: "settings-2",
|
||||
href: budget_budget_categories_path(@budget)
|
||||
) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-container-inset rounded-xl p-1">
|
||||
<%= render "budgets/budget_categories", budget: @budget %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user