Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add institution management and account editing controls #868

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions app/controllers/accounts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ class AccountsController < ApplicationController
layout "with_sidebar"

include Filterable
before_action :set_account, only: %i[ show destroy sync update ]
before_action :set_account, only: %i[ edit show destroy sync update ]
after_action :sync_account, only: :create

def index
@accounts = Current.family.accounts
@institutions = Current.family.institutions
@accounts = Current.family.accounts.ungrouped.alphabetically
end

def summary
Expand All @@ -26,13 +27,20 @@ def new
balance: nil,
accountable: Accountable.from_type(params[:type])&.new
)

if params[:institution_id]
@account.institution = Current.family.institutions.find_by(id: params[:institution_id])
end
end

def show
@balance_series = @account.series(period: @period)
@valuation_series = @account.valuations.to_series
end

def edit
end

def update
@account.update! account_params.except(:accountable_type)
redirect_back_or_to account_path(@account), notice: t(".success")
Expand All @@ -46,7 +54,7 @@ def create
start_date: account_params[:start_date],
start_balance: account_params[:start_balance]

redirect_to account_path(@account), notice: t(".success")
redirect_back_or_to account_path(@account), notice: t(".success")
end

def destroy
Expand Down Expand Up @@ -80,7 +88,7 @@ def set_account
end

def account_params
params.require(:account).permit(:name, :accountable_type, :balance, :start_date, :start_balance, :currency, :subtype, :is_active)
params.require(:account).permit(:name, :accountable_type, :balance, :start_date, :start_balance, :currency, :subtype, :is_active, :institution_id)
end

def sync_account
Expand Down
35 changes: 35 additions & 0 deletions app/controllers/institutions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class InstitutionsController < ApplicationController
before_action :set_institution, except: %i[ new create ]

def new
@institution = Institution.new
end

def create
Current.family.institutions.create!(institution_params)
redirect_to accounts_path, notice: t(".success")
end

def edit
end

def update
@institution.update!(institution_params)
redirect_to accounts_path, notice: t(".success")
end

def destroy
@institution.destroy!
redirect_to accounts_path, notice: t(".success")
end

private

def institution_params
params.require(:institution).permit(:name, :logo)
end

def set_institution
@institution = Current.family.institutions.find(params[:id])
end
end
5 changes: 5 additions & 0 deletions app/helpers/institutions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module InstitutionsHelper
def institution_logo(institution)
institution.logo.attached? ? institution.logo : institution.logo_url
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default class extends Controller {
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
this.imagePreviewTarget.innerHTML = `<img src="${e.target.result}" alt="Preview" class="w-24 h-24 rounded-full object-cover" />`;
this.imagePreviewTarget.innerHTML = `<img src="${e.target.result}" alt="Preview" class="w-full h-full rounded-full object-cover" />`;
this.templateTarget.classList.add("hidden");
this.clearBtnTarget.classList.remove("hidden");
};
Expand Down
5 changes: 4 additions & 1 deletion app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ class Account < ApplicationRecord
include Syncable
include Monetizable

broadcasts_refreshes

validates :family, presence: true

broadcasts_refreshes
belongs_to :family
belongs_to :institution, optional: true
has_many :balances, dependent: :destroy
has_many :valuations, dependent: :destroy
has_many :transactions, dependent: :destroy
Expand All @@ -19,6 +21,7 @@ class Account < ApplicationRecord
scope :assets, -> { where(classification: "asset") }
scope :liabilities, -> { where(classification: "liability") }
scope :alphabetically, -> { order(:name) }
scope :ungrouped, -> { where(institution_id: nil) }

delegated_type :accountable, types: Accountable::TYPES, dependent: :destroy

Expand Down
1 change: 1 addition & 0 deletions app/models/family.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ class Family < ApplicationRecord
has_many :users, dependent: :destroy
has_many :tags, dependent: :destroy
has_many :accounts, dependent: :destroy
has_many :institutions, dependent: :destroy
has_many :transactions, through: :accounts
has_many :imports, through: :accounts
has_many :transaction_categories, dependent: :destroy, class_name: "Transaction::Category"
Expand Down
7 changes: 7 additions & 0 deletions app/models/institution.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Institution < ApplicationRecord
belongs_to :family
has_many :accounts, dependent: :nullify
has_one_attached :logo

scope :alphabetically, -> { order(name: :asc) }
end
6 changes: 5 additions & 1 deletion app/views/accounts/_account.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<%= turbo_frame_tag dom_id(account) do %>
<div class="p-4 flex items-center justify-between gap-3">
<div class="p-4 flex items-center justify-between gap-3 group/account">
<div class="flex items-center gap-3">
<div class="w-8 h-8 flex items-center justify-center rounded-full text-xs font-medium <%= account.is_active ? "bg-blue-500/10 text-blue-500" : "bg-gray-500/10 text-gray-500" %>">
<%= account.name[0].upcase %>
</div>
<%= link_to account.name, account, class: [(account.is_active ? "text-gray-900" : "text-gray-400"), "text-sm font-medium hover:underline"], data: { turbo_frame: "_top" } %>

<%= link_to edit_account_path(account), data: { turbo_frame: :modal }, class: "group-hover/account:flex hidden hover:opacity-80 items-center justify-center" do %>
<%= lucide_icon "pencil-line", class: "w-4 h-4 text-gray-500" %>
<% end %>
</div>
<div class="flex items-center gap-8">
<p class="text-sm font-medium <%= account.is_active ? "text-gray-900" : "text-gray-400" %>">
Expand Down
2 changes: 1 addition & 1 deletion app/views/accounts/_account_type.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<%= link_to new_account_path(step: "method", type: type.class.name.demodulize), class: "flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-25 border border-transparent focus:border focus:border-gray-200 block px-2 hover:bg-gray-25 rounded-lg p-2" do %>
<%= link_to new_account_path(step: "method", type: type.class.name.demodulize, institution_id: params[:institution_id]), class: "flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-25 border border-transparent focus:border focus:border-gray-200 block px-2 hover:bg-gray-25 rounded-lg p-2" do %>
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg <%= bg_color %> border border-alpha-black-25">
<%= lucide_icon(icon, class: "#{text_color} w-5 h-5") %>
</span>
Expand Down
17 changes: 17 additions & 0 deletions app/views/accounts/_accountable_group.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<%# locals: (accounts:) %>

<% accounts.group_by(&:accountable_type).each do |group, accounts| %>
<div class="bg-gray-25 p-1 rounded-xl">
<div class="flex items-center px-4 py-2 text-xs font-medium text-gray-500">
<p><%= to_accountable_title(Accountable.from_type(group)) %></p>
<span class="text-gray-400 mx-2">&middot;</span>
<p><%= accounts.count %></p>
<p class="ml-auto"><%= format_money accounts.sum(&:balance_money) %></p>
</div>
<div class="bg-white">
<% accounts.each do |account| %>
<%= render account %>
<% end %>
</div>
</div>
<% end %>
11 changes: 11 additions & 0 deletions app/views/accounts/_empty.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div class="flex justify-center items-center h-[800px] text-sm">
<div class="text-center flex flex-col items-center max-w-[300px]">
<%= tag.p t(".no_accounts"), class: "text-gray-900 mb-1 font-medium" %>
<%= tag.p t(".empty_message"), class: "text-gray-500 mb-4" %>

<%= link_to new_account_path, class: "w-fit flex text-white text-sm font-medium items-center gap-1 bg-gray-900 rounded-lg p-2 pr-3", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5") %>
<span><%= t(".new_account") %></span>
<% end %>
</div>
</div>
2 changes: 1 addition & 1 deletion app/views/accounts/_entry_method.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<%= text %>
</span>
<% else %>
<%= link_to new_account_path(type: type.class.name.demodulize), class: "flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2" do %>
<%= link_to new_account_path(type: type.class.name.demodulize, institution_id: params[:institution_id]), class: "flex items-center gap-4 w-full text-center focus:outline-none focus:bg-gray-50 border border-transparent focus:border focus:border-gray-200 px-2 hover:bg-gray-50 rounded-lg p-2" do %>
<span class="flex w-8 h-8 shrink-0 grow-0 items-center justify-center rounded-lg bg-alpha-black-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.02)]">
<%= lucide_icon(icon, class: "text-gray-500 w-5 h-5") %>
</span>
Expand Down
69 changes: 69 additions & 0 deletions app/views/accounts/_institution_accounts.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<%# locals: (institution:) %>

<details open class="group bg-white p-4 border border-alpha-black-25 shadow-xs rounded-xl">
<summary class="flex items-center gap-2 focus-visible:outline-none">
<%= lucide_icon "chevron-right", class: "group-open:transform group-open:rotate-90 text-gray-500 w-5" %>

<div class="flex items-center justify-center h-8 w-8 bg-blue-600/10 rounded-full bg-black/5">
<% if institution_logo(institution) %>
<%= image_tag institution_logo(institution), class: "rounded-full h-full w-full" %>
<% else %>
<div class="flex items-center justify-center">
<%= tag.p institution.name.first.upcase, class: "text-blue-600 text-xs font-medium" %>
</div>
<% end %>
</div>

<%= link_to institution.name, edit_institution_path(institution), data: { turbo_frame: :modal }, class: "text-sm font-medium text-gray-900 ml-1 mr-auto hover:underline" %>

<%= 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">
<%= link_to new_account_path(institution_id: institution.id),
class: "block w-full py-2 px-3 space-x-2 text-gray-900 hover:bg-gray-50 flex items-center rounded-lg",
data: { turbo_frame: :modal } do %>
<%= lucide_icon "plus", class: "w-5 h-5 text-gray-500" %>

<span><%= t(".add_account_to_institution") %></span>
<% end %>

<%= link_to edit_institution_path(institution),
class: "block w-full py-2 px-3 space-x-2 text-gray-900 hover:bg-gray-50 flex items-center rounded-lg",
data: { turbo_frame: :modal } do %>
<%= lucide_icon "pencil-line", class: "w-5 h-5 text-gray-500" %>

<span><%= t(".edit") %></span>
<% end %>

<%= button_to institution_path(institution),
method: :delete,
class: "block w-full py-2 px-3 space-x-2 text-red-600 hover:bg-red-50 flex items-center rounded-lg",
data: {
turbo_confirm: {
title: t(".confirm_title"),
body: t(".confirm_body"),
accept: t(".confirm_accept")
}
} do %>
<%= lucide_icon "trash-2", class: "w-5 h-5" %>

<span><%= t(".delete") %></span>
<% end %>
</div>

<% end %>
</summary>

<div class="space-y-4 mt-4">
<% if institution.accounts.any? %>
<%= render "accountable_group", accounts: institution.accounts %>
<% else %>
<div class="p-4 flex flex-col gap-3 items-center justify-center">
<p class="text-gray-500 text-sm">There are no accounts in this financial institution</p>
<%= link_to new_account_path(institution_id: institution.id), class: "w-fit flex text-white text-sm font-medium items-center gap-1 bg-gray-900 rounded-lg p-1.5 pr-2", data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-4 h-4") %>
<span><%= t(".new_account") %></span>
<% end %>
</div>
<% end %>
</div>
</details>
17 changes: 17 additions & 0 deletions app/views/accounts/_institutionless_accounts.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<%# locals: (accounts:) %>

<details open class="group bg-white p-4 border border-alpha-black-25 shadow-xs rounded-xl">
<summary class="flex items-center gap-2 focus-visible:outline-none">
<%= lucide_icon "chevron-right", class: "group-open:transform group-open:rotate-90 text-gray-500 w-5" %>

<div class="flex items-center justify-center h-8 w-8 rounded-full bg-black/5">
<%= lucide_icon("folder-pen", class: "w-5 h-5 text-gray-500") %>
</div>

<span class="mr-auto text-sm font-medium text-gray-900"><%= t(".other_accounts") %></span>
</summary>

<div class="space-y-4 mt-4">
<%= render "accountable_group", accounts: accounts %>
</div>
</details>
21 changes: 21 additions & 0 deletions app/views/accounts/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<%= modal do %>
<article class="mx-auto w-full p-4 space-y-4 min-w-[350px]">
<header class="flex justify-between">
<h2 class="font-medium text-xl"><%= t(".edit", account: @account.name) %></h2>
<%= lucide_icon "x", class: "w-5 h-5 text-gray-500", data: { action: "click->modal#close" } %>
</header>

<%= form_with model: @account, data: { turbo_frame: "_top" } do |f| %>
<%= f.text_field :name, label: "Name" %>

<div class="relative">
<%= f.collection_select :institution_id, Current.family.institutions.alphabetically, :id, :name, { include_blank: t(".ungrouped"), label: t(".institution") } %>
<%= link_to new_institution_path do %>
<%= lucide_icon "plus", class: "text-gray-700 hover:text-gray-500 w-4 h-4 absolute right-3 top-2" %>
<% end %>
</div>

<%= f.submit %>
<% end %>
</article>
<% end %>
Loading