mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-26 00:29:40 +02:00
Account::Entry Delegated Type (namespace updates part 7) (#923)
* Initial entryable models * Update transfer and tests * Update transaction controllers and tests * Update sync process to use new entries model * Get dashboard working again * Update transfers, imports, and accounts to use Account::Entry * Update system tests * Consolidate transaction management into entries controller * Add permitted partial key helper * Move account transactions list to entries controller * Delegate transaction entries search * Move transfer relation to entry * Update bulk transaction management flows to use entries * Remove test code * Test fix attempt * Update demo data script * Consolidate remaining transaction partials to entries * Consolidate valuations controller to entries controller * Lint fix * Remove unused files, additional cleanup * Add back valuation creation * Make migrations fully reversible * Stale routes cleanup * Migrations reversible fix * Move types to entryable concern * Fix search when no entries found * Remove more unused code
This commit is contained in:
parent
320954282a
commit
c3314e62d1
105 changed files with 2150 additions and 1576 deletions
4
app/views/account/entries/_entry.html.erb
Normal file
4
app/views/account/entries/_entry.html.erb
Normal file
|
@ -0,0 +1,4 @@
|
|||
<%# locals: (entry:, **opts) %>
|
||||
<%= turbo_frame_tag dom_id(entry) do %>
|
||||
<%= render permitted_entryable_partial_path(entry, entry.entryable_name_short), entry: entry, **opts %>
|
||||
<% end %>
|
21
app/views/account/entries/_entry_group.html.erb
Normal file
21
app/views/account/entries/_entry_group.html.erb
Normal file
|
@ -0,0 +1,21 @@
|
|||
<%# locals: (date:, entries:, selectable: true, **opts) %>
|
||||
<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">
|
||||
<% if selectable %>
|
||||
<%= check_box_tag "#{date}_entries_selection",
|
||||
class: ["maybe-checkbox maybe-checkbox--light", "hidden": entries.size == 0],
|
||||
id: "selection_entry_#{date}",
|
||||
data: { action: "bulk-select#toggleGroupSelection" } %>
|
||||
<% end %>
|
||||
|
||||
<%= tag.span "#{date.strftime('%b %d, %Y')} · #{entries.size}" %>
|
||||
</div>
|
||||
|
||||
<%= totals_by_currency(collection: entries, money_method: :amount_money, negate: true) %>
|
||||
</div>
|
||||
<div class="bg-white shadow-xs rounded-md border border-alpha-black-25 divide-y divide-alpha-black-50">
|
||||
<%= render entries.reject { |e| e.transfer_id.present? }, selectable:, **opts %>
|
||||
<%= render transfer_entries(entries), selectable:, **opts %>
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,6 @@
|
|||
<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 "transaction_selection", 1, true, class: "maybe-checkbox maybe-checkbox--dark", data: { action: "bulk-select#deselectAll" } %>
|
||||
<%= 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>
|
||||
|
@ -19,7 +19,12 @@
|
|||
accept: t(".mark_transfers_confirm"),
|
||||
}
|
||||
} do |f| %>
|
||||
<button id="bulk-transfer-btn" type="button" data-bulk-select-scope-param="bulk_update" data-action="bulk-select#submitBulkRequest" class="p-1.5 group hover:bg-gray-700 flex items-center justify-center rounded-md" title="Mark as transfer">
|
||||
<button id="bulk-transfer-btn"
|
||||
type="button"
|
||||
data-bulk-select-scope-param="bulk_update"
|
||||
data-action="bulk-select#submitBulkRequest"
|
||||
class="p-1.5 group hover:bg-gray-700 flex items-center justify-center rounded-md"
|
||||
title="Mark as transfer">
|
||||
<%= lucide_icon "arrow-right-left", class: "w-5 group-hover:text-white" %>
|
||||
</button>
|
||||
<% end %>
|
3
app/views/account/entries/edit.html.erb
Normal file
3
app/views/account/entries/edit.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<%= turbo_frame_tag dom_id(@entry) do %>
|
||||
<%= render permitted_entryable_partial_path(@entry, "edit"), entry: @entry %>
|
||||
<% end %>
|
113
app/views/account/entries/entryables/transaction/_show.html.erb
Normal file
113
app/views/account/entries/entryables/transaction/_show.html.erb
Normal file
|
@ -0,0 +1,113 @@
|
|||
<%# locals: (entry:) %>
|
||||
|
||||
<% transaction, account = entry.account_transaction, entry.account %>
|
||||
|
||||
<%= drawer do %>
|
||||
<div>
|
||||
<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"><%= entry.date.strftime("%A %d %B") %></span>
|
||||
</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">
|
||||
<%= form_with model: [account, entry], url: account_entry_path(account, entry), html: { data: { controller: "auto-submit-form" } } do |f| %>
|
||||
<div class="space-y-2">
|
||||
<%= f.text_field :name, label: t(".name_label"), "data-auto-submit-form-target": "auto" %>
|
||||
<%= f.date_field :date, label: t(".date_label"), max: Date.current, "data-auto-submit-form-target": "auto" %>
|
||||
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<% unless entry.marked_as_transfer? %>
|
||||
<%= ef.collection_select :category_id, selectable_categories, :id, :name, { prompt: t(".category_placeholder"), label: t(".category_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
|
||||
<%= ef.collection_select :merchant_id, selectable_merchants, :id, :name, { prompt: t(".merchant_placeholder"), label: t(".merchant_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= f.collection_select :account_id, selectable_accounts, :id, :name, { prompt: t(".account_placeholder"), label: t(".account_label"), class: "text-gray-500" }, { class: "form-field__input cursor-not-allowed text-gray-400", disabled: "disabled" } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</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(".additional") %></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_with model: [account, entry], url: account_entry_path(account, entry), html: { data: { controller: "auto-submit-form" } } do |f| %>
|
||||
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<%= ef.select :tag_ids,
|
||||
options_for_select(selectable_tags, transaction.tag_ids),
|
||||
{
|
||||
multiple: true,
|
||||
label: t(".tags_label"),
|
||||
class: "placeholder:text-gray-500"
|
||||
},
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
<%= ef.text_area :notes, label: t(".note_label"), placeholder: t(".note_placeholder"), "data-auto-submit-form-target": "auto" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</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(".settings") %></h4>
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
|
||||
<div class="pb-6">
|
||||
<%= form_with model: [account, entry], url: account_entry_path(account, entry), html: { class: "p-3 space-y-3", data: { controller: "auto-submit-form" } } do |f| %>
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<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">
|
||||
<%= ef.check_box :excluded, class: "sr-only peer", "data-auto-submit-form-target": "auto" %>
|
||||
<label for="account_entry_entryable_attributes_excluded" class="maybe-switch"></label>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% 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>
|
||||
|
||||
<%= 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>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
|
@ -1,26 +1,27 @@
|
|||
<%# locals: (transaction:, selectable: true, editable: true, short: false, show_tags: false) %>
|
||||
<%= turbo_frame_tag dom_id(transaction), class: "grid grid-cols-12 items-center text-gray-900 text-sm font-medium p-4" do %>
|
||||
<%# locals: (entry:, selectable: true, editable: true, short: false, show_tags: false, **opts) %>
|
||||
<% transaction, account = entry.account_transaction, entry.account %>
|
||||
|
||||
<% name_col_span = transaction.transfer? ? "col-span-10" : short ? "col-span-6" : "col-span-4" %>
|
||||
<div class="grid grid-cols-12 items-center text-gray-900 text-sm font-medium p-4">
|
||||
<% name_col_span = entry.marked_as_transfer? ? "col-span-10" : short ? "col-span-6" : "col-span-4" %>
|
||||
<div class="pr-10 flex items-center gap-4 <%= name_col_span %>">
|
||||
<% if selectable %>
|
||||
<%= check_box_tag dom_id(transaction, "selection"),
|
||||
<%= check_box_tag dom_id(entry, "selection"),
|
||||
class: "maybe-checkbox maybe-checkbox--light",
|
||||
data: { id: transaction.id, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection" } %>
|
||||
data: { id: entry.id, "bulk-select-target": "row", action: "bulk-select#toggleRowSelection" } %>
|
||||
<% end %>
|
||||
|
||||
<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">
|
||||
<%= transaction.name[0].upcase %>
|
||||
<%= entry.name[0].upcase %>
|
||||
</div>
|
||||
|
||||
<div class="truncate text-gray-900">
|
||||
<% if transaction.new_record? %>
|
||||
<%= content_tag :p, transaction.name %>
|
||||
<% if entry.new_record? %>
|
||||
<%= content_tag :p, entry.name %>
|
||||
<% else %>
|
||||
<%= link_to transaction.name,
|
||||
account_transaction_path(transaction.account, transaction),
|
||||
<%= link_to entry.name,
|
||||
account_entry_path(account, entry),
|
||||
data: { turbo_frame: "drawer", turbo_prefetch: false },
|
||||
class: "hover:underline hover:text-gray-800" %>
|
||||
<% end %>
|
||||
|
@ -28,7 +29,7 @@
|
|||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if unconfirmed_transfer?(transaction) %>
|
||||
<% if unconfirmed_transfer?(entry) %>
|
||||
<% if editable %>
|
||||
<%= form_with url: unmark_transfers_transactions_path, builder: ActionView::Helpers::FormBuilder, class: "flex items-center", data: {
|
||||
turbo_confirm: {
|
||||
|
@ -38,7 +39,7 @@
|
|||
},
|
||||
turbo_frame: "_top"
|
||||
} do |f| %>
|
||||
<%= f.hidden_field "bulk_update[transaction_ids][]", value: transaction.id %>
|
||||
<%= 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" %>
|
||||
|
@ -50,7 +51,7 @@
|
|||
<% end %>
|
||||
</div>
|
||||
|
||||
<% unless transaction.transfer? %>
|
||||
<% unless entry.marked_as_transfer? %>
|
||||
<% unless short %>
|
||||
<div class="flex items-center gap-1 <%= show_tags ? "col-span-6" : "col-span-3" %>">
|
||||
<% if editable %>
|
||||
|
@ -69,11 +70,11 @@
|
|||
|
||||
<% unless show_tags %>
|
||||
<%= tag.div class: short ? "col-span-4" : "col-span-3" do %>
|
||||
<% if transaction.new_record? %>
|
||||
<%= tag.p transaction.account.name %>
|
||||
<% if entry.new_record? %>
|
||||
<%= tag.p account.name %>
|
||||
<% else %>
|
||||
<%= link_to transaction.account.name,
|
||||
account_path(transaction.account, tab: "transactions"),
|
||||
<%= link_to account.name,
|
||||
account_path(account, tab: "transactions"),
|
||||
data: { turbo_frame: "_top" },
|
||||
class: "hover:underline" %>
|
||||
<% end %>
|
||||
|
@ -83,7 +84,7 @@
|
|||
|
||||
<div class="col-span-2 ml-auto">
|
||||
<%= content_tag :p,
|
||||
format_money(-transaction.amount_money),
|
||||
class: ["text-green-600": transaction.inflow?] %>
|
||||
format_money(-entry.amount_money),
|
||||
class: ["text-green-600": entry.inflow?] %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
<%= render permitted_entryable_partial_path(entry, "form"), entry: entry %>
|
|
@ -0,0 +1,24 @@
|
|||
<%# locals: (entry:) %>
|
||||
<%= form_with model: [entry.account, entry],
|
||||
data: { turbo_frame: "_top" },
|
||||
url: entry.new_record? ? account_entries_path(entry.account) : account_entry_path(entry.account, entry),
|
||||
builder: ActionView::Helpers::FormBuilder 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", max: 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 %>
|
||||
<%= f.hidden_field :entryable_type, value: entry.entryable_type %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 flex gap-2 justify-end items-center">
|
||||
<%= link_to t(".cancel"), valuation_account_entries_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>
|
||||
</div>
|
||||
<% end %>
|
|
@ -0,0 +1,2 @@
|
|||
<%= render permitted_entryable_partial_path(entry, "form"), entry: entry %>
|
||||
<div class="h-px bg-alpha-black-50 ml-20 mr-4"></div>
|
|
@ -0,0 +1 @@
|
|||
<%= render permitted_entryable_partial_path(@entry, "valuation"), entry: @entry %>
|
|
@ -1,36 +1,42 @@
|
|||
<%= turbo_frame_tag dom_id(valuation) do %>
|
||||
<%# locals: (entry:, **opts) %>
|
||||
|
||||
<% account = entry.account %>
|
||||
|
||||
<%= turbo_frame_tag dom_id(entry) do %>
|
||||
<% is_oldest = entry.first_of_type? %>
|
||||
|
||||
<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: valuation_style(valuation).html_safe do %>
|
||||
<%= lucide_icon valuation_icon(valuation), class: "w-4 h-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" %>
|
||||
<% end %>
|
||||
|
||||
<div class="text-sm">
|
||||
<%= tag.p valuation.date, class: "text-gray-900 font-medium" %>
|
||||
<%= tag.p valuation.oldest? ? t(".start_balance") : t(".value_update"), class: "text-gray-500" %>
|
||||
<%= 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>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 justify-self-end">
|
||||
<%= tag.p format_money(valuation.value_money), class: "font-medium text-sm text-gray-900" %>
|
||||
<%= 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: <%= valuation.trend.color %>">
|
||||
<% if valuation.trend.direction.flat? %>
|
||||
<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(valuation.trend.value) %>
|
||||
<%= tag.span "(#{valuation.trend.percent}%)" %>
|
||||
<%= 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_valuation_path(valuation.account, valuation) %>
|
||||
<%= contextual_menu_modal_action_item t(".edit_entry"), edit_account_entry_path(account, entry) %>
|
||||
|
||||
<%= contextual_menu_destructive_item t(".delete_entry"),
|
||||
account_valuation_path(valuation.account, valuation),
|
||||
account_entry_path(account, entry),
|
||||
turbo_frame: "_top",
|
||||
turbo_confirm: {
|
||||
title: t(".confirm_title"),
|
3
app/views/account/entries/new.html.erb
Normal file
3
app/views/account/entries/new.html.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
<%= turbo_frame_tag dom_id(@entry) do %>
|
||||
<%= render permitted_entryable_partial_path(@entry, "new"), entry: @entry %>
|
||||
<% end %>
|
1
app/views/account/entries/show.html.erb
Normal file
1
app/views/account/entries/show.html.erb
Normal file
|
@ -0,0 +1 @@
|
|||
<%= render partial: permitted_entryable_partial_path(@entry, "show"), locals: { entry: @entry } %>
|
|
@ -12,15 +12,15 @@
|
|||
|
||||
<div id="transactions" data-controller="bulk-select" data-bulk-select-resource-value="<%= t(".transaction") %>">
|
||||
<div hidden id="transaction-selection-bar" data-bulk-select-target="selectionBar">
|
||||
<%= render "account/transactions/selection_bar" %>
|
||||
<%= render "selection_bar" %>
|
||||
</div>
|
||||
|
||||
<% if @transactions.empty? %>
|
||||
<% if @transaction_entries.empty? %>
|
||||
<p class="text-gray-500 py-4"><%= t(".no_transactions") %></p>
|
||||
<% else %>
|
||||
<div class="space-y-6">
|
||||
<% group_transactions_by_date(@transactions).each do |date, group| %>
|
||||
<%= render "transaction_group", date:, transactions: group[:transactions], transfers: group[:transfers] %>
|
||||
<% @transaction_entries.group_by(&:date).each do |date, entries| %>
|
||||
<%= render "entry_group", date:, entries: entries %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
|
@ -2,8 +2,8 @@
|
|||
<div class="bg-white space-y-4 p-5 border border-alpha-black-25 rounded-xl shadow-xs">
|
||||
<div class="flex items-center justify-between">
|
||||
<%= tag.h2 t(".valuations"), class: "font-medium text-lg" %>
|
||||
<%= link_to new_account_valuation_path(@account),
|
||||
data: { turbo_frame: dom_id(Account::Valuation.new) },
|
||||
<%= link_to new_account_entry_path(@account, entryable_type: "Account::Valuation"),
|
||||
data: { turbo_frame: dom_id(@account.entries.account_valuations.new) },
|
||||
class: "flex gap-1 font-medium items-center bg-gray-50 text-gray-900 p-2 rounded-lg" do %>
|
||||
<%= lucide_icon("plus", class: "w-5 h-5 text-gray-900") %>
|
||||
<%= tag.span t(".new_entry"), class: "text-sm" %>
|
||||
|
@ -19,13 +19,13 @@
|
|||
</div>
|
||||
|
||||
<div class="rounded-lg bg-white border-alpha-black-25 shadow-xs">
|
||||
<%= turbo_frame_tag dom_id(Account::Valuation.new) %>
|
||||
<%= turbo_frame_tag dom_id(@account.entries.account_valuations.new) %>
|
||||
|
||||
<% valuations = @account.valuations.reverse_chronological %>
|
||||
<% if valuations.any? %>
|
||||
<%= render partial: "valuation",
|
||||
collection: @account.valuations.reverse_chronological,
|
||||
spacer_template: "valuation_ruler" %>
|
||||
<% if @valuation_entries.any? %>
|
||||
<%= render partial: "account/entries/entryables/valuation/valuation",
|
||||
collection: @valuation_entries,
|
||||
as: :entry,
|
||||
spacer_template: "ruler" %>
|
||||
<% else %>
|
||||
<p class="text-gray-500 text-sm p-4"><%= t(".no_valuations") %></p>
|
||||
<% end %>
|
|
@ -1 +0,0 @@
|
|||
<%= render "account/transactions/transaction", transaction: @transaction %>
|
|
@ -1,21 +0,0 @@
|
|||
<%# locals: (date:, transactions:, transfers: [], selectable: true, **transaction_opts) %>
|
||||
<div id="date-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">
|
||||
<% if selectable %>
|
||||
<%= check_box_tag "#{date}_transactions_selection",
|
||||
class: ["maybe-checkbox maybe-checkbox--light", "hidden": transactions.count == 0],
|
||||
id: "selection_transaction_#{date}",
|
||||
data: { action: "bulk-select#toggleGroupSelection" } %>
|
||||
<% end %>
|
||||
|
||||
<%= tag.span "#{date.strftime('%b %d, %Y')} · #{transactions.size + (transfers.size * 2)}" %>
|
||||
</div>
|
||||
|
||||
<%= totals_by_currency(collection: transactions, money_method: :amount_money, negate: true) %>
|
||||
</div>
|
||||
<div class="bg-white shadow-xs rounded-md border border-alpha-black-25 divide-y divide-alpha-black-50">
|
||||
<%= render transactions, selectable:, **transaction_opts.except(:selectable) %>
|
||||
<%= render transfers %>
|
||||
</div>
|
||||
</div>
|
|
@ -1,103 +0,0 @@
|
|||
<%= drawer do %>
|
||||
<div>
|
||||
<header class="mb-4 space-y-1">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="font-medium">
|
||||
<span class="text-2xl"><%= format_money -@transaction.amount_money %></span>
|
||||
<span class="text-lg text-gray-500"><%= @transaction.currency %></span>
|
||||
</h3>
|
||||
|
||||
<% if @transaction.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"><%= @transaction.date.strftime("%A %d %B") %></span>
|
||||
</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">
|
||||
<%= form_with model: [@account, @transaction], url: account_transaction_path, html: { data: { controller: "auto-submit-form" } } do |f| %>
|
||||
<div class="space-y-2">
|
||||
<%= f.text_field :name, label: t(".name_label"), "data-auto-submit-form-target": "auto" %>
|
||||
<%= f.date_field :date, label: t(".date_label"), max: Date.today, "data-auto-submit-form-target": "auto" %>
|
||||
|
||||
<% unless @transaction.marked_as_transfer %>
|
||||
<%= f.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: t(".category_placeholder"), label: t(".category_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
|
||||
<%= f.collection_select :merchant_id, Current.family.merchants.alphabetically, :id, :name, { prompt: t(".merchant_placeholder"), label: t(".merchant_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
|
||||
<% end %>
|
||||
|
||||
<%= f.collection_select :account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".account_placeholder"), label: t(".account_label"), class: "text-gray-500" }, { class: "form-field__input cursor-not-allowed text-gray-400", disabled: "disabled" } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</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(".additional") %></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_with model: [@account, @transaction], url: account_transaction_path, html: { data: { controller: "auto-submit-form" } } do |f| %>
|
||||
<%= f.select :tag_ids,
|
||||
options_for_select(Current.family.tags.alphabetically.pluck(:name, :id), @transaction.tag_ids),
|
||||
{
|
||||
multiple: true,
|
||||
label: t(".tags_label"),
|
||||
class: "placeholder:text-gray-500"
|
||||
},
|
||||
"data-auto-submit-form-target": "auto" %>
|
||||
<%= f.text_area :notes, label: t(".note_label"), placeholder: t(".note_placeholder"), "data-auto-submit-form-target": "auto" %>
|
||||
<% end %>
|
||||
</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(".settings") %></h4>
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
|
||||
<div class="pb-6">
|
||||
|
||||
<%= form_with model: [@account, @transaction], url: account_transaction_path, html: { class: "p-3 space-y-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, class: "sr-only peer", "data-auto-submit-form-target": "auto" %>
|
||||
<label for="account_transaction_excluded" class="maybe-switch"></label>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% unless @transaction.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>
|
||||
|
||||
<%= button_to t(".delete"),
|
||||
account_transaction_path(@account, @transaction),
|
||||
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>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
|
@ -19,11 +19,11 @@
|
|||
</section>
|
||||
|
||||
<section class="space-y-2">
|
||||
<%= f.text_field :name, value: transfer.transactions.first&.name, label: t(".description"), placeholder: t(".description_placeholder"), required: true %>
|
||||
<%= f.text_field :name, value: transfer.name, label: t(".description"), placeholder: t(".description_placeholder"), required: true %>
|
||||
<%= f.collection_select :from_account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".from") }, required: true %>
|
||||
<%= f.collection_select :to_account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".to") }, required: true %>
|
||||
<%= f.money_field :amount_money, label: t(".amount"), required: true %>
|
||||
<%= f.date_field :date, value: transfer.transactions.first&.date, label: t(".date"), required: true, max: Date.current %>
|
||||
<%= f.date_field :date, value: transfer.date, label: t(".date"), required: true, max: Date.current %>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -1,34 +1,49 @@
|
|||
<%= turbo_frame_tag dom_id(transfer), class: "block" do %>
|
||||
<details class="group flex items-center text-gray-900 text-sm font-medium">
|
||||
<summary class="flex items-center justify-between p-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<%= button_to account_transfer_path(transfer),
|
||||
method: :delete,
|
||||
class: "flex items-center group/transfer",
|
||||
data: {
|
||||
turbo_frame: "_top",
|
||||
turbo_confirm: {
|
||||
title: t(".remove_title"),
|
||||
body: t(".remove_body"),
|
||||
confirm: t(".remove_confirm")
|
||||
}
|
||||
} do %>
|
||||
<%= lucide_icon "arrow-left-right", class: "group-hover/transfer:hidden w-5 h-5 text-gray-500" %>
|
||||
<%= lucide_icon "unlink", class: "group-hover/transfer:inline-block hidden w-5 h-5 text-gray-500" %>
|
||||
<% end %>
|
||||
<%# locals: (transfer:, selectable: true, editable: true, short: false, **opts) %>
|
||||
|
||||
<div class="max-w-full pr-10 select-none">
|
||||
<%= tag.p t(".transfer_name", from_account: transfer.outflow_transaction&.account&.name, to_account: transfer.inflow_transaction&.account&.name) %>
|
||||
<%= 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">
|
||||
<%= circle_logo("T") %>
|
||||
|
||||
<%= tag.p transfer.name, class: "truncate text-gray-900" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
<%= 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 %>
|
||||
|
||||
<div class="divide-y divide-alpha-black-200">
|
||||
<% transfer.transactions.each do |transaction| %>
|
||||
<%= render transaction, selectable: false, editable: false %>
|
||||
<%= 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>
|
||||
</details>
|
||||
|
||||
<% 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">→</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 %>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
<%# locals: (valuation:) %>
|
||||
<%= form_with model: valuation,
|
||||
data: { turbo_frame: "_top" },
|
||||
url: valuation.new_record? ? account_valuations_path(valuation.account) : account_valuation_path(valuation.account, valuation),
|
||||
builder: ActionView::Helpers::FormBuilder 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", max: Date.today, 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 :value, 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: valuation.account.currency %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 flex gap-2 justify-end items-center">
|
||||
<%= link_to t(".cancel"), account_valuations_path(valuation.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>
|
||||
</div>
|
||||
<% end %>
|
|
@ -1,5 +0,0 @@
|
|||
<div class="bg-white space-y-4 p-5 border border-alpha-black-25 rounded-xl shadow-xs">
|
||||
<div class="p-5 flex justify-center items-center">
|
||||
<%= tag.p t(".loading"), class: "text-gray-500 animate-pulse text-sm" %>
|
||||
</div>
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<%= turbo_frame_tag dom_id(@valuation) do %>
|
||||
<%= render "form", valuation: @valuation %>
|
||||
<% end %>
|
|
@ -1,4 +0,0 @@
|
|||
<%= turbo_frame_tag dom_id(@valuation) do %>
|
||||
<%= render "form", valuation: @valuation %>
|
||||
<div class="h-px bg-alpha-black-50 ml-20 mr-4"></div>
|
||||
<% end %>
|
|
@ -1 +0,0 @@
|
|||
<%= render "valuation", valuation: @valuation %>
|
|
@ -81,12 +81,12 @@
|
|||
|
||||
<div class="min-h-[800px]">
|
||||
<% if selected_tab == "transactions" %>
|
||||
<%= turbo_frame_tag dom_id(@account, "transactions"), src: account_transactions_path(@account) do %>
|
||||
<%= render "account/transactions/loading" %>
|
||||
<%= turbo_frame_tag dom_id(@account, "transactions"), src: transaction_account_entries_path(@account) do %>
|
||||
<%= render "account/entries/loading" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= turbo_frame_tag dom_id(@account, "valuations"), src: account_valuations_path(@account) do %>
|
||||
<%= render "account/valuations/loading" %>
|
||||
<%= turbo_frame_tag dom_id(@account, "valuations"), src: valuation_account_entries_path(@account) do %>
|
||||
<%= render "account/entries/loading" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<% is_selected = category.id === @selected_category&.id %>
|
||||
|
||||
<%= content_tag :div, class: ["filterable-item flex justify-between items-center border-none rounded-lg px-2 py-1 group w-full", { "bg-gray-25": is_selected }], data: { filter_name: category.name } do %>
|
||||
<%= button_to account_transaction_row_path(@transaction.account, @transaction, transaction: { category_id: category.id }), method: :patch, data: { turbo_frame: dom_id(@transaction) }, class: "flex w-full items-center gap-1.5 cursor-pointer" do %>
|
||||
<%= button_to account_entry_path(@transaction.entry.account, @transaction.entry, account_entry: { entryable_type: "Account::Transaction", entryable_attributes: { id: @transaction.id, category_id: category.id } }), method: :patch, data: { turbo_frame: dom_id(@transaction.entry) }, class: "flex w-full items-center gap-1.5 cursor-pointer" do %>
|
||||
<span class="w-5 h-5">
|
||||
<%= lucide_icon("check", class: "w-5 h-5 text-gray-500") if is_selected %>
|
||||
</span>
|
||||
|
|
|
@ -25,10 +25,10 @@
|
|||
<% end %>
|
||||
|
||||
<% if @transaction.category %>
|
||||
<%= button_to account_transaction_row_path(@transaction.account, @transaction),
|
||||
<%= button_to account_entry_path(@transaction.entry.account, @transaction.entry),
|
||||
method: :patch,
|
||||
data: { turbo_frame: dom_id(@transaction) },
|
||||
params: { transaction: { category_id: nil } },
|
||||
data: { turbo_frame: dom_id(@transaction.entry) },
|
||||
params: { account_entry: { entryable_type: "Account::Transaction", entryable_attributes: { id: @transaction.id, category_id: nil } } },
|
||||
class: "flex text-sm font-medium items-center gap-2 text-gray-500 w-full rounded-lg p-2 hover:bg-gray-100" do %>
|
||||
<%= lucide_icon "minus", class: "w-5 h-5" %>
|
||||
|
||||
|
|
|
@ -9,12 +9,11 @@
|
|||
</div>
|
||||
|
||||
<div class="mb-8 space-y-4">
|
||||
<% transactions = @import.dry_run %>
|
||||
<% group_transactions_by_date(transactions).each do |date, group| %>
|
||||
<%= render "account/transactions/transaction_group",
|
||||
<% transaction_entries = @import.dry_run %>
|
||||
<% transaction_entries.group_by(&:date).each do |date, transactions| %>
|
||||
<%= render "account/entries/entry_group",
|
||||
date: date,
|
||||
transactions: group[:transactions],
|
||||
transfers: group[:transfers],
|
||||
entries: transaction_entries,
|
||||
show_tags: true,
|
||||
selectable: false,
|
||||
editable: false %>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<% end %>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<%= previous_setting("Rules", account_transaction_rules_path) %>
|
||||
<%= previous_setting("Rules", rules_transactions_path) %>
|
||||
<%= next_setting("What's new", changelog_path) %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -35,6 +35,6 @@
|
|||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<%= previous_setting("Categories", categories_path) %>
|
||||
<%= next_setting("Rules", account_transaction_rules_path) %>
|
||||
<%= next_setting("Rules", rules_transactions_path) %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -156,17 +156,16 @@
|
|||
<section class="grid grid-cols-2 gap-4 items-baseline">
|
||||
<div class="bg-white p-4 border border-alpha-black-25 shadow-xs rounded-xl space-y-4">
|
||||
<h2 class="text-lg font-medium text-gray-900"><%= t(".transactions") %></h2>
|
||||
<% if @transactions.empty? %>
|
||||
<% if @transaction_entries.empty? %>
|
||||
<div class="text-gray-500 flex items-center justify-center py-12">
|
||||
<p><%= t(".no_transactions") %></p>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="text-gray-500 p-1 space-y-1 bg-gray-25 rounded-xl">
|
||||
<% group_transactions_by_date(@transactions).each do |date, group| %>
|
||||
<%= render "account/transactions/transaction_group",
|
||||
<% @transaction_entries.group_by(&:date).each do |date, transactions| %>
|
||||
<%= render "account/entries/entry_group",
|
||||
date: date,
|
||||
transactions: group[:transactions],
|
||||
transfers: group[:transfers],
|
||||
entries: transactions,
|
||||
selectable: false,
|
||||
editable: false,
|
||||
short: true %>
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<%= sidebar_link_to t(".merchants_label"), merchants_path, icon: "store" %>
|
||||
</li>
|
||||
<li>
|
||||
<%= sidebar_link_to t(".rules_label"), account_transaction_rules_path, icon: "list-checks" %>
|
||||
<%= sidebar_link_to t(".rules_label"), rules_transactions_path, icon: "list-checks" %>
|
||||
</li>
|
||||
<li>
|
||||
<%= sidebar_link_to t(".imports_label"), imports_path, icon: "download" %>
|
||||
|
|
13
app/views/shared/_circle_logo.html.erb
Normal file
13
app/views/shared/_circle_logo.html.erb
Normal file
|
@ -0,0 +1,13 @@
|
|||
<%# locals: (name:, hex: nil, size: "md") %>
|
||||
|
||||
<% size_classes = {
|
||||
"sm" => "w-6 h-6",
|
||||
"md" => "w-8 h-8",
|
||||
"lg" => "w-10 h-10",
|
||||
"full" => "w-full h-full"
|
||||
} %>
|
||||
|
||||
<%= tag.div style: mixed_hex_styles(hex),
|
||||
class: [size_classes[size], "flex shrink-0 items-center justify-center rounded-full"] do %>
|
||||
<%= tag.span name[0].upcase, class: ["font-medium", size == "sm" ? "text-xs" : "text-sm"] %>
|
||||
<% end %>
|
|
@ -1,4 +1,4 @@
|
|||
<%= form_with model: @transaction, url: transactions_path, scope: "transaction", data: { turbo_frame: "_top" } do |f| %>
|
||||
<%= form_with model: @entry, url: transactions_path, data: { turbo_frame: "_top" } do |f| %>
|
||||
<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: :expense, label: t(".expense"), icon: "minus-circle", checked: params[:nature] == "expense" || params[:nature].nil? %>
|
||||
|
@ -14,7 +14,10 @@
|
|||
<%= f.text_field :name, label: t(".description"), placeholder: t(".description_placeholder"), required: true %>
|
||||
<%= f.collection_select :account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".account_prompt"), label: t(".account") }, required: true %>
|
||||
<%= f.money_field :amount_money, label: t(".amount"), required: true %>
|
||||
<%= f.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: t(".category_prompt"), label: t(".category") } %>
|
||||
<%= f.hidden_field :entryable_type, value: "Account::Transaction" %>
|
||||
<%= 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, max: Date.today %>
|
||||
</section>
|
||||
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
<%# locals: (pagy:) %>
|
||||
<nav class="flex items-center justify-between px-4 mt-4 sm:px-0 w-full">
|
||||
<div class="flex">
|
||||
<nav class="flex w-full items-center justify-between">
|
||||
<div class="flex items-center gap-1">
|
||||
<div>
|
||||
<% if pagy.prev %>
|
||||
<%= link_to pagy_url_for(pagy, pagy.prev), class: "inline-flex items-center px-3 py-3 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= link_to pagy_url_for(pagy, pagy.prev), class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= lucide_icon("chevron-left", class: "w-5 h-5 text-gray-500") %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="inline-flex items-center px-3 py-3 text-sm font-medium hover:border-gray-300">
|
||||
<div class="inline-flex items-center p-2 text-sm font-medium hover:border-gray-300">
|
||||
<%= lucide_icon("chevron-left", class: "w-5 h-5 text-gray-200") %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="bg-gray-25 rounded-xl">
|
||||
<div class="rounded-xl p-1 bg-gray-25">
|
||||
<% pagy.series.each do |series_item| %>
|
||||
<% if series_item.is_a?(Integer) %>
|
||||
<%= link_to pagy_url_for(pagy, series_item), class: "inline-flex items-center px-3 py-3 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= link_to pagy_url_for(pagy, series_item), class: "rounded-md px-2 py-1 inline-flex items-center text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= series_item %>
|
||||
<% end %>
|
||||
<% elsif series_item.is_a?(String) %>
|
||||
<%= link_to pagy_url_for(pagy, series_item), class: "shadow-lg ring-2 ring-inset ring-gray-200 rounded-xl bg-white inline-flex items-center m-1 px-4 py-2 text-sm font-medium text-gray-900" do %>
|
||||
<%= link_to pagy_url_for(pagy, series_item), class: "rounded-md px-2 py-1 bg-white border border-alpha-black-25 shadow-xs inline-flex items-center text-sm font-medium text-gray-900" do %>
|
||||
<%= series_item %>
|
||||
<% end %>
|
||||
<% elsif series_item == :gap %>
|
||||
<span class="inline-flex items-center px-3 py-3 text-sm font-medium text-gray-500">...</span>
|
||||
<span class="inline-flex items-center px-2 py-1 text-sm font-medium text-gray-500">...</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div>
|
||||
<% if pagy.next %>
|
||||
<%= link_to pagy_url_for(pagy, pagy.next), class: "inline-flex items-center px-3 py-3 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= link_to pagy_url_for(pagy, pagy.next), class: "inline-flex items-center p-2 text-sm font-medium text-gray-500 hover:border-gray-300 hover:text-gray-700" do %>
|
||||
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-500") %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="inline-flex items-center px-3 py-3 text-sm font-medium hover:border-gray-300">
|
||||
<div class="inline-flex items-center p-2 text-sm font-medium hover:border-gray-300">
|
||||
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-200") %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
@ -47,7 +47,7 @@
|
|||
data: { controller: "auto-submit-form" } do |f| %>
|
||||
<%= f.label :per_page, t(".rows_per_page"), class: "text-sm text-gray-500" %>
|
||||
<%= f.select :per_page,
|
||||
options_for_select(["10", "20", "30", "50"], params[:per_page]),
|
||||
options_for_select(["10", "20", "30", "50"], pagy.items),
|
||||
{},
|
||||
class: "py-1.5 pr-8 text-sm text-gray-900 font-medium border border-gray-200 rounded-lg focus:border-gray-900 focus:ring-gray-900 focus-visible:ring-gray-900",
|
||||
data: { "auto-submit-form-target": "auto" } %>
|
||||
|
|
|
@ -28,44 +28,26 @@
|
|||
|
||||
<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" } %>
|
||||
</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(".additional") %></h4>
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
|
||||
<div>
|
||||
<%= form.text_area :notes, label: t(".note_label"), placeholder: t(".note_placeholder"), rows: 5 %>
|
||||
</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(".settings") %></h4>
|
||||
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
|
||||
</summary>
|
||||
|
||||
<div class="flex cursor-pointer items-center justify-between gap-4 p-3 pb-6">
|
||||
<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">
|
||||
<%= form.check_box :excluded, class: "sr-only peer" %>
|
||||
<label for="bulk_update_excluded" class="maybe-switch"></label>
|
||||
</div>
|
||||
</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" %>
|
||||
|
||||
|
|
|
@ -3,19 +3,20 @@
|
|||
|
||||
<%= render "summary", totals: @totals %>
|
||||
|
||||
<div id="transactions" data-controller="bulk-select" data-bulk-select-resource-value="<%= t(".transaction") %>" class="overflow-y-auto flex flex-col bg-white rounded-xl border border-alpha-black-25 shadow-xs pb-4">
|
||||
<div class="p-4 pb-0">
|
||||
<%= render partial: "transactions/searches/search", locals: { transactions: @transactions } %>
|
||||
</div>
|
||||
<div id="transactions"
|
||||
data-controller="bulk-select"
|
||||
data-bulk-select-resource-value="<%= t(".transaction") %>"
|
||||
class="overflow-y-auto flex flex-col bg-white rounded-xl border border-alpha-black-25 shadow-xs p-4">
|
||||
<%= render "transactions/searches/search" %>
|
||||
|
||||
<% if @transactions.present? %>
|
||||
<div hidden id="transaction-selection-bar" data-bulk-select-target="selectionBar">
|
||||
<%= render "account/transactions/selection_bar" %>
|
||||
<% if @transaction_entries.present? %>
|
||||
<div hidden id="entry-selection-bar" data-bulk-select-target="selectionBar">
|
||||
<%= render "account/entries/selection_bar" %>
|
||||
</div>
|
||||
<div class="grow overflow-y-auto px-4">
|
||||
<div class="grow overflow-y-auto">
|
||||
<div class="grid grid-cols-12 bg-gray-25 rounded-xl px-5 py-3 text-xs uppercase font-medium text-gray-500 items-center mb-4">
|
||||
<div class="pl-0.5 col-span-4 flex items-center gap-4">
|
||||
<%= check_box_tag "selection_transaction",
|
||||
<%= check_box_tag "selection_entry",
|
||||
class: "maybe-checkbox maybe-checkbox--light",
|
||||
data: { action: "bulk-select#togglePageSelection" } %>
|
||||
<p class="col-span-4">transaction</p>
|
||||
|
@ -26,19 +27,17 @@
|
|||
<p class="col-span-2 justify-self-end">amount</p>
|
||||
</div>
|
||||
<div class="space-y-6">
|
||||
<% group_transactions_by_date(@transactions).each do |date, group| %>
|
||||
<%= render "account/transactions/transaction_group", date:, transactions: group[:transactions], transfers: group[:transfers] %>
|
||||
<% @transaction_entries.group_by(&:date).each do |date, entries| %>
|
||||
<%= render "account/entries/entry_group", date:, entries: %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= render "account/transactions/empty" %>
|
||||
<%= render "account/entries/empty" %>
|
||||
<% end %>
|
||||
|
||||
<div class="px-4">
|
||||
<% if @pagy.pages > 1 %>
|
||||
<%= render "pagination", pagy: @pagy %>
|
||||
<% end %>
|
||||
<div class="pt-4">
|
||||
<%= render "pagination", pagy: @pagy %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
<%= lucide_icon "x", class: "w-5 h-5 text-gray-500", data: { action: "click->modal#close" } %>
|
||||
</header>
|
||||
|
||||
<%= render "form", transaction: @transaction %>
|
||||
<%= render "form", transaction: @transaction, entry: @entry %>
|
||||
</article>
|
||||
<% end %>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<%# locals: (transactions:) %>
|
||||
<%= form_with url: transactions_path,
|
||||
id: "transactions-search",
|
||||
scope: :q,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<%= render partial: "transactions/searches/form", locals: { transactions: transactions } %>
|
||||
<%= render "transactions/searches/form" %>
|
||||
|
||||
<ul id="transaction-search-filters" class="flex items-center flex-wrap gap-2">
|
||||
<% @q.each do |param_key, param_value| %>
|
||||
<% unless param_value.blank? %>
|
||||
<div class="pb-4">
|
||||
<% Array(param_value).each do |value| %>
|
||||
<%= render partial: "transactions/searches/filters/badge", locals: { param_key: param_key, param_value: value } %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="pb-4">
|
||||
<% Array(param_value).each do |value| %>
|
||||
<%= render partial: "transactions/searches/filters/badge", locals: { param_key: param_key, param_value: value } %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue