mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 15:35:22 +02:00
Centralize all disclosure instances
This commit is contained in:
parent
db3953825f
commit
779ca08d95
18 changed files with 86 additions and 145 deletions
25
app/components/disclosure_component.html.erb
Normal file
25
app/components/disclosure_component.html.erb
Normal file
|
@ -0,0 +1,25 @@
|
|||
<details class="group" <%= "open" if open %>>
|
||||
<%= tag.summary class: class_names(
|
||||
"px-3 py-2 rounded-xl cursor-pointer flex items-center justify-between bg-surface"
|
||||
) do %>
|
||||
<div class="flex items-center gap-3">
|
||||
<% if align == :left %>
|
||||
<%= lucide_icon "chevron-right", class: "fg-gray w-5 h-5 group-open:transform group-open:rotate-90" %>
|
||||
<% end %>
|
||||
|
||||
<%= tag.span class: class_names("font-medium", align == :left ? "text-sm text-primary" : "text-xs uppercase text-secondary") do %>
|
||||
<%= title %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if align == :right %>
|
||||
<%= lucide_icon "chevron-down", class: "fg-gray w-5 h-5 group-open:transform group-open:rotate-180" %>
|
||||
<% elsif summary_content? %>
|
||||
<%= summary_content %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<div class="mt-2">
|
||||
<%= content %>
|
||||
</div>
|
||||
</details>
|
12
app/components/disclosure_component.rb
Normal file
12
app/components/disclosure_component.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class DisclosureComponent < ViewComponent::Base
|
||||
renders_one :summary_content
|
||||
|
||||
attr_reader :title, :align, :open, :opts
|
||||
|
||||
def initialize(title:, align: "right", open: false, **opts)
|
||||
@title = title
|
||||
@align = align.to_sym
|
||||
@open = open
|
||||
@opts = opts
|
||||
end
|
||||
end
|
|
@ -70,11 +70,6 @@ module ApplicationHelper
|
|||
render partial: "shared/drawer", locals: { content:, reload_on_close: }
|
||||
end
|
||||
|
||||
def disclosure(title, default_open: true, &block)
|
||||
content = capture &block
|
||||
render partial: "shared/disclosure", locals: { title: title, content: content, open: default_open }
|
||||
end
|
||||
|
||||
def page_active?(path)
|
||||
current_page?(path) || (request.path.start_with?(path) && path != "/")
|
||||
end
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import { Controller } from "@hotwired/stimulus";
|
||||
|
||||
// Connects to data-controller="account-collapse"
|
||||
export default class extends Controller {
|
||||
static values = { type: String };
|
||||
initialToggle = false;
|
||||
STORAGE_NAME = "accountCollapseStates";
|
||||
|
||||
connect() {
|
||||
this.element.addEventListener("toggle", this.onToggle);
|
||||
this.updateFromLocalStorage();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.element.removeEventListener("toggle", this.onToggle);
|
||||
}
|
||||
|
||||
onToggle = () => {
|
||||
if (this.initialToggle) {
|
||||
this.initialToggle = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const items = this.getItemsFromLocalStorage();
|
||||
if (items.has(this.typeValue)) {
|
||||
items.delete(this.typeValue);
|
||||
} else {
|
||||
items.add(this.typeValue);
|
||||
}
|
||||
localStorage.setItem(this.STORAGE_NAME, JSON.stringify([...items]));
|
||||
};
|
||||
|
||||
updateFromLocalStorage() {
|
||||
const items = this.getItemsFromLocalStorage();
|
||||
|
||||
if (items.has(this.typeValue)) {
|
||||
this.initialToggle = true;
|
||||
this.element.setAttribute("open", "");
|
||||
}
|
||||
}
|
||||
|
||||
getItemsFromLocalStorage() {
|
||||
try {
|
||||
const items = localStorage.getItem(this.STORAGE_NAME);
|
||||
return new Set(items ? JSON.parse(items) : []);
|
||||
} catch (error) {
|
||||
console.error("Error parsing items from localStorage:", error);
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@
|
|||
class: "justify-start"
|
||||
) %>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<% family.balance_sheet.account_groups("asset").each do |group| %>
|
||||
<%= render "accounts/accountable_group", account_group: group %>
|
||||
<% end %>
|
||||
|
@ -60,7 +60,7 @@
|
|||
class: "justify-start"
|
||||
) %>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<% family.balance_sheet.account_groups("liability").each do |group| %>
|
||||
<%= render "accounts/accountable_group", account_group: group %>
|
||||
<% end %>
|
||||
|
@ -80,7 +80,7 @@
|
|||
class: "justify-start"
|
||||
) %>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div>
|
||||
<% family.balance_sheet.account_groups.each do |group| %>
|
||||
<%= render "accounts/accountable_group", account_group: group %>
|
||||
<% end %>
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
<%# locals: (account_group:) %>
|
||||
|
||||
<details class="group" data-controller="account-collapse" data-account-collapse-type-value="<%= account_group.key %>">
|
||||
<summary class="px-3 py-2 flex items-center gap-3 cursor-pointer h-10 mb-1">
|
||||
<%= icon("chevron-right", class: "group-open:rotate-90") %>
|
||||
|
||||
<%= tag.p account_group.name, class: "text-sm font-medium" %>
|
||||
|
||||
<%= render DisclosureComponent.new(title: account_group.name, align: :left) do |disclosure| %>
|
||||
<% disclosure.with_summary_content do %>
|
||||
<div class="ml-auto text-right grow">
|
||||
<%= tag.p format_money(account_group.total_money), class: "text-sm font-medium text-primary" %>
|
||||
|
||||
|
@ -15,7 +11,7 @@
|
|||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</summary>
|
||||
<% end %>
|
||||
|
||||
<div class="space-y-1">
|
||||
<% account_group.accounts.each do |account| %>
|
||||
|
@ -49,4 +45,4 @@
|
|||
frame: :modal,
|
||||
class: "justify-start"
|
||||
) %>
|
||||
</details>
|
||||
<% end %>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<% else %>
|
||||
<div class="w-8 h-8 group-hover:scale-105 transition-all duration-300 rounded-full flex justify-center items-center" style="<%= mixed_hex_styles(budget_category.category.color) %>">
|
||||
<% if budget_category.category.lucide_icon %>
|
||||
<%= icon(budget_category.category.lucide_icon) %>
|
||||
<%= icon(budget_category.category.lucide_icon, color: "current") %>
|
||||
<% else %>
|
||||
<%= render "shared/circle_logo", name: budget_category.category.name, hex: budget_category.category.color %>
|
||||
<% end %>
|
||||
|
|
|
@ -26,13 +26,7 @@
|
|||
<% end %>
|
||||
</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>Overview</h4>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
|
||||
<%= render DisclosureComponent.new(title: "Overview", open: true) do %>
|
||||
<div class="pb-4">
|
||||
<dl class="space-y-3 px-3 py-2">
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
|
@ -91,15 +85,9 @@
|
|||
</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>Recent Transactions</h4>
|
||||
<%= icon "chevron-down", size: "sm", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
<% end %>
|
||||
|
||||
<%= render DisclosureComponent.new(title: "Recent Transactions", open: true) do %>
|
||||
<div class="space-y-2">
|
||||
<div class="px-3 py-4 space-y-2">
|
||||
<% if @recent_transactions.any? %>
|
||||
|
@ -149,6 +137,6 @@
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -9,12 +9,7 @@
|
|||
<%= 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>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
|
||||
<%= render DisclosureComponent.new(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<dl class="space-y-3 px-3 py-2">
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
|
@ -45,14 +40,9 @@
|
|||
</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>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
<% end %>
|
||||
|
||||
<%= render DisclosureComponent.new(title: t(".history"), open: true) do %>
|
||||
<div class="space-y-2">
|
||||
<div class="px-3 py-4">
|
||||
<% if @holding.trades.any? %>
|
||||
|
@ -85,15 +75,10 @@
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
|
||||
<% 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>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
|
||||
<%= render DisclosureComponent.new(title: t(".settings"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<div class="flex items-center justify-between gap-2 p-3">
|
||||
<div class="text-sm space-y-1">
|
||||
|
@ -108,7 +93,7 @@
|
|||
data: { turbo_confirm: true } %>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<%# locals: (title:, content:, open: true) %>
|
||||
|
||||
<details class="group space-y-2" <%= "open" if open %>>
|
||||
<summary class="flex list-none items-center justify-between rounded-xl px-3 py-2 text-xs font-medium
|
||||
uppercase text-secondary bg-surface focus-visible:outline-hidden">
|
||||
<h3><%= title %></h3>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
|
||||
<%= content %>
|
||||
</details>
|
|
@ -26,7 +26,7 @@
|
|||
<% trade = entry.trade %>
|
||||
|
||||
<div class="mb-2">
|
||||
<%= disclosure t(".overview") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<dl class="space-y-3 px-3 py-2">
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<!-- Details Section -->
|
||||
<%= disclosure t(".details") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".details")) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: @entry,
|
||||
url: trade_path(@entry),
|
||||
|
@ -43,7 +43,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Additional Section -->
|
||||
<%= disclosure t(".additional") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".additional")) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: @entry,
|
||||
url: trade_path(@entry),
|
||||
|
@ -59,7 +59,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Settings Section -->
|
||||
<%= disclosure t(".settings") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".settings")) do %>
|
||||
<div class="pb-4">
|
||||
<!-- Exclude Trade Form -->
|
||||
<%= styled_form_with model: @entry,
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<%= f.date_field :date, label: t(".date"), required: true, min: Entry.min_supported_date, max: Date.current, value: Date.current %>
|
||||
</section>
|
||||
|
||||
<%= disclosure t(".details"), default_open: false do %>
|
||||
<%= render DisclosureComponent.new(title: t(".details")) do %>
|
||||
<%= f.fields_for :entryable do |ef| %>
|
||||
<%= ef.select :tag_ids,
|
||||
Current.family.tags.alphabetically.pluck(:name, :id),
|
||||
|
|
|
@ -18,30 +18,20 @@
|
|||
</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-secondary bg-gray-25 focus-visible:outline-hidden">
|
||||
<h4>Overview</h4>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
|
||||
<%= render DisclosureComponent.new(title: "Overview", open: true) do %>
|
||||
<div class="pb-6 space-y-2">
|
||||
<%= form.date_field :date, label: "Date", 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-secondary bg-gray-25 focus-visible:outline-hidden">
|
||||
<h4>Details</h4>
|
||||
<%= icon "chevron-down", class: "group-open:transform group-open:rotate-180" %>
|
||||
</summary>
|
||||
<% end %>
|
||||
|
||||
<%= render DisclosureComponent.new(title: "Details", open: true) do %>
|
||||
<div class="space-y-2">
|
||||
<%= form.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: "Select a category", label: "Category", class: "text-subdued" } %>
|
||||
<%= form.collection_select :merchant_id, Current.family.merchants.alphabetically, :id, :name, { prompt: "Select a merchant", label: "Merchant", class: "text-subdued" } %>
|
||||
<%= form.select :tag_ids, Current.family.tags.alphabetically.pluck(:name, :id), { include_blank: "None", multiple: true, label: "Tags", container_class: "h-40" } %>
|
||||
<%= form.text_area :notes, label: "Notes", placeholder: "Enter a note that will be applied to selected transactions", rows: 5 %>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<!-- Overview Section -->
|
||||
<%= disclosure t(".overview") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: @entry,
|
||||
url: transaction_path(@entry),
|
||||
|
@ -48,7 +48,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Details Section -->
|
||||
<%= disclosure t(".details"), default_open: false do %>
|
||||
<%= render DisclosureComponent.new(title: t(".details")) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: @entry,
|
||||
url: transaction_path(@entry),
|
||||
|
@ -96,9 +96,8 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Settings Section -->
|
||||
<%= disclosure t(".settings") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".settings")) do %>
|
||||
<div class="pb-4">
|
||||
|
||||
<%= styled_form_with model: @entry,
|
||||
url: transaction_path(@entry),
|
||||
class: "p-3",
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<!-- Overview Section -->
|
||||
<%= disclosure t(".overview") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4 px-3 pt-2 text-sm space-y-3 text-primary">
|
||||
<div class="space-y-3">
|
||||
<dl class="flex items-center gap-2 justify-between">
|
||||
|
@ -68,7 +68,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Details Section -->
|
||||
<%= disclosure t(".details") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".details")) do %>
|
||||
<%= styled_form_with model: @transfer,
|
||||
data: { controller: "auto-submit-form" }, class: "space-y-2" do |f| %>
|
||||
<% if @transfer.categorizable? %>
|
||||
|
@ -84,7 +84,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Settings Section -->
|
||||
<%= disclosure t(".settings") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".settings")) do %>
|
||||
<div class="pb-4">
|
||||
<div class="flex items-center justify-between gap-2 p-3">
|
||||
<div class="text-sm space-y-1">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<!-- Overview Section -->
|
||||
<%= disclosure t(".overview") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: entry,
|
||||
url: entry_path(entry),
|
||||
|
@ -30,7 +30,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Details Section -->
|
||||
<%= disclosure t(".details") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".details")) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: entry,
|
||||
url: entry_path(entry),
|
||||
|
@ -46,7 +46,7 @@
|
|||
<% end %>
|
||||
|
||||
<!-- Settings Section -->
|
||||
<%= disclosure t(".settings") do %>
|
||||
<%= render DisclosureComponent.new(title: t(".settings")) do %>
|
||||
<div class="pb-4">
|
||||
<!-- Delete Valuation Form -->
|
||||
<div class="flex items-center justify-between gap-2 p-3">
|
||||
|
|
13
test/components/previews/disclosure_component_preview.rb
Normal file
13
test/components/previews/disclosure_component_preview.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
class DisclosureComponentPreview < ViewComponent::Preview
|
||||
# @display container_classes max-w-[400px]
|
||||
# @param align select ["left", "right"]
|
||||
def default(align: "right")
|
||||
render DisclosureComponent.new(title: "Title", align: align, open: true) do |disclosure|
|
||||
disclosure.with_summary_content do
|
||||
content_tag(:p, "$200.25", class: "text-xs font-mono font-medium")
|
||||
end
|
||||
|
||||
content_tag(:p, "Sample disclosure content", class: "text-sm")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue