1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-20 21:59:38 +02:00

Fix budget allocation forms from resetting and clearing data on slow networks (#1804)

* First pass

* Fix null constraint bug for budget category assignment

* Fix autofocus reset when allocating budget

* Lint fix
This commit is contained in:
Zach Gollwitzer 2025-02-05 09:09:38 -05:00 committed by GitHub
parent f498212b2d
commit 5b083c9e33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 56 additions and 53 deletions

View file

@ -1,12 +1,12 @@
class BudgetCategoriesController < ApplicationController class BudgetCategoriesController < ApplicationController
before_action :set_budget
def index def index
@budget = Current.family.budgets.find(params[:budget_id]) @budget_categories = @budget.budget_categories.includes(:category)
render layout: "wizard" render layout: "wizard"
end end
def show def show
@budget = Current.family.budgets.find(params[:budget_id])
@recent_transactions = @budget.entries @recent_transactions = @budget.entries
if params[:id] == BudgetCategory.uncategorized.id if params[:id] == BudgetCategory.uncategorized.id
@ -23,13 +23,25 @@ class BudgetCategoriesController < ApplicationController
def update def update
@budget_category = Current.family.budget_categories.find(params[:id]) @budget_category = Current.family.budget_categories.find(params[:id])
@budget_category.update!(budget_category_params)
redirect_to budget_budget_categories_path(@budget_category.budget) if @budget_category.update(budget_category_params)
respond_to do |format|
format.turbo_stream
format.html { redirect_to budget_budget_categories_path(@budget) }
end
else
render :index, status: :unprocessable_entity
end
end end
private private
def budget_category_params def budget_category_params
params.require(:budget_category).permit(:budgeted_spending) params.require(:budget_category).permit(:budgeted_spending).tap do |params|
params[:budgeted_spending] = params[:budgeted_spending].presence || 0
end
end
def set_budget
@budget = Current.family.budgets.find(params[:budget_id])
end end
end end

View file

@ -25,7 +25,7 @@ export default class extends Controller {
} }
handleInput = (event) => { handleInput = (event) => {
const target = event.target const target = event.target;
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
@ -34,18 +34,18 @@ export default class extends Controller {
}; };
#debounceTimeout(element) { #debounceTimeout(element) {
if(element.dataset.autosubmitDebounceTimeout) { if (element.dataset.autosubmitDebounceTimeout) {
return Number.parseInt(element.dataset.autosubmitDebounceTimeout); return Number.parseInt(element.dataset.autosubmitDebounceTimeout);
} }
const type = element.type || element.tagName; const type = element.type || element.tagName;
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case 'input': case "input":
case 'textarea': case "textarea":
return 500; return 500;
case 'select-one': case "select-one":
case 'select-multiple': case "select-multiple":
return 0; return 0;
default: default:
return 500; return 500;

View file

@ -1,26 +1,44 @@
<%# locals: (budget:) %> <%# locals: (budget:) %>
<div class="space-y-2 mb-6"> <div id="<%= dom_id(budget, :allocation_progress) %>" class="space-y-2 mb-6">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<% if budget.available_to_allocate.negative? %>
<div class="rounded-full w-1.5 h-1.5 bg-red-500"></div>
<% else %>
<div class="rounded-full w-1.5 h-1.5 <%= budget.allocated_spending > 0 ? "bg-gray-900" : "bg-gray-100" %>"></div> <div class="rounded-full w-1.5 h-1.5 <%= budget.allocated_spending > 0 ? "bg-gray-900" : "bg-gray-100" %>"></div>
<% end %>
<% if budget.available_to_allocate.negative? %>
<p class="text-gray-900 text-sm">&gt; 100% set</p>
<% else %>
<p class="text-gray-500 text-sm"> <p class="text-gray-500 text-sm">
<%= number_to_percentage(budget.allocated_percent, precision: 0) %> set <%= number_to_percentage(budget.allocated_percent, precision: 0) %> set
</p> </p>
<% end %>
<p class="ml-auto text-sm space-x-1"> <p class="ml-auto text-sm space-x-1">
<span class="text-gray-900"><%= format_money(budget.allocated_spending_money) %></span> <span class="<%= budget.available_to_allocate.negative? ? "text-red-500" : "text-gray-900" %>"><%= format_money(budget.allocated_spending_money) %></span>
<span class="text-gray-500"> / </span> <span class="text-gray-500"> / </span>
<span class="text-gray-500"><%= format_money(budget.budgeted_spending_money) %></span> <span class="text-gray-500"><%= format_money(budget.budgeted_spending_money) %></span>
</p> </p>
</div> </div>
<div class="relative h-1.5 rounded-2xl bg-gray-100"> <div class="relative h-1.5 rounded-2xl bg-gray-100">
<% if budget.available_to_allocate.negative? %>
<div class="absolute inset-0 bg-red-500 rounded-2xl" style="width: 100%;"></div>
<% else %>
<div class="absolute inset-0 bg-gray-900 rounded-2xl" style="width: <%= budget.allocated_percent %>%;"></div> <div class="absolute inset-0 bg-gray-900 rounded-2xl" style="width: <%= budget.allocated_percent %>%;"></div>
<% end %>
</div> </div>
<div class="text-sm"> <div class="text-sm">
<% if budget.available_to_allocate.negative? %>
<p class="text-gray-500">
Budget exceeded by <span class="text-red-500"><%= format_money(budget.available_to_allocate_money.abs) %></span>
</p>
<% else %>
<span class="text-gray-900"><%= format_money(budget.available_to_allocate_money) %></span> <span class="text-gray-900"><%= format_money(budget.available_to_allocate_money) %></span>
<span class="text-gray-500">left to allocate</span> <span class="text-gray-500">left to allocate</span>
<% end %>
</div> </div>
</div> </div>

View file

@ -1,25 +0,0 @@
<%# locals: (budget:) %>
<div class="space-y-2 mb-6">
<div class="flex items-center gap-2">
<div class="rounded-full w-1.5 h-1.5 bg-red-500"></div>
<p class="text-gray-900 text-sm">&gt; 100% set</p>
<p class="ml-auto text-sm space-x-1">
<span class="text-red-500"><%= format_money(budget.allocated_spending_money) %></span>
<span class="text-gray-500"> / </span>
<span class="text-gray-500"><%= format_money(budget.budgeted_spending_money) %></span>
</p>
</div>
<div class="relative h-1.5 rounded-2xl bg-gray-100">
<div class="absolute inset-0 bg-red-500 rounded-2xl" style="width: 100%;"></div>
</div>
<div class="text-sm">
<p class="text-gray-500">
Budget exceeded by <span class="text-red-500"><%= format_money(budget.available_to_allocate_money.abs) %></span>
</p>
</div>
</div>

View file

@ -12,7 +12,7 @@
</div> </div>
<div class="ml-auto"> <div class="ml-auto">
<%= form_with model: [budget_category.budget, budget_category], data: { controller: "auto-submit-form", auto_submit_form_trigger_event_value: "blur", turbo_frame: :_top } do |f| %> <%= form_with model: [budget_category.budget, budget_category], data: { controller: "auto-submit-form preserve-focus" } do |f| %>
<div class="form-field w-[120px]"> <div class="form-field w-[120px]">
<div class="flex items-center"> <div class="flex items-center">
<span class="text-gray-500 text-sm mr-2"><%= currency.symbol %></span> <span class="text-gray-500 text-sm mr-2"><%= currency.symbol %></span>
@ -20,6 +20,7 @@
class: "form-field__input text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none", class: "form-field__input text-right [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
placeholder: "0", placeholder: "0",
step: currency.step, step: currency.step,
id: dom_id(budget_category, :budgeted_spending),
min: 0, min: 0,
data: { auto_submit_form_target: "auto" } %> data: { auto_submit_form_target: "auto" } %>
</div> </div>

View file

@ -21,14 +21,10 @@
</div> </div>
<% else %> <% else %>
<div class="max-w-md mx-auto"> <div class="max-w-md mx-auto">
<% if @budget.available_to_allocate.negative? %>
<%= render "budget_categories/allocation_progress_overage", budget: @budget %>
<% else %>
<%= render "budget_categories/allocation_progress", budget: @budget %> <%= render "budget_categories/allocation_progress", budget: @budget %>
<% end %>
<div class="space-y-4 mb-4"> <div class="space-y-4 mb-4">
<% BudgetCategory::Group.for(@budget.budget_categories).sort_by(&:name).each do |group| %> <% BudgetCategory::Group.for(@budget_categories).sort_by(&:name).each do |group| %>
<div class="space-y-4"> <div class="space-y-4">
<%= render "budget_categories/budget_category_form", budget_category: group.budget_category %> <%= render "budget_categories/budget_category_form", budget_category: group.budget_category %>

View file

@ -0,0 +1 @@
<%= turbo_stream.replace dom_id(@budget, :allocation_progress), partial: "budget_categories/allocation_progress", locals: { budget: @budget } %>