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)
* 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:
parent
ba7e8d3893
commit
662f2c04ce
66 changed files with 1036 additions and 427 deletions
7
app/views/properties/_form_alert.html.erb
Normal file
7
app/views/properties/_form_alert.html.erb
Normal 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 %>
|
16
app/views/properties/_form_tab.html.erb
Normal file
16
app/views/properties/_form_tab.html.erb
Normal 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 %>
|
7
app/views/properties/_form_tabs.html.erb
Normal file
7
app/views/properties/_form_tabs.html.erb
Normal 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>
|
35
app/views/properties/_overview_fields.html.erb
Normal file
35
app/views/properties/_overview_fields.html.erb
Normal 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>
|
50
app/views/properties/address.html.erb
Normal file
50
app/views/properties/address.html.erb
Normal 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 %>
|
30
app/views/properties/balances.html.erb
Normal file
30
app/views/properties/balances.html.erb
Normal 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 %>
|
|
@ -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 %>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue