mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-24 07:39:39 +02:00
Add sync status and errors to account settings page (#1169)
This commit is contained in:
parent
86741401c3
commit
b9341ac302
13 changed files with 124 additions and 38 deletions
|
@ -102,6 +102,10 @@
|
||||||
.btn--primary {
|
.btn--primary {
|
||||||
@apply bg-gray-900 text-white hover:bg-gray-700;
|
@apply bg-gray-900 text-white hover:bg-gray-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn--light {
|
||||||
|
@apply bg-gray-25 border border-alpha-black-200 text-gray-900 hover:bg-gray-50;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Small, single purpose classes that should take precedence over other styles */
|
/* Small, single purpose classes that should take precedence over other styles */
|
||||||
|
|
|
@ -23,6 +23,11 @@ class InstitutionsController < ApplicationController
|
||||||
redirect_to accounts_path, notice: t(".success")
|
redirect_to accounts_path, notice: t(".success")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sync
|
||||||
|
@institution.sync
|
||||||
|
redirect_back_or_to accounts_path, notice: t(".success")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def institution_params
|
def institution_params
|
||||||
|
|
|
@ -4,4 +4,22 @@ class Institution < ApplicationRecord
|
||||||
has_one_attached :logo
|
has_one_attached :logo
|
||||||
|
|
||||||
scope :alphabetically, -> { order(name: :asc) }
|
scope :alphabetically, -> { order(name: :asc) }
|
||||||
|
|
||||||
|
def sync
|
||||||
|
accounts.active.each do |account|
|
||||||
|
if account.needs_sync?
|
||||||
|
account.sync
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
update! last_synced_at: Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def syncing?
|
||||||
|
accounts.active.any? { |account| account.syncing? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_issues?
|
||||||
|
accounts.active.any? { |account| account.has_issues? }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,17 @@
|
||||||
<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" %>">
|
<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 %>
|
<%= account.name[0].upcase %>
|
||||||
</div>
|
</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" } %>
|
|
||||||
|
<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" } %>
|
||||||
|
<% if account.has_issues? %>
|
||||||
|
<div class="text-sm flex items-center gap-1 text-error">
|
||||||
|
<%= lucide_icon "alert-octagon", class: "shrink-0 w-4 h-4" %>
|
||||||
|
<%= tag.span t(".has_issues") %>
|
||||||
|
<%= link_to t(".troubleshoot"), issue_path(account.issues.first), class: "underline", data: { turbo_frame: :drawer } %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= link_to edit_account_path(account), data: { turbo_frame: :modal }, class: "group-hover/account:flex hidden hover:opacity-80 items-center justify-center" do %>
|
<%= 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" %>
|
<%= lucide_icon "pencil-line", class: "w-4 h-4 text-gray-500" %>
|
||||||
|
|
|
@ -1,40 +1,62 @@
|
||||||
<%# locals: (institution:) %>
|
<%# locals: (institution:) %>
|
||||||
|
|
||||||
<details open class="group bg-white p-4 border border-alpha-black-25 shadow-xs rounded-xl">
|
<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">
|
<summary class="flex items-center justify-between 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 gap-2">
|
||||||
|
<%= 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">
|
<div class="flex items-center justify-center h-8 w-8 bg-blue-600/10 rounded-full bg-black/5">
|
||||||
<% if institution_logo(institution) %>
|
<% if institution_logo(institution) %>
|
||||||
<%= image_tag institution_logo(institution), class: "rounded-full h-full w-full" %>
|
<%= image_tag institution_logo(institution), class: "rounded-full h-full w-full" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<%= tag.p institution.name.first.upcase, class: "text-blue-600 text-xs font-medium" %>
|
<%= tag.p institution.name.first.upcase, class: "text-blue-600 text-xs font-medium" %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pl-1 text-sm">
|
||||||
|
<%= link_to institution.name, edit_institution_path(institution), data: { turbo_frame: :modal }, class: "font-medium text-gray-900 hover:underline" %>
|
||||||
|
<% if institution.has_issues? %>
|
||||||
|
<div class="flex items-center gap-1 text-error">
|
||||||
|
<%= lucide_icon "alert-octagon", class: "shrink-0 w-4 h-4" %>
|
||||||
|
<%= tag.span t(".has_issues") %>
|
||||||
|
</div>
|
||||||
|
<% elsif institution.syncing? %>
|
||||||
|
<div class="text-gray-500 flex items-center gap-1">
|
||||||
|
<%= lucide_icon "loader", class: "w-4 h-4 animate-pulse" %>
|
||||||
|
<%= tag.span t(".syncing") %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<p class="text-gray-500"><%= institution.last_synced_at ? t(".status", last_synced_at: time_ago_in_words(institution.last_synced_at)) : t(".status_never") %></p>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</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" %>
|
<div class="flex items-center gap-2">
|
||||||
|
<%= button_to sync_institution_path(institution), method: :post, class: "text-gray-900 flex hover:text-gray-800 items-center text-sm font-medium hover:underline" do %>
|
||||||
|
<%= lucide_icon "refresh-cw", class: "w-4 h-4" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= contextual_menu do %>
|
<%= 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">
|
<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),
|
<%= 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",
|
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 %>
|
data: { turbo_frame: :modal } do %>
|
||||||
<%= lucide_icon "plus", class: "w-5 h-5 text-gray-500" %>
|
<%= lucide_icon "plus", class: "w-5 h-5 text-gray-500" %>
|
||||||
|
|
||||||
<span><%= t(".add_account_to_institution") %></span>
|
<span><%= t(".add_account_to_institution") %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= link_to edit_institution_path(institution),
|
<%= 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",
|
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 %>
|
data: { turbo_frame: :modal } do %>
|
||||||
<%= lucide_icon "pencil-line", class: "w-5 h-5 text-gray-500" %>
|
<%= lucide_icon "pencil-line", class: "w-5 h-5 text-gray-500" %>
|
||||||
|
|
||||||
<span><%= t(".edit") %></span>
|
<span><%= t(".edit") %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= button_to institution_path(institution),
|
<%= button_to institution_path(institution),
|
||||||
method: :delete,
|
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",
|
class: "block w-full py-2 px-3 space-x-2 text-red-600 hover:bg-red-50 flex items-center rounded-lg",
|
||||||
data: {
|
data: {
|
||||||
|
@ -44,13 +66,13 @@
|
||||||
accept: t(".confirm_accept")
|
accept: t(".confirm_accept")
|
||||||
}
|
}
|
||||||
} do %>
|
} do %>
|
||||||
<%= lucide_icon "trash-2", class: "w-5 h-5" %>
|
<%= lucide_icon "trash-2", class: "w-5 h-5" %>
|
||||||
|
|
||||||
<span><%= t(".delete") %></span>
|
<span><%= t(".delete") %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
</div>
|
||||||
</summary>
|
</summary>
|
||||||
|
|
||||||
<div class="space-y-4 mt-4">
|
<div class="space-y-4 mt-4">
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<%= button_to sync_all_accounts_path, method: :post, class: "rounded-lg bg-gray-900 text-white flex items-center gap-1 justify-center hover:bg-gray-700 px-3 py-2", title: "Sync All" do %>
|
|
||||||
<%= lucide_icon "refresh-cw", class: "w-5 h-5" %>
|
|
||||||
<% end %>
|
|
|
@ -18,14 +18,17 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<%= button_to sync_all_accounts_path, class: "btn btn--light flex items-center gap-2", title: "Sync All" do %>
|
||||||
|
<%= lucide_icon "refresh-cw", class: "w-5 h-5" %>
|
||||||
|
<span><%= t(".sync_all") %></span>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<%= link_to new_account_path,
|
<%= link_to new_account_path,
|
||||||
data: { turbo_frame: "modal" },
|
data: { turbo_frame: "modal" },
|
||||||
class: "rounded-lg bg-gray-900 text-white flex items-center gap-1 justify-center hover:bg-gray-700 px-3 py-2" do %>
|
class: "btn btn--primary flex items-center gap-1" do %>
|
||||||
<%= lucide_icon("plus", class: "w-5 h-5") %>
|
<%= lucide_icon("plus", class: "w-5 h-5") %>
|
||||||
<p class="text-sm font-medium"><%= t(".new_account") %></p>
|
<p class="text-sm font-medium"><%= t(".new_account") %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "sync_all_button" %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
@ -38,7 +41,9 @@
|
||||||
<%= render "institution_accounts", institution: %>
|
<%= render "institution_accounts", institution: %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render "institutionless_accounts", accounts: @accounts %>
|
<% if @accounts.any? %>
|
||||||
|
<%= render "institutionless_accounts", accounts: @accounts %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
accounts:
|
accounts:
|
||||||
|
account:
|
||||||
|
has_issues: Issue detected.
|
||||||
|
troubleshoot: Troubleshoot
|
||||||
accountables:
|
accountables:
|
||||||
property:
|
property:
|
||||||
area_unit: Area unit
|
area_unit: Area unit
|
||||||
|
@ -63,6 +66,7 @@ en:
|
||||||
accounts: Accounts
|
accounts: Accounts
|
||||||
add_institution: Add institution
|
add_institution: Add institution
|
||||||
new_account: New account
|
new_account: New account
|
||||||
|
sync_all: Sync all
|
||||||
institution_accounts:
|
institution_accounts:
|
||||||
add_account_to_institution: Add new account
|
add_account_to_institution: Add new account
|
||||||
confirm_accept: Delete institution
|
confirm_accept: Delete institution
|
||||||
|
@ -72,7 +76,11 @@ en:
|
||||||
confirm_title: Delete financial institution?
|
confirm_title: Delete financial institution?
|
||||||
delete: Delete institution
|
delete: Delete institution
|
||||||
edit: Edit institution
|
edit: Edit institution
|
||||||
|
has_issues: Issue detected, see accounts
|
||||||
new_account: Add account
|
new_account: Add account
|
||||||
|
status: Last synced %{last_synced_at} ago
|
||||||
|
status_never: Requires data sync
|
||||||
|
syncing: Syncing...
|
||||||
institutionless_accounts:
|
institutionless_accounts:
|
||||||
other_accounts: Other accounts
|
other_accounts: Other accounts
|
||||||
new:
|
new:
|
||||||
|
|
|
@ -11,5 +11,7 @@ en:
|
||||||
name: Financial institution name
|
name: Financial institution name
|
||||||
new:
|
new:
|
||||||
new_institution: New financial institution
|
new_institution: New financial institution
|
||||||
|
sync:
|
||||||
|
success: Institution sync started
|
||||||
update:
|
update:
|
||||||
success: Institution updated
|
success: Institution updated
|
||||||
|
|
|
@ -95,7 +95,9 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :institutions, except: %i[index show]
|
resources :institutions, except: %i[index show] do
|
||||||
|
post :sync, on: :member
|
||||||
|
end
|
||||||
resources :invite_codes, only: %i[index create]
|
resources :invite_codes, only: %i[index create]
|
||||||
|
|
||||||
resources :issues, only: :show
|
resources :issues, only: :show
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddLastSyncedAtInstitution < ActiveRecord::Migration[7.2]
|
||||||
|
def change
|
||||||
|
add_column :institutions, :last_synced_at, :datetime
|
||||||
|
end
|
||||||
|
end
|
3
db/schema.rb
generated
3
db/schema.rb
generated
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.2].define(version: 2024_08_23_125526) do
|
ActiveRecord::Schema[7.2].define(version: 2024_09_11_143158) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -318,6 +318,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_08_23_125526) do
|
||||||
t.uuid "family_id", null: false
|
t.uuid "family_id", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
|
t.datetime "last_synced_at"
|
||||||
t.index ["family_id"], name: "index_institutions_on_family_id"
|
t.index ["family_id"], name: "index_institutions_on_family_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -52,4 +52,11 @@ class InstitutionsControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_redirected_to accounts_url
|
assert_redirected_to accounts_url
|
||||||
assert_equal "Institution deleted", flash[:notice]
|
assert_equal "Institution deleted", flash[:notice]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "can sync institution" do
|
||||||
|
post sync_institution_url(@institution)
|
||||||
|
|
||||||
|
assert_redirected_to accounts_url
|
||||||
|
assert_equal "Institution sync started", flash[:notice]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue