mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-07 06:25:19 +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
|
@ -6,7 +6,7 @@
|
|||
<%= render "accounts/logo", account: account, size: "md" %>
|
||||
|
||||
<div>
|
||||
<% if account.scheduled_for_deletion? %>
|
||||
<% if account.pending_deletion? %>
|
||||
<p class="text-sm font-medium text-primary">
|
||||
<span>
|
||||
<%= account.name %>
|
||||
|
@ -16,31 +16,45 @@
|
|||
</span>
|
||||
</p>
|
||||
<% else %>
|
||||
<%= link_to account.name, account, class: [(account.is_active ? "text-primary" : "text-subdued"), "text-sm font-medium hover:underline"], data: { turbo_frame: "_top" } %>
|
||||
<%= link_to account.name, account, class: [(account.active? ? "text-primary" : "text-subdued"), "text-sm font-medium hover:underline"], data: { turbo_frame: "_top" } %>
|
||||
<% if account.long_subtype_label %>
|
||||
<p class="text-sm text-secondary truncate"><%= account.long_subtype_label %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% unless account.scheduled_for_deletion? %>
|
||||
<% unless account.pending_deletion? %>
|
||||
<%= link_to edit_account_path(account, return_to: return_to), data: { turbo_frame: :modal }, class: "group-hover/account:flex hidden hover:opacity-80 items-center justify-center" do %>
|
||||
<%= icon("pencil-line", size: "sm") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="flex items-center gap-8">
|
||||
<% if account.syncing? %>
|
||||
<% if account.draft? %>
|
||||
<!-- Balance hidden for draft accounts -->
|
||||
<% elsif account.syncing? %>
|
||||
<div class="w-16 h-6 bg-loader rounded-full animate-pulse"></div>
|
||||
<% else %>
|
||||
<p class="text-sm font-medium <%= account.is_active ? "text-primary" : "text-subdued" %>">
|
||||
<p class="text-sm font-medium <%= account.active? ? "text-primary" : "text-subdued" %>">
|
||||
<%= format_money account.balance_money %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<% unless account.scheduled_for_deletion? %>
|
||||
<%= styled_form_with model: account, data: { turbo_frame: "_top", controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.toggle :is_active, { data: { auto_submit_form_target: "auto" } } %>
|
||||
<% if account.draft? %>
|
||||
<%= render LinkComponent.new(
|
||||
text: "Complete setup",
|
||||
href: edit_account_path(account, return_to: return_to),
|
||||
variant: :outline,
|
||||
frame: :modal
|
||||
) %>
|
||||
<% elsif account.active? || account.disabled? %>
|
||||
<%= form_with model: account, url: toggle_active_account_path(account), method: :patch, data: { turbo_frame: "_top", controller: "auto-submit-form" } do |f| %>
|
||||
<%= render ToggleComponent.new(
|
||||
id: "account_#{account.id}_active",
|
||||
name: "active",
|
||||
checked: account.active?,
|
||||
data: { auto_submit_form_target: "auto" }
|
||||
) %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<%# locals: (account:, url:) %>
|
||||
|
||||
<% if @error_message.present? %>
|
||||
<%= render AlertComponent.new(message: @error_message, variant: :error) %>
|
||||
<% end %>
|
||||
|
||||
<%= styled_form_with model: account, url: url, scope: :account, data: { turbo: false }, class: "flex flex-col gap-4 justify-between grow text-primary" do |form| %>
|
||||
<div class="grow space-y-2">
|
||||
<%= form.hidden_field :accountable_type %>
|
||||
|
|
|
@ -12,7 +12,18 @@
|
|||
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="truncate">
|
||||
<h2 class="font-medium text-xl truncate <%= "animate-pulse" if account.syncing? %>"><%= title || account.name %></h2>
|
||||
<div class="flex items-center gap-3">
|
||||
<h2 class="font-medium text-xl truncate <%= "animate-pulse" if account.syncing? %>"><%= title || account.name %></h2>
|
||||
<% if account.draft? %>
|
||||
<%= render LinkComponent.new(
|
||||
text: "Complete setup",
|
||||
href: edit_account_path(account),
|
||||
variant: :outline,
|
||||
size: :sm,
|
||||
frame: :modal
|
||||
) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if subtitle.present? %>
|
||||
<p class="text-sm text-secondary"><%= subtitle %></p>
|
||||
<% end %>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<%= form.select :col_sep, Import::SEPARATORS, label: true %>
|
||||
|
||||
<% if @import.type == "TransactionImport" || @import.type == "TradeImport" %>
|
||||
<%= form.select :account_id, @import.family.accounts.pluck(:name, :id), { label: "Account (optional)", include_blank: "Multi-account import", selected: @import.account_id } %>
|
||||
<%= form.select :account_id, @import.family.accounts.visible.pluck(:name, :id), { label: "Account (optional)", include_blank: "Multi-account import", selected: @import.account_id } %>
|
||||
<% end %>
|
||||
|
||||
<div class="flex flex-col items-center justify-center w-full h-64 border border-secondary border-dashed rounded-xl cursor-pointer" data-controller="file-upload" data-action="click->file-upload#triggerFileInput" data-file-upload-target="uploadArea">
|
||||
|
@ -54,7 +54,7 @@
|
|||
<%= form.select :col_sep, Import::SEPARATORS, label: true %>
|
||||
|
||||
<% if @import.type == "TransactionImport" || @import.type == "TradeImport" %>
|
||||
<%= form.select :account_id, @import.family.accounts.pluck(:name, :id), { label: "Account (optional)", include_blank: "Multi-account import", selected: @import.account_id } %>
|
||||
<%= form.select :account_id, @import.family.accounts.visible.pluck(:name, :id), { label: "Account (optional)", include_blank: "Multi-account import", selected: @import.account_id } %>
|
||||
<% end %>
|
||||
|
||||
<%= form.text_area :raw_file_str,
|
||||
|
|
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 %>
|
||||
|
|
|
@ -1,62 +1,85 @@
|
|||
<%# locals: (form:, amount_method:, currency_method:, **options) %>
|
||||
|
||||
<% currency_value = if options[:currency_value_override].present?
|
||||
options[:currency_value_override]
|
||||
elsif form.object && form.object.respond_to?(currency_method)
|
||||
form.object.public_send(currency_method)
|
||||
end
|
||||
options[:currency_value_override]
|
||||
elsif form.object && form.object.respond_to?(currency_method)
|
||||
form.object.public_send(currency_method)
|
||||
end
|
||||
currency = Money::Currency.new(currency_value || options[:default_currency] || "USD") %>
|
||||
|
||||
<div class="form-field pr-0 <%= options[:container_class] %>" data-controller="money-field">
|
||||
<%= form.label options[:label] || t(".label"), class: "form-field__label" do %>
|
||||
<%= options[:label] || t(".label") %>
|
||||
<% if options[:required] %>
|
||||
<span class="text-red-500">*</span>
|
||||
<% end %>
|
||||
<div class="form-field <%= options[:container_class] %>" data-controller="money-field">
|
||||
<% if options[:label_tooltip] %>
|
||||
<div class="form-field__header">
|
||||
<%= form.label options[:label] || t(".label"), class: "form-field__label" do %>
|
||||
<%= options[:label] || t(".label") %>
|
||||
<% if options[:required] %>
|
||||
<span class="text-red-500 ml-0.5">*</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="form-field__actions">
|
||||
<div data-controller="tooltip">
|
||||
<%= icon "help-circle", size: "sm", color: "default", class: "cursor-help" %>
|
||||
<div role="tooltip" data-tooltip-target="tooltip" class="tooltip bg-gray-700 text-sm p-2 rounded w-64 text-white">
|
||||
<%= options[:label_tooltip] %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="flex items-center grow gap-1">
|
||||
<span class="text-subdued text-sm" data-money-field-target="symbol">
|
||||
<%= currency.symbol %>
|
||||
</span>
|
||||
|
||||
<%= form.number_field amount_method,
|
||||
class: "form-field__input",
|
||||
inline: true,
|
||||
placeholder: "100",
|
||||
value: if options[:value]
|
||||
sprintf("%.#{currency.default_precision}f", options[:value])
|
||||
elsif form.object && form.object.respond_to?(amount_method)
|
||||
val = form.object.public_send(amount_method)
|
||||
sprintf("%.#{currency.default_precision}f", val) if val.present?
|
||||
end,
|
||||
min: options[:min] || -99999999999999,
|
||||
max: options[:max] || 99999999999999,
|
||||
step: currency.step,
|
||||
disabled: options[:disabled],
|
||||
data: {
|
||||
"money-field-target": "amount",
|
||||
"auto-submit-form-target": ("auto" if options[:auto_submit])
|
||||
}.compact,
|
||||
required: options[:required] %>
|
||||
</div>
|
||||
|
||||
<% unless options[:hide_currency] %>
|
||||
<div>
|
||||
<%= form.select currency_method,
|
||||
Money::Currency.as_options.map(&:iso_code),
|
||||
{ inline: true, selected: currency.iso_code },
|
||||
{
|
||||
class: "w-fit pr-5 disabled:text-subdued form-field__input",
|
||||
disabled: options[:disable_currency],
|
||||
data: {
|
||||
"money-field-target": "currency",
|
||||
action: "change->money-field#handleCurrencyChange",
|
||||
"auto-submit-form-target": ("auto" if options[:auto_submit])
|
||||
}.compact
|
||||
} %>
|
||||
</div>
|
||||
<div class="form-field__body">
|
||||
<% unless options[:label_tooltip] %>
|
||||
<%= form.label options[:label] || t(".label"), class: "form-field__label" do %>
|
||||
<%= options[:label] || t(".label") %>
|
||||
<% if options[:required] %>
|
||||
<span class="text-red-500 ml-0.5">*</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="flex items-center grow gap-1">
|
||||
<span class="text-subdued text-sm" data-money-field-target="symbol">
|
||||
<%= currency.symbol %>
|
||||
</span>
|
||||
|
||||
<%= form.number_field amount_method,
|
||||
class: "form-field__input",
|
||||
inline: true,
|
||||
placeholder: "100",
|
||||
value: if options[:value]
|
||||
sprintf("%.#{currency.default_precision}f", options[:value])
|
||||
elsif form.object && form.object.respond_to?(amount_method)
|
||||
val = form.object.public_send(amount_method)
|
||||
sprintf("%.#{currency.default_precision}f", val) if val.present?
|
||||
end,
|
||||
min: options[:min] || -99999999999999,
|
||||
max: options[:max] || 99999999999999,
|
||||
step: currency.step,
|
||||
disabled: options[:disabled],
|
||||
data: {
|
||||
"money-field-target": "amount",
|
||||
"auto-submit-form-target": ("auto" if options[:auto_submit])
|
||||
}.compact,
|
||||
required: options[:required] %>
|
||||
</div>
|
||||
|
||||
<% unless options[:hide_currency] %>
|
||||
<div>
|
||||
<%= form.select currency_method,
|
||||
Money::Currency.as_options.map(&:iso_code),
|
||||
{ inline: true, selected: currency.iso_code },
|
||||
{
|
||||
class: "w-fit pr-5 disabled:text-subdued form-field__input",
|
||||
disabled: options[:disable_currency],
|
||||
data: {
|
||||
"money-field-target": "currency",
|
||||
action: "change->money-field#handleCurrencyChange",
|
||||
"auto-submit-form-target": ("auto" if options[:auto_submit])
|
||||
}.compact
|
||||
} %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<% end %>
|
||||
|
||||
<% if %w[deposit withdrawal].include?(type) %>
|
||||
<%= form.collection_select :transfer_account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".account_prompt"), label: t(".account") } %>
|
||||
<%= form.collection_select :transfer_account_id, Current.family.accounts.visible.alphabetically, :id, :name, { prompt: t(".account_prompt"), label: t(".account") } %>
|
||||
<% end %>
|
||||
|
||||
<% if %w[buy sell].include?(type) %>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<%# locals: (entry:) %>
|
||||
<%# locals: (entry:, error_message:) %>
|
||||
|
||||
<%= styled_form_with model: entry, url: valuations_path, class: "space-y-4" do |form| %>
|
||||
<%= form.hidden_field :account_id %>
|
||||
|
||||
<% if entry.errors.any? %>
|
||||
<%= render "shared/form_errors", model: entry %>
|
||||
<% if error_message.present? %>
|
||||
<%= render AlertComponent.new(message: error_message, variant: :error) %>
|
||||
<% end %>
|
||||
|
||||
<div class="space-y-3">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<%= tag.header class: "mb-4 space-y-1", id: dom_id(entry, :header) do %>
|
||||
<span class="text-secondary text-sm">
|
||||
<%= t(".balance") %>
|
||||
<%= entry.name %>
|
||||
</span>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
|
|
|
@ -25,15 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 justify-self-end font-medium text-sm">
|
||||
<% if balance_trend&.trend %>
|
||||
<%= tag.span format_money(balance_trend.trend.value), style: "color: #{balance_trend.trend.color}" %>
|
||||
<% else %>
|
||||
<%= tag.span "--", class: "text-gray-400" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 justify-self-end">
|
||||
<div class="col-span-4 justify-self-end">
|
||||
<%= tag.p format_money(entry.amount_money), class: "font-medium text-sm text-primary" %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<%= render DialogComponent.new do |dialog| %>
|
||||
<% dialog.with_header(title: t(".title")) %>
|
||||
<% dialog.with_body do %>
|
||||
<%= render "form", entry: @entry %>
|
||||
<%= render "form", entry: @entry, error_message: @error_message %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -6,17 +6,18 @@
|
|||
<% end %>
|
||||
|
||||
<% dialog.with_body do %>
|
||||
<% if @error_message.present? %>
|
||||
<div class="mb-4">
|
||||
<%= render AlertComponent.new(message: @error_message, variant: :error) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% dialog.with_section(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: entry,
|
||||
url: entry_path(entry),
|
||||
class: "space-y-2",
|
||||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.text_field :name,
|
||||
label: t(".name_label"),
|
||||
placeholder: t(".name_placeholder"),
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
|
||||
<%= f.date_field :date,
|
||||
label: t(".date_label"),
|
||||
max: Date.current,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue