mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-08 23:15:24 +02:00
Improve account transaction, trade, and valuation editing and sync experience (#1506)
* Consolidate entry controller logic * Transaction builder * Update trades controller to use new params * Load account charts in turbo frames, fix PG overflow * Consolidate tests * Tests passing * Remove unused code * Add client side trade form validations
This commit is contained in:
parent
76f2714006
commit
c3248cd796
97 changed files with 1103 additions and 1159 deletions
37
app/views/account/transactions/_form.html.erb
Normal file
37
app/views/account/transactions/_form.html.erb
Normal file
|
@ -0,0 +1,37 @@
|
|||
<%= styled_form_with model: @entry, url: account_transactions_path, class: "space-y-4" do |f| %>
|
||||
|
||||
<% if entry.errors.any? %>
|
||||
<%= render "shared/form_errors", model: entry %>
|
||||
<% end %>
|
||||
|
||||
<section>
|
||||
<fieldset class="bg-gray-50 rounded-lg p-1 grid grid-flow-col justify-stretch gap-x-2">
|
||||
<%= radio_tab_tag form: f, name: :nature, value: :outflow, label: t(".expense"), icon: "minus-circle", checked: params[:nature] == "outflow" || params[:nature].nil? %>
|
||||
<%= radio_tab_tag form: f, name: :nature, value: :inflow, label: t(".income"), icon: "plus-circle", checked: params[:nature] == "inflow" %>
|
||||
<%= link_to new_account_transfer_path, data: { turbo_frame: :modal }, class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-gray-400 group-has-[:checked]:bg-white group-has-[:checked]:text-gray-800 group-has-[:checked]:shadow-sm" do %>
|
||||
<%= lucide_icon "arrow-right-left", class: "w-5 h-5" %>
|
||||
<%= tag.span t(".transfer") %>
|
||||
<% end %>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
<section class="space-y-2 overflow-hidden">
|
||||
<%= f.text_field :name, label: t(".description"), placeholder: t(".description_placeholder"), required: true %>
|
||||
|
||||
<% if @entry.account_id %>
|
||||
<%= f.hidden_field :account_id %>
|
||||
<% else %>
|
||||
<%= f.collection_select :account_id, Current.family.accounts.manual.alphabetically, :id, :name, { prompt: t(".account_prompt"), label: t(".account") }, required: true, class: "form-field__input text-ellipsis" %>
|
||||
<% end %>
|
||||
|
||||
<%= f.money_field :amount, label: t(".amount"), required: true %>
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<%= ef.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: t(".category_prompt"), label: t(".category") } %>
|
||||
<% end %>
|
||||
<%= f.date_field :date, label: t(".date"), required: true, min: Account::Entry.min_supported_date, max: Date.today, value: Date.today %>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<%= f.submit t(".submit") %>
|
||||
</section>
|
||||
<% end %>
|
23
app/views/account/transactions/_header.html.erb
Normal file
23
app/views/account/transactions/_header.html.erb
Normal file
|
@ -0,0 +1,23 @@
|
|||
<%# locals: (entry:) %>
|
||||
|
||||
<%= tag.header class: "mb-4 space-y-1", id: dom_id(entry, :header) do %>
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="font-medium">
|
||||
<span class="text-2xl">
|
||||
<%= format_money -entry.amount_money %>
|
||||
</span>
|
||||
|
||||
<span class="text-lg text-gray-500">
|
||||
<%= entry.currency %>
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<% if entry.marked_as_transfer? %>
|
||||
<%= lucide_icon "arrow-left-right", class: "text-gray-500 mt-1 w-5 h-5" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<span class="text-sm text-gray-500">
|
||||
<%= I18n.l(entry.date, format: :long) %>
|
||||
</span>
|
||||
<% end %>
|
|
@ -8,7 +8,7 @@
|
|||
<div class="flex items-center gap-1 text-gray-500">
|
||||
<%= turbo_frame_tag "bulk_transaction_edit_drawer" %>
|
||||
|
||||
<%= form_with url: mark_transfers_transactions_path,
|
||||
<%= form_with url: mark_transfers_account_transactions_path,
|
||||
scope: "bulk_update",
|
||||
data: {
|
||||
turbo_frame: "_top",
|
||||
|
@ -28,14 +28,14 @@
|
|||
</button>
|
||||
<% end %>
|
||||
|
||||
<%= link_to bulk_edit_transactions_path,
|
||||
<%= link_to bulk_edit_account_transactions_path,
|
||||
class: "p-1.5 group hover:bg-gray-700 flex items-center justify-center rounded-md",
|
||||
title: "Edit",
|
||||
data: { turbo_frame: "bulk_transaction_edit_drawer" } do %>
|
||||
<%= lucide_icon "pencil-line", class: "w-5 group-hover:text-white" %>
|
||||
<% end %>
|
||||
|
||||
<%= form_with url: bulk_delete_transactions_path, data: { turbo_confirm: true, turbo_frame: "_top" } do %>
|
||||
<%= form_with url: bulk_delete_account_transactions_path, data: { turbo_confirm: true, turbo_frame: "_top" } do %>
|
||||
<button type="button" data-bulk-select-scope-param="bulk_delete" data-action="bulk-select#submitBulkRequest" class="p-1.5 group hover:bg-gray-700 flex items-center justify-center rounded-md" title="Delete">
|
||||
<%= lucide_icon "trash-2", class: "w-5 group-hover:text-white" %>
|
||||
</button>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%# locals: (entry:, selectable: true, show_balance: false, origin: nil) %>
|
||||
<%# locals: (entry:, selectable: true, show_balance: false) %>
|
||||
<% transaction, account = entry.account_transaction, entry.account %>
|
||||
|
||||
<div class="grid grid-cols-12 items-center <%= entry.excluded ? "text-gray-400 bg-gray-25" : "text-gray-900" %> text-sm font-medium p-4">
|
||||
|
@ -20,7 +20,7 @@
|
|||
<%= content_tag :p, transaction.name %>
|
||||
<% else %>
|
||||
<%= link_to transaction.name,
|
||||
entry.transfer.present? ? account_transfer_path(entry.transfer, origin:) : account_entry_path(account, entry, origin:),
|
||||
entry.transfer.present? ? account_transfer_path(entry.transfer) : account_entry_path(entry),
|
||||
data: { turbo_frame: "drawer", turbo_prefetch: false },
|
||||
class: "hover:underline hover:text-gray-800" %>
|
||||
<% end %>
|
||||
|
@ -43,7 +43,7 @@
|
|||
</div>
|
||||
<% else %>
|
||||
<div class="flex items-center gap-1 col-span-2">
|
||||
<%= render "categories/menu", transaction: transaction, origin: origin %>
|
||||
<%= render "categories/menu", transaction: transaction %>
|
||||
</div>
|
||||
|
||||
<% unless show_balance %>
|
||||
|
|
62
app/views/account/transactions/bulk_edit.html.erb
Normal file
62
app/views/account/transactions/bulk_edit.html.erb
Normal file
|
@ -0,0 +1,62 @@
|
|||
<%= turbo_frame_tag "bulk_transaction_edit_drawer" do %>
|
||||
<dialog data-controller="modal"
|
||||
data-action="click->modal#clickOutside"
|
||||
class="bg-white border border-alpha-black-25 rounded-2xl max-h-[calc(100vh-32px)] max-w-[480px] w-full shadow-xs h-full mt-4 mr-4">
|
||||
<%= styled_form_with url: bulk_update_account_transactions_path, scope: "bulk_update", class: "h-full", data: { turbo_frame: "_top" } do |form| %>
|
||||
<div class="flex h-full flex-col justify-between p-4">
|
||||
<div>
|
||||
<div class="flex h-9 items-center justify-end">
|
||||
<div data-action="click->modal#close" class="cursor-pointer">
|
||||
<%= lucide_icon("x", class: "w-5 h-5 shrink-0") %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col overflow-scroll">
|
||||
<div>
|
||||
<header class="mb-4 space-y-1">
|
||||
<h3 class="text-2xl font-medium" data-bulk-select-target="bulkEditDrawerTitle">
|
||||
Edit transactions
|
||||
</h3>
|
||||
</header>
|
||||
|
||||
<div class="space-y-2">
|
||||
<details class="group space-y-2" open>
|
||||
<summary class="flex list-none items-center justify-between rounded-xl px-3 py-2 text-xs font-medium uppercase text-gray-500 bg-gray-25 focus-visible:outline-none">
|
||||
<h4><%= t(".overview") %></h4>
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
|
||||
<div class="pb-6 space-y-2">
|
||||
<%= form.date_field :date, label: t(".date_label"), max: Date.current %>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="group space-y-2" open>
|
||||
<summary class="flex list-none items-center justify-between rounded-xl px-3 py-2 text-xs font-medium uppercase text-gray-500 bg-gray-25 focus-visible:outline-none">
|
||||
<h4><%= t(".details") %></h4>
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
|
||||
<div class="space-y-2">
|
||||
<%= form.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: t(".category_placeholder"), label: t(".category_label"), class: "text-gray-400" } %>
|
||||
<%= form.collection_select :merchant_id, Current.family.merchants.alphabetically, :id, :name, { prompt: t(".merchant_placeholder"), label: t(".merchant_label"), class: "text-gray-400" } %>
|
||||
<%= form.text_area :notes, label: t(".note_label"), placeholder: t(".note_placeholder"), rows: 5 %>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end items-center gap-2">
|
||||
<%= link_to t(".cancel"), transactions_path, class: "text-sm font-medium text-gray-900 px-3 py-2" %>
|
||||
|
||||
<%= tag.button t(".save"),
|
||||
type: "button",
|
||||
data: { "bulk-select-scope-param": "bulk_update", action: "bulk-select#submitBulkRequest" },
|
||||
class: "px-3 py-2 bg-gray-900 text-white text-sm font-medium rounded-lg" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</dialog>
|
||||
<% end %>
|
|
@ -2,7 +2,7 @@
|
|||
<div class="bg-white space-y-4 p-5 border border-alpha-black-25 rounded-xl shadow-xs">
|
||||
<div class="flex justify-between items-center">
|
||||
<h3 class="font-medium text-lg"><%= t(".transactions") %></h3>
|
||||
<%= link_to new_transaction_path(account_id: @account),
|
||||
<%= link_to new_account_transaction_path(account_id: @account),
|
||||
class: "flex gap-1 font-medium items-center bg-gray-50 text-gray-900 p-2 rounded-lg",
|
||||
data: { turbo_frame: :modal } do %>
|
||||
<%= lucide_icon("plus", class: "w-5 h-5 text-gray-900") %>
|
||||
|
|
3
app/views/account/transactions/new.html.erb
Normal file
3
app/views/account/transactions/new.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<%= modal_form_wrapper title: t(".new_transaction") do %>
|
||||
<%= render "form", entry: @entry %>
|
||||
<% end %>
|
|
@ -1,39 +1,14 @@
|
|||
<% entry, transaction, account = @entry, @entry.account_transaction, @entry.account %>
|
||||
|
||||
<% origin = params[:origin] %>
|
||||
|
||||
<%= drawer do %>
|
||||
<header class="mb-4 space-y-1">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="font-medium">
|
||||
<span class="text-2xl">
|
||||
<%= format_money -entry.amount_money %>
|
||||
</span>
|
||||
|
||||
<span class="text-lg text-gray-500">
|
||||
<%= entry.currency %>
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<% if entry.marked_as_transfer? %>
|
||||
<%= lucide_icon "arrow-left-right", class: "text-gray-500 mt-1 w-5 h-5" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<span class="text-sm text-gray-500">
|
||||
<%= I18n.l(entry.date, format: :long) %>
|
||||
</span>
|
||||
</header>
|
||||
<%= drawer(reload_on_close: true) do %>
|
||||
<%= render "account/transactions/header", entry: @entry %>
|
||||
|
||||
<div class="space-y-2">
|
||||
<!-- Overview Section -->
|
||||
<%= disclosure t(".overview") do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: [account, entry],
|
||||
url: account_transaction_path(account, entry),
|
||||
<%= styled_form_with model: @entry,
|
||||
url: account_transaction_path(@entry),
|
||||
class: "space-y-2",
|
||||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.hidden_field :origin, value: origin %>
|
||||
<%= f.text_field :name,
|
||||
label: t(".name_label"),
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
|
@ -43,25 +18,25 @@
|
|||
max: Date.current,
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
|
||||
<% unless entry.marked_as_transfer? %>
|
||||
<% unless @entry.marked_as_transfer? %>
|
||||
<div class="flex items-center gap-2">
|
||||
<%= f.select :nature,
|
||||
[["Expense", "expense"], ["Income", "income"]],
|
||||
{ container_class: "w-1/3", label: t(".nature"), selected: entry.amount.negative? ? "income" : "expense" },
|
||||
[["Expense", "outflow"], ["Income", "inflow"]],
|
||||
{ container_class: "w-1/3", label: t(".nature"), selected: @entry.amount.negative? ? "inflow" : "outflow" },
|
||||
{ data: { "auto-submit-form-target": "auto" } } %>
|
||||
|
||||
<%= f.money_field :amount, label: t(".amount"),
|
||||
container_class: "w-2/3",
|
||||
auto_submit: true,
|
||||
min: 0,
|
||||
value: entry.amount.abs %>
|
||||
value: @entry.amount.abs %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= f.select :account,
|
||||
options_for_select(
|
||||
Current.family.accounts.alphabetically.pluck(:name, :id),
|
||||
entry.account_id
|
||||
@entry.account_id
|
||||
),
|
||||
{ label: t(".account_label") },
|
||||
{ disabled: true } %>
|
||||
|
@ -72,55 +47,45 @@
|
|||
<!-- Details Section -->
|
||||
<%= disclosure t(".details") do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: [account, entry],
|
||||
url: account_transaction_path(account, entry),
|
||||
<%= styled_form_with model: @entry,
|
||||
url: account_transaction_path(@entry),
|
||||
class: "space-y-2",
|
||||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.hidden_field :origin, value: origin %>
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<% unless entry.marked_as_transfer? %>
|
||||
<% unless @entry.marked_as_transfer? %>
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<%= ef.collection_select :category_id,
|
||||
Current.family.categories.alphabetically,
|
||||
Current.family.categories.alphabetically,
|
||||
:id, :name,
|
||||
{ prompt: t(".category_placeholder"),
|
||||
label: t(".category_label"),
|
||||
class: "text-gray-400" },
|
||||
{ label: t(".category_label"),
|
||||
class: "text-gray-400", include_blank: t(".uncategorized") },
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
|
||||
<%= ef.collection_select :merchant_id,
|
||||
Current.family.merchants.alphabetically,
|
||||
Current.family.merchants.alphabetically,
|
||||
:id, :name,
|
||||
{ prompt: t(".merchant_placeholder"),
|
||||
{ include_blank: t(".none"),
|
||||
label: t(".merchant_label"),
|
||||
class: "text-gray-400" },
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
<% end %>
|
||||
|
||||
<%= ef.select :tag_ids,
|
||||
options_for_select(
|
||||
<%= ef.select :tag_ids,
|
||||
Current.family.tags.alphabetically.pluck(:name, :id),
|
||||
transaction.tag_ids
|
||||
),
|
||||
{
|
||||
multiple: true,
|
||||
label: t(".tags_label"),
|
||||
container_class: "h-40"
|
||||
},
|
||||
{
|
||||
include_blank: t(".none"),
|
||||
multiple: true,
|
||||
label: t(".tags_label"),
|
||||
container_class: "h-40"
|
||||
},
|
||||
{ "data-auto-submit-form-target": "auto" } %>
|
||||
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= styled_form_with model: [account, entry],
|
||||
url: account_transaction_path(account, entry),
|
||||
class: "space-y-2",
|
||||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.hidden_field :origin, value: origin %>
|
||||
<%= f.text_area :notes,
|
||||
<%= f.text_area :notes,
|
||||
label: t(".note_label"),
|
||||
placeholder: t(".note_placeholder"),
|
||||
rows: 5,
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
@ -129,11 +94,10 @@
|
|||
<%= disclosure t(".settings") do %>
|
||||
<div class="pb-4">
|
||||
<!-- Exclude Transaction Form -->
|
||||
<%= styled_form_with model: [account, entry],
|
||||
url: account_transaction_path(account, entry),
|
||||
<%= styled_form_with model: @entry,
|
||||
url: account_transaction_path(@entry),
|
||||
class: "p-3",
|
||||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.hidden_field :origin, value: origin %>
|
||||
<div class="flex cursor-pointer items-center gap-2 justify-between">
|
||||
<div class="text-sm space-y-1">
|
||||
<h4 class="text-gray-900"><%= t(".exclude_title") %></h4>
|
||||
|
@ -158,7 +122,7 @@
|
|||
</div>
|
||||
|
||||
<%= button_to t(".delete"),
|
||||
account_entry_path(account, entry),
|
||||
account_entry_path(@entry),
|
||||
method: :delete,
|
||||
class: "rounded-lg px-3 py-2 text-red-500 text-sm
|
||||
font-medium border border-alpha-black-200",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue