Add a tab to list customer payment on the account page

This commit is contained in:
Gaetan Craig-Riou
2026-01-21 16:27:20 +11:00
parent e21fadd124
commit c58a65a52b
6 changed files with 156 additions and 5 deletions

View File

@@ -0,0 +1,45 @@
%script{ type: "text/ng-template", id: "account/customer_account_transactions.html" }
.active_table.orders
%h3= t('.title')
- @shops.each do |shop|
- data_loader = CustomerAccountTransactions::DataLoaderService.new(user: @user, enterprise: shop)
%distributor.active_table_node.row.animate-repeat.closed.inactive{ "data-controller": "frontend-toggle-control", "data-frontend-toggle-control-selector-value": "#transaction-list-#{shop.id}", "data-frontend-toggle-control-target": "classUpdate" }
.small-12.columns
.row.active_table_row.skinny-head.margin-top.closed{ "data-action": "click->frontend-toggle-control#toggleDisplay", "data-frontend-toggle-control-target": "classUpdate" }
.columns.small-2
- if shop.logo_url(:medium)
= image_tag shop.logo_url(:medium), class: "margin-top account-logo"
- else
%i.ofn-i_059-producer
.columns.small-5
%h3.margin-top
= link_to shop.name, enterprise_url_selector(shop)
.columns.small-4.text-right
%h3.margin-top.distributor-balance
= t(".credit_available", credit: Spree::Money.new(data_loader.available_credit))
.columns.small-1.text-right
%h3.margin-top
%i.ofn-i_005-caret-down{ "data-frontend-toggle-control-target": "chevron" }
.row{ style: "display: none", id: "transaction-list-#{shop.id}" }
.columns.small-12.fat
%table
%tr
%th.order1= t(".transaction_date")
%th.order2= t(".description")
%th.order3= t(".payment_method")
%th.order4= t(".amount")
%th.order5= t(".running_balance")
%tbody.transaction-group
- data_loader.customer_account_transactions.each do |transaction|
%tr.transaction-row.even
%td.order1
- # TODO move to helper
= transaction.updated_at.strftime("%Y-%m-%d")
%td.order2
= transaction.description
%td.order3
= t(transaction.payment_method.name.to_s)
%td.order4
= Spree::Money.new(transaction.amount)
%td.order5
= Spree::Money.new(transaction.balance)

View File

@@ -19,18 +19,21 @@
= render 'orders'
= render 'cards'
= render 'transactions'
= render 'customer_account_transactions'
= render 'settings'
= render 'developer_settings' if @user.show_api_key_view
.row.tabset-ctrl#account-tabs{ style: 'margin-bottom: 100px', navigate: 'true', selected: 'orders', prefix: 'account' }
.small.12.medium-2.columns.tab{ name: "orders" }
.small.12.medium-3.columns.tab{ name: "orders" }
%a=t('.tabs.orders')
- if Spree::Config.stripe_connect_enabled && Stripe.publishable_key
.small.12.medium-2.columns.tab{ name: "cards" }
.small.12.medium-3.columns.tab{ name: "cards" }
%a=t('.tabs.cards')
.small.12.medium-2.columns.tab{ name: "transactions" }
.small.12.medium-3.columns.tab{ name: "transactions" }
%a=t('.tabs.transactions')
.small.12.medium-2.columns.tab{ name: "settings" }
.small.12.medium-3.columns.tab{ name: "customer_account_transactions" }
%a=t('.tabs.customer_account_transactions')
.small.12.medium-3.columns.tab{ name: "settings" }
%a=t('.tabs.settings')
// the api_keys partial is the only content for now, so we have to hide the whole tab for now
// if there is new content, we will need to handle this inside the developer_settings partial

View File

@@ -0,0 +1,23 @@
import { Controller } from "stimulus";
export default class extends Controller {
static targets = ["chevron", "classUpdate"];
static values = { selector: String };
toggleDisplay(_event) {
if (this.hasChevronTarget) {
this.chevronTarget.classList.toggle("ofn-i_005-caret-down");
this.chevronTarget.classList.toggle("ofn-i_006-caret-up");
}
if (this.hasClassUpdateTarget) {
this.classUpdateTargets.forEach((t) => {
t.classList.toggle("closed");
t.classList.toggle("open");
});
}
const element = document.querySelector(this.selectorValue);
element.style.display = element.style.display === "none" ? "block" : "none";
}
}

View File

@@ -5020,6 +5020,7 @@ en:
orders: Orders
cards: Credit Cards
transactions: Transactions
customer_account_transactions: Customer Transactions
settings: Account Settings
unconfirmed_email: "Pending email confirmation for: %{unconfirmed_email}. Your email address will be updated once the new email is confirmed."
orders:
@@ -5029,6 +5030,14 @@ en:
transaction_history: Transaction History
authorisation_required: Authorisation Required
authorise: Authorize
customer_account_transactions:
title: Customer Transactions
credit_available: "Credit available: %{credit}"
transaction_date: Transaction Date
payment_method: Payment method
description: Description
amount: Amount
running_balance: Running balance
open_orders:
order: Order
shop: Shop

View File

@@ -0,0 +1,71 @@
/**
* @jest-environment jsdom
*/
import { Application } from "stimulus";
import frontendToggleController from "controllers/frontend_toggle_control_controller";
describe("FrontendToggleControlController", () => {
beforeAll(() => {
const application = Application.start();
application.register("frontend-toggle-control", frontendToggleController);
});
describe("#toggleDispay", () => {
beforeEach(() => {
document.body.innerHTML = `
<div data-controller="frontend-toggle-control" data-frontend-toggle-control-selector-value="#content">
<div id="class-update" class="closed" data-frontend-toggle-control-target="classUpdate">
<button id="remote-toggle" data-action="click->frontend-toggle-control#toggleDisplay"></button>
<i class="ofn-i_005-caret-down" data-frontend-toggle-control-target="chevron"></i>
</div>
</div>
<div id="content">...</div>
`;
});
it("switches the visibility of the element macthing the selector value", () => {
const button = document.getElementById("remote-toggle");
const content = document.getElementById("content");
expect(content.style.display).toBe("");
button.click();
expect(content.style.display).toBe("none");
button.click();
expect(content.style.display).toBe("block");
});
it("switches the direction of the chevron icon", () => {
const button = document.getElementById("remote-toggle");
const chevron = document.querySelector("i");
expect(chevron.className).toBe("ofn-i_005-caret-down");
button.click();
expect(chevron.className).toBe("ofn-i_006-caret-up");
button.click();
expect(chevron.className).toBe("ofn-i_005-caret-down");
});
it("toggles the open/closed class on the matching targets", () => {
const button = document.getElementById("remote-toggle");
const div = document.getElementById("class-update");
expect(div.className).toBe("closed");
button.click();
expect(div.className).toBe("open");
button.click();
expect(div.className).toBe("closed");
});
});
});

View File

@@ -5,7 +5,7 @@ RSpec.describe Customer do
it { is_expected.to belong_to(:user).optional }
it { is_expected.to belong_to(:bill_address).optional }
it { is_expected.to belong_to(:ship_address).optional }
it { is_expected.to have_many(:customer_account_transactions) }
it { is_expected.to have_many(:customer_account_transactions).dependent(:restrict_with_error) }
describe 'an existing customer' do
let(:customer) { create(:customer) }