1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-09 07:25:19 +02:00

Account:: namespace simplifications and cleanup (#2110)

* Flatten Holding model

* Flatten balance model

* Entries domain renames

* Fix valuations reference

* Fix trades stream

* Fix brakeman warnings

* Fix tests

* Replace existing entryable type references in DB
This commit is contained in:
Zach Gollwitzer 2025-04-14 11:40:34 -04:00 committed by GitHub
parent f181ba941f
commit e657c40d19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
172 changed files with 1297 additions and 1258 deletions

View file

@ -0,0 +1,32 @@
<%# locals: (account:) %>
<% currency = Money::Currency.new(account.currency) %>
<div class="grid grid-cols-12 items-center text-primary text-sm font-medium p-4">
<div class="col-span-4 flex items-center gap-4">
<%= render "shared/circle_logo", name: currency.iso_code %>
<div class="space-y-0.5">
<%= tag.p t(".brokerage_cash"), class: "text-primary" %>
<%= tag.p account.currency, class: "text-secondary text-xs uppercase" %>
</div>
</div>
<div class="col-span-2 flex justify-end items-center gap-2">
<% cash_weight = account.balance.zero? ? 0 : account.cash_balance / account.balance * 100 %>
<%= render "shared/progress_circle", progress: cash_weight %>
<%= tag.p number_to_percentage(cash_weight, precision: 1) %>
</div>
<div class="col-span-2 text-right">
<%= tag.p "--", class: "text-secondary" %>
</div>
<div class="col-span-2 text-right">
<%= tag.p format_money account.cash_balance_money %>
</div>
<div class="col-span-2 text-right">
<%= tag.p "--", class: "text-secondary" %>
</div>
</div>

View file

@ -0,0 +1,51 @@
<%# locals: (holding:) %>
<%= turbo_frame_tag dom_id(holding) do %>
<div class="grid grid-cols-12 items-center text-primary text-sm font-medium p-4">
<div class="col-span-4 flex items-center gap-4">
<%= image_tag "https://logo.synthfinance.com/ticker/#{holding.ticker}", class: "w-9 h-9 rounded-full", loading: "lazy" %>
<div class="space-y-0.5">
<%= link_to holding.name, holding_path(holding), data: { turbo_frame: :drawer }, class: "hover:underline" %>
<% if holding.amount %>
<%= tag.p holding.ticker, class: "text-secondary text-xs uppercase" %>
<% else %>
<%= render "missing_price_tooltip" %>
<% end %>
</div>
</div>
<div class="col-span-2 flex justify-end items-center gap-2">
<% if holding.weight %>
<%= render "shared/progress_circle", progress: holding.weight %>
<%= tag.p number_to_percentage(holding.weight, precision: 1) %>
<% else %>
<%= tag.p "--", class: "text-secondary mb-5" %>
<% end %>
</div>
<div class="col-span-2 text-right">
<%= tag.p format_money holding.avg_cost %>
<%= tag.p t(".per_share"), class: "font-normal text-secondary" %>
</div>
<div class="col-span-2 text-right">
<% if holding.amount_money %>
<%= tag.p format_money holding.amount_money %>
<% else %>
<%= tag.p "--", class: "text-secondary" %>
<% end %>
<%= tag.p t(".shares", qty: number_with_precision(holding.qty, precision: 1)), class: "font-normal text-secondary" %>
</div>
<div class="col-span-2 text-right">
<% if holding.trend %>
<%= tag.p format_money(holding.trend.value), style: "color: #{holding.trend.color};" %>
<%= tag.p "(#{number_to_percentage(holding.trend.percent, precision: 1)})", style: "color: #{holding.trend.color};" %>
<% else %>
<%= tag.p "--", class: "text-secondary mb-4" %>
<% end %>
</div>
</div>
<% end %>

View file

@ -0,0 +1,11 @@
<div data-controller="tooltip" data-tooltip-cross-axis-value="50">
<div class="flex items-center gap-1 text-warning">
<%= lucide_icon "info", class: "w-4 h-4 shrink-0" %>
<%= tag.span t(".missing_data"), class: "font-normal text-xs" %>
</div>
<div role="tooltip" data-tooltip-target="tooltip" class="tooltip bg-gray-700 text-sm p-2 rounded w-64">
<div class="text-white">
<%= t(".description") %>
</div>
</div>
</div>

View file

@ -0,0 +1 @@
<div class="h-px bg-alpha-black-50 ml-16 mr-4"></div>

View file

@ -0,0 +1,34 @@
<%= turbo_frame_tag dom_id(@account, "holdings") do %>
<div class="bg-container space-y-4 p-5 rounded-xl shadow-border-xs">
<div class="flex items-center justify-between">
<%= tag.h2 t(".holdings"), class: "font-medium text-lg" %>
<%= link_to new_trade_path(account_id: @account.id),
id: dom_id(@account, "new_trade"),
data: { turbo_frame: :modal },
class: "flex gap-1 font-medium items-center bg-gray-50 text-primary p-2 rounded-lg" do %>
<%= lucide_icon("plus", class: "w-5 h-5 text-primary") %>
<%= tag.span t(".new_holding"), class: "text-sm" %>
<% end %>
</div>
<div class="rounded-xl bg-container-inset p-1">
<div class="grid grid-cols-12 items-center uppercase text-xs font-medium text-secondary px-4 py-2">
<%= tag.p t(".name"), class: "col-span-4" %>
<%= tag.p t(".weight"), class: "col-span-2 justify-self-end" %>
<%= tag.p t(".cost"), class: "col-span-2 justify-self-end" %>
<%= tag.p t(".holdings"), class: "col-span-2 justify-self-end" %>
<%= tag.p t(".return"), class: "col-span-2 justify-self-end" %>
</div>
<div class="rounded-lg bg-container shadow-border-xs">
<%= render "holdings/cash", account: @account %>
<%= render "holdings/ruler" %>
<% if @account.current_holdings.any? %>
<%= render partial: "holdings/holding", collection: @account.current_holdings, spacer_template: "ruler" %>
<% end %>
</div>
</div>
</div>
<% end %>

View file

@ -0,0 +1 @@
<p>Coming soon...</p>

View file

@ -0,0 +1,114 @@
<%= drawer do %>
<div class="space-y-4">
<header class="flex justify-between">
<div>
<%= tag.h3 @holding.name, class: "text-2xl font-medium text-primary" %>
<%= tag.p @holding.ticker, class: "text-sm text-secondary" %>
</div>
<%= image_tag "https://logo.synthfinance.com/ticker/#{@holding.ticker}", loading: "lazy", class: "w-9 h-9 rounded-full" %>
</header>
<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-secondary bg-gray-25 focus-visible:outline-hidden">
<h4><%= t(".overview") %></h4>
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-secondary w-5" %>
</summary>
<div class="pb-4">
<dl class="space-y-3 px-3 py-2">
<div class="flex items-center justify-between text-sm">
<dt class="text-secondary"><%= t(".ticker_label") %></dt>
<dd class="text-primary"><%= @holding.ticker %></dd>
</div>
<div class="flex items-center justify-between text-sm">
<dt class="text-secondary"><%= t(".current_market_price_label") %></dt>
<dd class="text-primary"><%= @holding.security.current_price ? format_money(@holding.security.current_price) : t(".unknown") %></dd>
</div>
<div class="flex items-center justify-between text-sm">
<dt class="text-secondary"><%= t(".portfolio_weight_label") %></dt>
<dd class="text-primary"><%= @holding.weight ? number_to_percentage(@holding.weight, precision: 2) : t(".unknown") %></dd>
</div>
<div class="flex items-center justify-between text-sm">
<dt class="text-secondary"><%= t(".avg_cost_label") %></dt>
<dd class="text-primary"><%= @holding.avg_cost ? format_money(@holding.avg_cost) : t(".unknown") %></dd>
</div>
<div class="flex items-center justify-between text-sm">
<dt class="text-secondary"><%= t(".trend_label") %></dt>
<dd style="color: <%= @holding.trend&.color %>;">
<%= @holding.trend ? render("shared/trend_change", trend: @holding.trend) : t(".unknown") %>
</dd>
</div>
</dl>
</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-secondary bg-gray-25 focus-visible:outline-hidden">
<h4><%= t(".history") %></h4>
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-secondary w-5" %>
</summary>
<div class="space-y-2">
<div class="px-3 py-4">
<% if @holding.trades.any? %>
<ul class="space-y-2">
<% @holding.trades.each_with_index do |trade_entry, index| %>
<li class="flex gap-4 text-sm space-y-1">
<div class="flex flex-col items-center gap-1.5 pt-2">
<div class="rounded-full h-1.5 w-1.5 bg-gray-300"></div>
<% unless index == @holding.trades.length - 1 %>
<div class="h-12 w-px bg-alpha-black-200"></div>
<% end %>
</div>
<div>
<p class="text-secondary text-xs uppercase"><%= l(trade_entry.date, format: :long) %></p>
<p><%= t(
".trade_history_entry",
qty: trade_entry.trade.qty,
security: trade_entry.trade.security.ticker,
price: trade_entry.trade.price_money.format
) %></p>
</div>
</li>
<% end %>
</ul>
<% else %>
<p class="text-secondary">No trade history available for this holding.</p>
<% end %>
</div>
</div>
</details>
<% unless @holding.account.plaid_account_id.present? %>
<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-secondary bg-gray-25 focus-visible:outline-hidden">
<h4><%= t(".settings") %></h4>
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-secondary w-5" %>
</summary>
<div class="pb-4">
<div class="flex items-center justify-between gap-2 p-3">
<div class="text-sm space-y-1">
<h4 class="text-primary"><%= t(".delete_title") %></h4>
<p class="text-secondary"><%= t(".delete_subtitle") %></p>
</div>
<%= button_to t(".delete"),
holding_path(@holding),
method: :delete,
class: "rounded-lg px-3 py-2 text-red-500 text-sm font-medium border border-secondary",
data: { turbo_confirm: true } %>
</div>
</div>
</details>
<% end %>
</div>
<% end %>