1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-08 06:55:21 +02:00

Multi-step account forms + clearer balance editing (#2427)
Some checks failed
Publish Docker image / ci (push) Has been cancelled
Publish Docker image / Build docker image (push) Has been cancelled

* Initial multi-step property form

* Improve form structure, add optional tooltip help icons to form fields

* Add basic inline alert component

* Clean up and improve property form lifecycle

* Implement Account status concept

* Lint fixes

* Remove whitespace

* Balance editing, scope updates for account

* Passing tests

* Fix brakeman warning

* Remove stale columns

* data constraint tweaks

* Redundant property
This commit is contained in:
Zach Gollwitzer 2025-07-03 09:33:07 -04:00 committed by GitHub
parent ba7e8d3893
commit 662f2c04ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 1036 additions and 427 deletions

View file

@ -0,0 +1,7 @@
<%# locals: (notice: nil, error: nil) %>
<% if notice.present? %>
<%= render AlertComponent.new(message: notice, variant: :success) %>
<% elsif error.present? %>
<%= render AlertComponent.new(message: error, variant: :error) %>
<% end %>

View file

@ -0,0 +1,16 @@
<%# locals: (label:, href: nil, active: false) %>
<% classes = class_names(
"flex items-center px-3 py-2 rounded-lg text-sm font-medium",
active ? "bg-surface-inset text-primary" : "text-secondary",
) %>
<% if href.present? %>
<%= link_to href, data: { turbo_frame: :modal }, class: class_names(classes, "cursor-pointer hover:bg-surface-inset-hover hover:text-primary") do %>
<%= label %>
<% end %>
<% else %>
<%= tag.span class: classes do %>
<%= label %>
<% end %>
<% end %>

View file

@ -0,0 +1,7 @@
<%# locals: (account:, active_tab:) %>
<div class="flex flex-col gap-0.5 w-[156px] shrink-0">
<%= render "properties/form_tab", label: "Overview", href: account.new_record? ? nil : edit_property_path(@account), active: active_tab == "overview" %>
<%= render "properties/form_tab", label: "Value", href: account.new_record? ? nil : balances_property_path(@account), active: active_tab == "value" %>
<%= render "properties/form_tab", label: "Address", href: account.new_record? ? nil : address_property_path(@account), active: active_tab == "address" %>
</div>

View file

@ -0,0 +1,35 @@
<%# locals: (form:) %>
<div class="flex flex-col gap-2">
<%= form.text_field :name,
label: "Name",
placeholder: "Vacation home",
required: true %>
<%= form.select :subtype,
Property::SUBTYPES.map { |k, v| [v[:long], k] },
{ prompt: "Select type", label: "Property type" }, required: true %>
<%= form.hidden_field :accountable_type, value: "Property" %>
<%= form.fields_for :accountable do |property_form| %>
<div class="flex items-center gap-2">
<%= property_form.number_field :year_built,
label: "Year Built (optional)",
placeholder: "1990",
min: 1800,
max: Time.current.year %>
</div>
<div class="flex items-center gap-2">
<%= property_form.number_field :area_value,
label: "Area (optional)",
placeholder: "1200",
min: 0 %>
<%= property_form.select :area_unit,
[["Square Feet", "sqft"], ["Square Meters", "sqm"]],
{ label: "Area Unit" } %>
</div>
<% end %>
</div>

View file

@ -0,0 +1,50 @@
<%= render DialogComponent.new do |dialog| %>
<% dialog.with_header(title: "Enter property manually") %>
<% dialog.with_body do %>
<div class="flex gap-4">
<!-- Left sidebar with tabs -->
<%= render "properties/form_tabs", account: @account, active_tab: "address" %>
<!-- Right content area with form -->
<div class="flex-1">
<%= styled_form_with model: @property, url: update_address_property_path(@account), method: :patch, data: { turbo_frame: @property.address.persisted? ? nil : :_top } do |form| %>
<div class="flex flex-col gap-2 min-h-[320px]">
<%= render "properties/form_alert", notice: @success_message, error: @error_message %>
<%= form.fields_for :address do |address_form| %>
<%= address_form.text_field :line1,
label: "Address Line 1",
placeholder: "123 Main Street" %>
<div class="flex items-center gap-2">
<%= address_form.text_field :locality,
label: "City",
placeholder: "San Francisco" %>
<%= address_form.text_field :region,
label: "State/Region",
placeholder: "CA" %>
</div>
<div class="flex items-center gap-2">
<%= address_form.text_field :postal_code,
label: "Postal Code",
placeholder: "12345" %>
<%= address_form.text_field :country,
label: "Country",
placeholder: "USA" %>
</div>
<% end %>
</div>
<!-- Save button -->
<div class="flex justify-end mt-4">
<%= render ButtonComponent.new(
text: "Save",
variant: "primary",
) %>
</div>
<% end %>
</div>
</div>
<% end %>
<% end %>

View file

@ -0,0 +1,30 @@
<%= render DialogComponent.new do |dialog| %>
<% dialog.with_header(title: "Enter property manually") %>
<% dialog.with_body do %>
<div class="flex gap-4">
<%= render "properties/form_tabs", account: @account, active_tab: "value" %>
<!-- Right content area with form -->
<div class="flex-1">
<%= styled_form_with model: @account, url: update_balances_property_path(@account), method: :patch do |form| %>
<div class="flex flex-col gap-4 min-h-[320px]">
<%= render "properties/form_alert", notice: @success_message, error: @error_message %>
<%= form.money_field :balance,
label: "Estimated market value",
label_tooltip: "The estimated market value of your property. This number can often be found on sites like Zillow or Redfin, and is never an exact number.",
placeholder: "0" %>
</div>
<!-- Next button -->
<div class="flex justify-end mt-4">
<%= render ButtonComponent.new(
text: @account.active? ? "Save" : "Next",
variant: "primary",
) %>
</div>
<% end %>
</div>
</div>
<% end %>
<% end %>

View file

@ -1,6 +1,27 @@
<%= render DialogComponent.new do |dialog| %>
<% dialog.with_header(title: t(".edit", account: @account.name)) %>
<% dialog.with_header(title: "Enter property manually") %>
<% dialog.with_body do %>
<%= render "form", account: @account, url: property_path(@account) %>
<div class="flex gap-4">
<!-- Left sidebar with tabs -->
<%= render "properties/form_tabs", account: @account, active_tab: "overview" %>
<!-- Right content area with form -->
<div class="flex-1">
<%= styled_form_with model: @account, url: property_path(@account), method: :patch do |form| %>
<div class="flex flex-col gap-2 min-h-[320px]">
<%= render "properties/form_alert", notice: @success_message, error: @error_message %>
<%= render "properties/overview_fields", form: form %>
</div>
<!-- Save button -->
<div class="flex justify-end mt-4">
<%= render ButtonComponent.new(
text: @account.active? ? "Save" : "Next",
variant: "primary",
) %>
</div>
<% end %>
</div>
</div>
<% end %>
<% end %>

View file

@ -1,6 +1,27 @@
<%= render DialogComponent.new do |dialog| %>
<% dialog.with_header(title: t(".title")) %>
<% dialog.with_header(title: "Enter property manually") %>
<% dialog.with_body do %>
<%= render "properties/form", account: @account, url: properties_path(return_to: params[:return_to]) %>
<div class="flex gap-4">
<!-- Left sidebar with tabs -->
<%= render "properties/form_tabs", account: @account, active_tab: "overview" %>
<!-- Right content area with form -->
<div class="flex-1">
<%= styled_form_with model: @account, url: properties_path do |form| %>
<div class="flex flex-col gap-2 min-h-[320px]">
<%= render "properties/form_alert", notice: @success_message, error: @error_message %>
<%= render "properties/overview_fields", form: form %>
</div>
<!-- Create button -->
<div class="flex justify-end mt-4">
<%= render ButtonComponent.new(
text: "Next",
variant: "primary",
) %>
</div>
<% end %>
</div>
</div>
<% end %>
<% end %>