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

Account Activity View + Account Forms (#1406)
Some checks are pending
Publish Docker image / ci (push) Waiting to run
Publish Docker image / Build docker image (push) Blocked by required conditions

* Remove balance mode, sketch out refactor

* Activity view checkpoint

* Entry partials, checkpoint

* Finish txn partial

* Give entries context when editing for different turbo responses

* Calculate change of balance for each entry

* Account tabs consolidation

* Translations, linting, brakeman updates

* Account actions concern

* Finalize forms, get account system tests passing

* Get tests passing

* Lint, rubocop, schema updates

* Improve routing and stream responses

* Fix broken routes

* Add import option for adding accounts

* Fix system test

* Fix test specificity

* Fix sparklines

* Improve account redirects
This commit is contained in:
Zach Gollwitzer 2024-11-04 20:27:31 -05:00 committed by GitHub
parent 12e4f1067d
commit 65db49273c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
216 changed files with 2043 additions and 1620 deletions

View file

@ -1,5 +1,5 @@
<%# locals: (entry:, **opts) %>
<%# locals: (entry:, selectable: true, show_balance: false, origin: nil) %>
<%= turbo_frame_tag dom_id(entry) do %>
<%= render partial: entry.entryable.to_partial_path, locals: { entry: entry, **opts } %>
<%= render partial: entry.entryable.to_partial_path, locals: { entry:, selectable:, show_balance:, origin: } %>
<% end %>

View file

@ -1,4 +1,4 @@
<%# locals: (date:, entries:, content:, selectable:) %>
<%# locals: (date:, entries:, content:, selectable:, totals: false) %>
<div id="entry-group-<%= date %>" class="bg-gray-25 rounded-xl p-1 w-full" data-bulk-select-target="group">
<div class="py-2 px-4 flex items-center justify-between font-medium text-xs text-gray-500">
<div class="flex pl-0.5 items-center gap-4">
@ -16,7 +16,9 @@
</p>
</div>
<%= totals_by_currency(collection: entries, money_method: :amount_money, negate: true) %>
<% if totals %>
<%= totals_by_currency(collection: entries, money_method: :amount_money, negate: true) %>
<% end %>
</div>
<div class="bg-white shadow-xs rounded-md border border-alpha-black-25 divide-y divide-alpha-black-50">
<%= content %>

View file

@ -0,0 +1,15 @@
<div class="fixed bottom-6 z-10 flex items-center justify-between rounded-xl bg-gray-900 px-4 text-sm text-white w-[420px] py-1.5">
<div class="flex items-center gap-2">
<%= check_box_tag "entry_selection", 1, true, class: "maybe-checkbox maybe-checkbox--dark", data: { action: "bulk-select#deselectAll" } %>
<p data-bulk-select-target="selectionBarText"></p>
</div>
<div class="flex items-center gap-1 text-gray-500">
<%= form_with url: bulk_delete_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>
<% end %>
</div>
</div>

View file

@ -0,0 +1,88 @@
<%= turbo_frame_tag dom_id(@account, "entries") do %>
<div class="bg-white p-5 border border-alpha-black-25 rounded-xl shadow-xs">
<div class="flex items-center justify-between mb-4">
<%= tag.h2 t(".title"), class: "font-medium text-lg" %>
<div data-controller="menu" data-testid="activity-menu">
<button class="btn btn--secondary flex items-center gap-2" data-menu-target="button">
<%= lucide_icon("plus", class: "w-4 h-4") %>
<%= tag.span t(".new") %>
</button>
<div data-menu-target="content" class="z-10 hidden bg-white rounded-lg border border-alpha-black-25 shadow-xs p-1">
<%= link_to new_account_valuation_path(@account), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
<%= lucide_icon("circle-dollar-sign", class: "text-gray-500 w-5 h-5") %>
<%= tag.span t(".new_balance"), class: "text-sm" %>
<% end %>
<%= link_to @account.investment? ? new_account_trade_path(@account) : new_transaction_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
<%= lucide_icon("credit-card", class: "text-gray-500 w-5 h-5") %>
<%= tag.span t(".new_transaction"), class: "text-sm" %>
<% end %>
</div>
</div>
</div>
<% if @entries.empty? %>
<p class="text-gray-500 text-sm p-4"><%= t(".no_entries") %></p>
<% else %>
<div>
<%= form_with url: account_entries_path(@account),
id: "entries-search",
scope: :q,
method: :get,
data: { controller: "auto-submit-form" } do |form| %>
<div class="flex gap-2 mb-4">
<div class="grow">
<div class="flex items-center px-3 py-2 gap-2 border border-gray-200 rounded-lg focus-within:ring-gray-100 focus-within:border-gray-900">
<%= lucide_icon("search", class: "w-5 h-5 text-gray-500") %>
<%= form.search_field :search,
placeholder: "Search entries by name",
value: @q[:search],
class: "form-field__input placeholder:text-sm placeholder:text-gray-500",
"data-auto-submit-form-target": "auto" %>
</div>
</div>
</div>
<% end %>
</div>
<%= tag.div id: dom_id(@account, "entries_bulk_select"),
data: {
controller: "bulk-select",
bulk_select_singular_label_value: t(".entry"),
bulk_select_plural_label_value: t(".entries")
} do %>
<div id="entry-selection-bar" data-bulk-select-target="selectionBar" class="flex justify-center hidden">
<%= render "account/entries/selection_bar" %>
</div>
<div class="grid bg-gray-25 rounded-xl grid-cols-12 items-center uppercase text-xs font-medium text-gray-500 px-5 py-3 mb-4">
<div class="pl-0.5 col-span-8 flex items-center gap-4">
<%= check_box_tag "selection_entry",
class: "maybe-checkbox maybe-checkbox--light",
data: { action: "bulk-select#togglePageSelection" } %>
<p><%= t(".date") %></p>
</div>
<%= tag.p t(".amount"), class: "col-span-2 justify-self-end" %>
<%= tag.p t(".balance"), class: "col-span-2 justify-self-end" %>
</div>
<div>
<div class="rounded-tl-lg rounded-tr-lg bg-white border-alpha-black-25 shadow-xs">
<div class="space-y-4">
<%= entries_by_date(@entries) do |entries| %>
<%= render entries, show_balance: true, origin: "account" %>
<% end %>
</div>
</div>
<div class="p-4 bg-white rounded-bl-lg rounded-br-lg">
<%= render "pagination", pagy: @pagy %>
</div>
</div>
<% end %>
<% end %>
</div>
<% end %>

View file

@ -8,4 +8,4 @@
<%= "#{security.symbol} (#{security.exchange_acronym})" %>
</span>
</div>
</div>
</div>

View file

@ -1,4 +1,4 @@
<%# locals: (entry:, selectable: true, **opts) %>
<%# locals: (entry:, selectable: true, show_balance: false, origin: nil) %>
<% trade, account = entry.account_trade, entry.account %>
@ -13,14 +13,14 @@
<div class="max-w-full">
<%= tag.div class: ["flex items-center gap-2"] do %>
<div class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gray-600/5 text-gray-600">
<%= entry_name(entry).first.upcase %>
<%= trade.name.first.upcase %>
</div>
<div class="truncate text-gray-900">
<% if entry.new_record? %>
<%= content_tag :p, entry_name(entry) %>
<%= content_tag :p, trade.name %>
<% else %>
<%= link_to entry_name(entry),
<%= link_to trade.name,
account_entry_path(account, entry),
data: { turbo_frame: "drawer", turbo_prefetch: false },
class: "hover:underline hover:text-gray-800" %>

View file

@ -1,2 +1,2 @@
<%= async_combobox_options @securities,
render_in: { partial: "account/trades/security" } %>
render_in: { partial: "account/trades/security" } %>

View file

@ -1,9 +1,8 @@
<%# locals: (entry:, selectable: true, editable: true, short: false, show_tags: false, **opts) %>
<%# locals: (entry:, selectable: true, show_balance: false, origin: nil) %>
<% transaction, account = entry.account_transaction, entry.account %>
<div class="grid grid-cols-12 items-center text-gray-900 text-sm font-medium p-4">
<% name_col_span = unconfirmed_transfer?(entry) ? "col-span-10" : short ? "col-span-6" : "col-span-4" %>
<div class="pr-10 flex items-center gap-4 <%= name_col_span %>">
<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">
<div class="pr-10 flex items-center gap-4 col-span-6">
<% if selectable %>
<%= check_box_tag dom_id(entry, "selection"),
class: "maybe-checkbox maybe-checkbox--light",
@ -13,15 +12,15 @@
<div class="max-w-full">
<%= content_tag :div, class: ["flex items-center gap-2"] do %>
<div class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gray-600/5 text-gray-600">
<%= entry_name(entry).first.upcase %>
<%= transaction.name.first.upcase %>
</div>
<div class="truncate text-gray-900">
<% if entry.new_record? || !editable %>
<%= content_tag :p, entry.name %>
<div class="truncate">
<% if entry.new_record? %>
<%= content_tag :p, transaction.name %>
<% else %>
<%= link_to entry_name(entry),
account_entry_path(account, entry),
<%= link_to transaction.name,
entry.transfer.present? ? account_transfer_path(entry.transfer, origin:) : account_entry_path(account, entry, origin:),
data: { turbo_frame: "drawer", turbo_prefetch: false },
class: "hover:underline hover:text-gray-800" %>
<% end %>
@ -30,46 +29,25 @@
</div>
<% if unconfirmed_transfer?(entry) %>
<% if editable %>
<%= form_with url: unmark_transfers_transactions_path, class: "flex items-center", data: {
turbo_confirm: {
title: t(".remove_transfer"),
body: t(".remove_transfer_body"),
accept: t(".remove_transfer_confirm"),
},
turbo_frame: "_top"
} do |f| %>
<%= f.hidden_field "bulk_update[entry_ids][]", value: entry.id %>
<%= f.button class: "flex items-center justify-center group", title: "Remove transfer" do %>
<%= lucide_icon "arrow-left-right", class: "group-hover:hidden text-gray-500 w-4 h-4" %>
<%= lucide_icon "unlink", class: "hidden group-hover:inline-block text-gray-900 w-4 h-4" %>
<% end %>
<% end %>
<% else %>
<%= lucide_icon "arrow-left-right", class: "text-gray-500 w-4 h-4" %>
<% end %>
<%= render "account/transfers/transfer_toggle", entry: entry %>
<% end %>
</div>
<% unless entry.marked_as_transfer? %>
<% unless short %>
<div class="flex items-center gap-1 <%= show_tags ? "col-span-6" : "col-span-3" %>">
<% if editable %>
<%= render "categories/menu", transaction: transaction %>
<% else %>
<%= render "categories/badge", category: transaction.category %>
<% end %>
<% if show_tags %>
<% transaction.tags.each do |tag| %>
<%= render partial: "tags/badge", locals: { tag: tag } %>
<% end %>
<% end %>
</div>
<% if entry.transfer.present? %>
<% unless show_balance %>
<div class="col-span-2"></div>
<% end %>
<% unless show_tags %>
<%= tag.div class: short ? "col-span-4" : "col-span-3" do %>
<div class="col-span-2">
<%= render "account/transfers/account_logos", transfer: entry.transfer, outflow: entry.outflow? %>
</div>
<% else %>
<div class="flex items-center gap-1 col-span-2">
<%= render "categories/menu", transaction: transaction, origin: origin %>
</div>
<% unless show_balance %>
<%= tag.div class: "col-span-2" do %>
<% if entry.new_record? %>
<%= tag.p account.name %>
<% else %>
@ -87,4 +65,10 @@
format_money(-entry.amount_money),
class: ["text-green-600": entry.inflow?] %>
</div>
<% if show_balance %>
<div class="col-span-2 justify-self-end">
<%= tag.p format_money(entry.trend.current), class: "font-medium text-sm text-gray-900" %>
</div>
<% end %>
</div>

View file

@ -1,5 +1,7 @@
<% 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">
@ -31,6 +33,7 @@
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_field :name,
label: t(".name_label"),
"data-auto-submit-form-target": "auto" %>
@ -73,6 +76,7 @@
url: account_transaction_path(account, 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? %>
<%= ef.collection_select :category_id,
@ -110,6 +114,7 @@
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,
label: t(".note_label"),
placeholder: t(".note_placeholder"),
@ -128,6 +133,7 @@
url: account_transaction_path(account, 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>
@ -138,28 +144,26 @@
<%= f.check_box :excluded,
class: "sr-only peer",
"data-auto-submit-form-target": "auto" %>
<label for="account_entry_entryable_attributes_excluded"
<label for="account_entry_excluded"
class="maybe-switch"></label>
</div>
</div>
<% end %>
<!-- Delete Transaction Form -->
<% unless entry.marked_as_transfer? %>
<div class="flex items-center justify-between gap-2 p-3">
<div class="text-sm space-y-1">
<h4 class="text-gray-900"><%= t(".delete_title") %></h4>
<p class="text-gray-500"><%= t(".delete_subtitle") %></p>
</div>
<div class="flex items-center justify-between gap-2 p-3">
<div class="text-sm space-y-1">
<h4 class="text-gray-900"><%= t(".delete_title") %></h4>
<p class="text-gray-500"><%= t(".delete_subtitle") %></p>
</div>
<%= button_to t(".delete"),
<%= button_to t(".delete"),
account_entry_path(account, entry),
method: :delete,
class: "rounded-lg px-3 py-2 text-red-500 text-sm
font-medium border border-alpha-black-200",
data: { turbo_confirm: true, turbo_frame: "_top" } %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>

View file

@ -0,0 +1,25 @@
<%# locals: (transfer:, outflow: false) %>
<div class="flex items-center gap-2">
<% if outflow %>
<%= link_to transfer.from_account, data: { turbo_frame: :_top }, class: "hover:opacity-90" do %>
<%= circle_logo(transfer.from_name[0].upcase, size: "sm") %>
<% end %>
<%= lucide_icon "arrow-right", class: "text-gray-500 w-4 h-4" %>
<%= link_to transfer.to_account, data: { turbo_frame: :_top }, class: "hover:opacity-90" do %>
<%= circle_logo(transfer.to_name[0].upcase, size: "sm") %>
<% end %>
<% else %>
<%= link_to transfer.to_account, data: { turbo_frame: :_top }, class: "hover:opacity-90" do %>
<%= circle_logo(transfer.to_name[0].upcase, size: "sm") %>
<% end %>
<%= lucide_icon "arrow-left", class: "text-gray-500 w-4 h-4" %>
<%= link_to transfer.from_account, data: { turbo_frame: :_top }, class: "hover:opacity-90" do %>
<%= circle_logo(transfer.from_name[0].upcase, size: "sm") %>
<% end %>
<% end %>
</div>

View file

@ -1,49 +0,0 @@
<%# locals: (transfer:, selectable: true, editable: true, short: false, **opts) %>
<%= turbo_frame_tag dom_id(transfer) do %>
<div class="grid grid-cols-12 items-center text-gray-900 text-sm font-medium p-4">
<div class="col-span-7 flex items-center">
<% if selectable %>
<%= check_box_tag dom_id(transfer, "selection"),
disabled: true,
class: "mr-3 cursor-not-allowed maybe-checkbox maybe-checkbox--light" %>
<% end %>
<%= tag.div class: short ? "max-w-[250px]" : "max-w-[325px]" do %>
<div class="flex items-center gap-2 <%= selectable ? "" : "pl-8" %>">
<%= circle_logo(transfer.from_name[0].upcase) %>
<%= tag.p transfer.name, class: "truncate text-gray-900" %>
</div>
<% end %>
<%= button_to account_transfer_path(transfer),
method: :delete,
class: "ml-2 flex items-center group/transfer hover:bg-gray-50 rounded-md p-1",
data: {
turbo_frame: "_top",
turbo_confirm: {
title: t(".remove_title"),
body: t(".remove_body"),
confirm: t(".remove_confirm")
}
} do %>
<%= lucide_icon "link-2", class: "group-hover/transfer:hidden w-4 h-4 text-gray-500" %>
<%= lucide_icon "unlink", class: "group-hover/transfer:inline-block hidden w-4 h-4 text-gray-500" %>
<% end %>
</div>
<% unless short %>
<div class="col-span-3 flex items-center gap-2">
<%= circle_logo(transfer.from_name[0].upcase, size: "sm") %>
<span class="text-gray-500 font-medium">&rarr;</span>
<%= circle_logo(transfer.to_name[0].upcase, size: "sm") %>
</div>
<% end %>
<div class="ml-auto <%= short ? "col-span-5" : "col-span-2" %>">
<%= tag.p format_money(transfer.amount_money), class: "font-medium" %>
</div>
</div>
<% end %>

View file

@ -0,0 +1,16 @@
<%# locals: (entry:) %>
<%= form_with url: unmark_transfers_transactions_path, class: "flex items-center", data: {
turbo_confirm: {
title: t(".remove_transfer"),
body: t(".remove_transfer_body"),
accept: t(".remove_transfer_confirm"),
},
turbo_frame: "_top"
} do |f| %>
<%= f.hidden_field "bulk_update[entry_ids][]", value: entry.id %>
<%= f.button class: "flex items-center justify-center group", title: "Remove transfer" do %>
<%= lucide_icon "arrow-left-right", class: "group-hover:hidden text-gray-500 w-4 h-4" %>
<%= lucide_icon "unlink", class: "hidden group-hover:inline-block text-gray-900 w-4 h-4" %>
<% end %>
<% end %>

View file

@ -0,0 +1,121 @@
<%= 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 @transfer.amount_money %>
</span>
<span class="text-lg text-gray-500">
<%= @transfer.amount_money.currency.iso_code %>
</span>
</h3>
<%= lucide_icon "arrow-left-right", class: "text-gray-500 mt-1 w-5 h-5" %>
</div>
<span class="text-sm text-gray-500">
<%= @transfer.name %>
</span>
</header>
<div class="space-y-2">
<!-- Overview Section -->
<%= disclosure t(".overview") do %>
<div class="pb-4 px-3 pt-2 text-sm space-y-3 text-gray-900">
<div class="space-y-3">
<dl class="flex items-center gap-2 justify-between">
<dt class="text-gray-500">To</dt>
<dd class="flex items-center gap-2 font-medium">
<%= render "accounts/logo", account: @transfer.inflow_transaction.account, size: "sm" %>
<%= @transfer.to_name %>
</dd>
</dl>
<dl class="flex items-center gap-2 justify-between">
<dt class="text-gray-500">Date</dt>
<dd class="font-medium"><%= l(@transfer.date, format: :long) %></dd>
</dl>
<dl class="flex items-center gap-2 justify-between">
<dt class="text-gray-500">Amount</dt>
<dd class="font-medium text-red-500"><%= format_money -@transfer.amount_money %></dd>
</dl>
</div>
<div class="bg-alpha-black-100 h-px my-2"></div>
<div class="space-y-3">
<dl class="flex items-center gap-2 justify-between">
<dt class="text-gray-500">From</dt>
<dd class="flex items-center gap-2 font-medium">
<%= render "accounts/logo", account: @transfer.outflow_transaction.account, size: "sm" %>
<%= @transfer.from_name %>
</dd>
</dl>
<dl class="flex items-center gap-2 justify-between">
<dt class="text-gray-500">Date</dt>
<dd class="font-medium"><%= l(@transfer.date, format: :long) %></dd>
</dl>
<dl class="flex items-center gap-2 justify-between">
<dt class="text-gray-500">Amount</dt>
<dd class="font-medium text-green-500">+<%= format_money @transfer.amount_money %></dd>
</dl>
</div>
</div>
<% end %>
<!-- Details Section -->
<%= disclosure t(".details") do %>
<%= styled_form_with model: @transfer,
data: { controller: "auto-submit-form" } do |f| %>
<%= f.text_area :notes,
label: t(".note_label"),
placeholder: t(".note_placeholder"),
value: @transfer.outflow_transaction.notes,
rows: 5,
"data-auto-submit-form-target": "auto" %>
<% end %>
<% end %>
<!-- Settings Section -->
<%= disclosure t(".settings") do %>
<div class="pb-4">
<%= styled_form_with model: @transfer,
class: "p-3", data: { controller: "auto-submit-form" } do |f| %>
<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>
<p class="text-gray-500"><%= t(".exclude_subtitle") %></p>
</div>
<div class="relative inline-block select-none">
<%= f.check_box :excluded,
checked: @transfer.inflow_transaction.excluded,
class: "sr-only peer",
"data-auto-submit-form-target": "auto" %>
<label for="account_transfer_excluded"
class="maybe-switch"></label>
</div>
</div>
<% end %>
<div class="flex items-center justify-between gap-2 p-3">
<div class="text-sm space-y-1">
<h4 class="text-gray-900"><%= t(".delete_title") %></h4>
<p class="text-gray-500"><%= t(".delete_subtitle") %></p>
</div>
<%= button_to t(".delete"),
account_transfer_path(@transfer),
method: :delete,
class: "rounded-lg px-3 py-2 text-red-500 text-sm
font-medium border border-alpha-black-200",
data: { turbo_confirm: true, turbo_frame: "_top" } %>
</div>
</div>
<% end %>
</div>
<% end %>

View file

@ -1,23 +1,13 @@
<%# locals: (entry:) %>
<%= form_with model: [entry.account, entry],
data: { turbo_frame: "_top" },
url: entry.new_record? ? account_valuations_path(entry.account) : account_entry_path(entry.account, entry) do |f| %>
<div class="grid grid-cols-10 p-4 items-center">
<div class="col-span-7 flex items-center gap-4">
<div class="w-8 h-8 rounded-full p-1.5 flex items-center justify-center bg-gray-500/5">
<%= lucide_icon("pencil-line", class: "w-4 h-4 text-gray-500") %>
</div>
<div class="w-full flex items-center justify-between gap-2">
<%= f.date_field :date, required: "required", min: Account::Entry.min_supported_date, max: Date.current, value: Date.current, class: "border border-alpha-black-200 bg-white rounded-lg shadow-xs min-w-[200px] px-3 py-1.5 text-gray-900 text-sm" %>
<%= f.number_field :amount, required: "required", placeholder: "0.00", step: "0.01", class: "bg-white border border-alpha-black-200 rounded-lg shadow-xs text-gray-900 text-sm px-3 py-1.5 text-right" %>
<%= f.hidden_field :currency, value: entry.account.currency %>
</div>
</div>
<div class="col-span-3 flex gap-2 justify-end items-center">
<%= link_to t(".cancel"), account_valuations_path(entry.account), class: "text-sm text-gray-900 hover:text-gray-800 font-medium px-3 py-1.5" %>
<%= f.submit class: "bg-gray-50 rounded-lg font-medium px-3 py-1.5 cursor-pointer hover:bg-gray-100 text-sm" %>
</div>
<%= styled_form_with model: [entry.account, entry],
url: entry.new_record? ? account_valuations_path(entry.account) : account_entry_path(entry.account, entry),
class: "space-y-4",
data: { turbo: false } do |form| %>
<div class="space-y-3">
<%= form.date_field :date, label: true, required: true, value: Date.current, min: Account::Entry.min_supported_date, max: Date.current %>
<%= form.money_field :amount, label: t(".amount"), required: true, default_currency: Current.family.currency %>
</div>
<%= form.submit t(".submit") %>
<% end %>

View file

@ -1,50 +1,39 @@
<%# locals: (entry:, **opts) %>
<%# locals: (entry:, selectable: true, show_balance: false, origin: nil) %>
<% account = entry.account %>
<% valuation = entry.account_valuation %>
<%= turbo_frame_tag dom_id(entry) do %>
<% is_oldest = entry.first_of_type? %>
<div class="p-4 grid grid-cols-12 items-center text-gray-900 text-sm font-medium">
<div class="col-span-8 flex items-center gap-4">
<% if selectable %>
<%= check_box_tag dom_id(entry, "selection"),
class: "maybe-checkbox maybe-checkbox--light",
data: { id: entry.id, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection" } %>
<% end %>
<div class="p-4 grid grid-cols-10 items-center">
<div class="col-span-5 flex items-center gap-4">
<%= tag.div class: "w-8 h-8 rounded-full p-1.5 flex items-center justify-center", style: entry_style(entry, is_oldest:).html_safe do %>
<%= lucide_icon entry_icon(entry, is_oldest:), class: "w-4 h-4" %>
<div class="flex items-center gap-3">
<%= tag.div class: "w-8 h-8 rounded-full p-1.5 flex items-center justify-center", style: mixed_hex_styles(valuation.color) do %>
<%= lucide_icon valuation.icon, class: "w-4 h-4" %>
<% end %>
<div class="text-sm">
<%= tag.p entry.date, class: "text-gray-900 font-medium" %>
<%= tag.p is_oldest ? t(".start_balance") : t(".value_update"), class: "text-gray-500" %>
<div class="truncate text-gray-900">
<% if entry.new_record? %>
<%= content_tag :p, entry.name %>
<% else %>
<%= link_to valuation.name,
account_entry_path(account, entry),
data: { turbo_frame: "drawer", turbo_prefetch: false },
class: "hover:underline hover:text-gray-800" %>
<% end %>
</div>
</div>
<div class="col-span-2 justify-self-end">
<%= tag.p format_money(entry.amount_money), class: "font-medium text-sm text-gray-900" %>
</div>
<div class="col-span-2 justify-self-end font-medium text-sm" style="color: <%= entry.trend.color %>">
<% if entry.trend.direction.flat? %>
<%= tag.span t(".no_change"), class: "text-gray-500" %>
<% else %>
<%= tag.span format_money(entry.trend.value) %>
<%= tag.span "(#{entry.trend.percent}%)" %>
<% end %>
</div>
<div class="col-span-1 justify-self-end">
<%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-gray-900 bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= contextual_menu_modal_action_item t(".edit_entry"), edit_account_entry_path(account, entry), turbo_frame: dom_id(entry) %>
<%= contextual_menu_destructive_item t(".delete_entry"),
account_entry_path(account, entry),
turbo_frame: "_top",
turbo_confirm: {
title: t(".confirm_title"),
body: t(".confirm_body_html"),
accept: t(".confirm_accept")
} %>
</div>
<% end %>
</div>
</div>
<% end %>
<div class="col-span-2 justify-self-end font-medium text-sm" style="color: <%= valuation.color %>">
<%= tag.span format_money(entry.trend.value) %>
</div>
<div class="col-span-2 justify-self-end">
<%= tag.p format_money(entry.amount_money), class: "font-medium text-sm text-gray-900" %>
</div>
</div>

View file

@ -1,4 +1,3 @@
<%= turbo_frame_tag dom_id(@entry) do %>
<%= render "account/valuations/form", entry: @entry %>
<div class="h-px bg-alpha-black-50 ml-20 mr-4"></div>
<%= modal_form_wrapper title: t(".title") do %>
<%= render "form", entry: @entry %>
<% end %>

View file

@ -1,3 +1,83 @@
<% entry = @entry %>
<% entry, account = @entry, @entry.account %>
<%= render "account/valuations/valuation", entry: entry %>
<%= drawer do %>
<header class="mb-4 space-y-1">
<span class="text-gray-500 text-sm">
<%= t(".balance") %>
</span>
<div class="flex items-center gap-4">
<h3 class="font-medium">
<span class="text-2xl">
<%= format_money entry.amount_money %>
</span>
</h3>
</div>
<span class="text-sm text-gray-500">
<%= I18n.l(entry.date, format: :long) %>
</span>
</header>
<div class="space-y-2">
<!-- Overview Section -->
<%= disclosure t(".overview") do %>
<div class="pb-4">
<%= styled_form_with model: [account, entry],
url: account_entry_path(account, 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,
"data-auto-submit-form-target": "auto" %>
<%= f.money_field :amount,
label: t(".amount"),
auto_submit: true,
disable_currency: true %>
<% end %>
</div>
<% end %>
<!-- Details Section -->
<%= disclosure t(".details") do %>
<div class="pb-4">
<%= styled_form_with model: [account, entry],
url: account_entry_path(account, entry),
class: "space-y-2",
data: { controller: "auto-submit-form" } do |f| %>
<%= f.text_area :notes,
label: t(".note_label"),
placeholder: t(".note_placeholder"),
rows: 5,
"data-auto-submit-form-target": "auto" %>
<% end %>
</div>
<% end %>
<!-- Settings Section -->
<%= disclosure t(".settings") do %>
<div class="pb-4">
<!-- Delete Valuation Form -->
<div class="flex items-center justify-between gap-2 p-3">
<div class="text-sm space-y-1">
<h4 class="text-gray-900"><%= t(".delete_title") %></h4>
<p class="text-gray-500"><%= t(".delete_subtitle") %></p>
</div>
<%= button_to t(".delete"),
account_entry_path(account, entry),
method: :delete,
class: "rounded-lg px-3 py-2 text-red-500 text-sm font-medium border border-alpha-black-200",
data: { turbo_confirm: true, turbo_frame: "_top" } %>
</div>
</div>
<% end %>
</div>
<% end %>