From 880efce310d828a6b0378791b60d5e3537d24030 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 30 Sep 2026 20:16:37 +0800 Subject: [PATCH] feat: resource view registries for ledger tables and panels Extensions can add columns, row actions, bulk actions and toolbar buttons to ledger tables, and buttons and menu items to its details panels, through ledger:table:: and ledger:details::. - @registry on every Tabular index view and Panel details view - Gateway::Hub merges registered columns once in the controller, and passes the registry to its TabularActions header - stable ids on built-in columns, actions and buttons so registered items can anchor with before/after - index query params include registered filter params - registries declared in extension.js Requires @fleetbase/ember-core 0.3.25 and @fleetbase/ember-ui 0.4.5. --- addon/components/gateway/hub.hbs | 1 + .../controllers/accounting/accounts/index.js | 11 +- addon/controllers/accounting/journal/index.js | 14 +- .../accounting/journal/index/details.js | 11 +- .../billing/invoice-templates/index.js | 14 +- addon/controllers/billing/invoices/index.js | 22 +- .../billing/invoices/index/details.js | 9 + .../controllers/payments/gateways/details.js | 11 +- addon/controllers/payments/gateways/index.js | 334 ++++++++++-------- .../payments/transactions/index.js | 26 +- addon/controllers/payments/wallets/index.js | 33 +- .../payments/wallets/index/details.js | 5 +- addon/extension.js | 4 + addon/services/invoice-template-actions.js | 1 + addon/templates/accounting/accounts/index.hbs | 1 + .../accounting/accounts/index/details.hbs | 1 + addon/templates/accounting/journal/index.hbs | 1 + .../accounting/journal/index/details.hbs | 1 + .../billing/invoice-templates/index.hbs | 1 + addon/templates/billing/invoices/index.hbs | 1 + .../billing/invoices/index/details.hbs | 1 + addon/templates/payments/gateways/index.hbs | 1 + .../templates/payments/transactions/index.hbs | 1 + .../payments/transactions/index/details.hbs | 1 + addon/templates/payments/wallets/index.hbs | 1 + .../payments/wallets/index/details.hbs | 1 + 26 files changed, 339 insertions(+), 169 deletions(-) diff --git a/addon/components/gateway/hub.hbs b/addon/components/gateway/hub.hbs index 56a0c90..9ac2251 100644 --- a/addon/components/gateway/hub.hbs +++ b/addon/components/gateway/hub.hbs @@ -34,6 +34,7 @@
this.sendInvoice(), @@ -69,6 +72,7 @@ export default class BillingInvoicesIndexDetailsController extends Controller { // Record Payment — for open / overdue / partially-paid invoices. if (['sent', 'viewed', 'overdue', 'partial'].includes(invoice?.status)) { dropdownItems.push({ + id: 'record-payment', text: this.intl.t('invoice.actions.record-payment'), icon: 'check-circle', fn: () => this.recordPayment(), @@ -77,6 +81,7 @@ export default class BillingInvoicesIndexDetailsController extends Controller { if (this.hasRefunds) { dropdownItems.push({ + id: 'view-refunds', text: 'View Refunds', icon: 'receipt', fn: () => this.viewRefunds(), @@ -86,6 +91,7 @@ export default class BillingInvoicesIndexDetailsController extends Controller { // Refund - paid or partially refunded invoices with remaining paid funds. if (this.canIssueRefund) { dropdownItems.push({ + id: 'issue-refund', text: 'Issue Refund', icon: 'undo', class: 'text-red-500 hover:text-red-700', @@ -96,6 +102,7 @@ export default class BillingInvoicesIndexDetailsController extends Controller { // Void — for any non-terminal status. if (!['paid', 'refunded', 'refund_pending', 'partial_refund_pending', 'void', 'cancelled'].includes(invoice?.status)) { dropdownItems.push({ + id: 'void-invoice', text: this.intl.t('invoice.actions.void'), icon: 'ban', class: 'text-red-500 hover:text-red-700', @@ -110,6 +117,7 @@ export default class BillingInvoicesIndexDetailsController extends Controller { // Copy Invoice URL — always available. dropdownItems.push({ + id: 'copy-invoice-url', text: this.intl.t('invoice.actions.copy-invoice-url'), icon: 'link', fn: () => this.invoiceActions.copyInvoiceUrl(invoice), @@ -117,6 +125,7 @@ export default class BillingInvoicesIndexDetailsController extends Controller { if (dropdownItems.length > 0) { buttons.push({ + id: 'more', icon: 'ellipsis-h', iconPrefix: 'fas', renderInPlace: true, diff --git a/addon/controllers/payments/gateways/details.js b/addon/controllers/payments/gateways/details.js index c808ad6..58ee419 100644 --- a/addon/controllers/payments/gateways/details.js +++ b/addon/controllers/payments/gateways/details.js @@ -32,8 +32,15 @@ export default class PaymentsGatewaysDetailsController extends Controller { get actionButtons() { return [ - { label: 'Edit', icon: 'pencil', helpText: 'Edit this payment gateway configuration.', onClick: this.editGateway }, - { label: 'Delete', icon: 'trash', type: 'danger', helpText: 'Permanently remove this payment gateway. This cannot be undone.', onClick: this.deleteGateway }, + { id: 'edit-gateway', label: 'Edit', icon: 'pencil', helpText: 'Edit this payment gateway configuration.', onClick: this.editGateway }, + { + id: 'delete-gateway', + label: 'Delete', + icon: 'trash', + type: 'danger', + helpText: 'Permanently remove this payment gateway. This cannot be undone.', + onClick: this.deleteGateway, + }, ]; } diff --git a/addon/controllers/payments/gateways/index.js b/addon/controllers/payments/gateways/index.js index ca4d5f2..41e2f97 100644 --- a/addon/controllers/payments/gateways/index.js +++ b/addon/controllers/payments/gateways/index.js @@ -10,7 +10,7 @@ export default class PaymentsGatewaysIndexController extends Controller { @service fetch; @service notifications; - @tracked queryParams = ['page', 'limit', 'sort', 'query', 'public_id', 'name', 'code', 'driver', 'environment', 'status', 'created_at', 'updated_at']; + @tracked queryParams = this.gatewayActions.queryParamsFor(['page', 'limit', 'sort', 'query', 'public_id', 'name', 'code', 'driver', 'environment', 'status', 'created_at', 'updated_at']); @tracked page = 1; @tracked limit = 30; @tracked sort = '-created_at'; @@ -35,11 +35,13 @@ export default class PaymentsGatewaysIndexController extends Controller { get actionButtons() { return [ { + id: 'refresh', icon: 'refresh', onClick: this.gatewayActions.refresh, helpText: this.intl.t('common.refresh'), }, { + id: 'create', text: this.intl.t('common.new'), type: 'primary', icon: 'plus', @@ -52,6 +54,7 @@ export default class PaymentsGatewaysIndexController extends Controller { const selected = this.tableContext.getSelectedRows(); return [ { + id: 'bulk-delete', label: this.intl.t('common.delete-selected-count', { count: selected.length }), class: 'text-red-500', fn: this.gatewayActions.bulkDelete, @@ -60,162 +63,179 @@ export default class PaymentsGatewaysIndexController extends Controller { } get columns() { - return [ - // ── Default visible columns ────────────────────────────────────── - { - sticky: true, - label: this.intl.t('column.name'), - valuePath: 'name', - cellComponent: 'table/cell/gateway-provider', - action: this.gatewayActions.transition.view, - width: 320, - resizable: true, - sortable: true, - filterable: true, - filterParam: 'name', - filterComponent: 'filter/string', - }, - { - label: this.intl.t('column.driver'), - valuePath: 'driver', - cellComponent: 'table/cell/base', - humanize: true, - width: 130, - resizable: true, - sortable: true, - filterable: true, - filterParam: 'driver', - filterComponent: 'filter/multi-option', - filterOptions: ['taler', 'stripe', 'cash', 'qpay'], - }, - { - label: this.intl.t('column.environment'), - valuePath: 'environment', - cellComponent: 'table/cell/base', - humanize: true, - width: 110, - resizable: true, - sortable: true, - filterable: true, - filterParam: 'environment', - filterComponent: 'filter/select', - filterOptionLabel: 'label', - filterOptionValue: 'value', - filterOptions: [ - { label: 'Live', value: 'live' }, - { label: 'Sandbox', value: 'sandbox' }, - ], - }, - { - label: this.intl.t('column.status'), - valuePath: 'status', - cellComponent: 'table/cell/status', - width: 100, - resizable: true, - sortable: true, - filterable: true, - filterParam: 'status', - filterComponent: 'filter/select', - filterOptionLabel: 'label', - filterOptionValue: 'value', - filterOptions: [ - { label: 'Active', value: 'active' }, - { label: 'Inactive', value: 'inactive' }, - ], - }, - { - label: this.intl.t('column.created-at'), - valuePath: 'createdAt', - sortParam: 'created_at', - filterParam: 'created_at', - width: 150, - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/date', - }, - // ── Hidden / toggleable columns ────────────────────────────────── - { - label: this.intl.t('column.id'), - valuePath: 'public_id', - width: 120, - hidden: true, - resizable: true, - sortable: false, - filterable: true, - filterParam: 'public_id', - filterComponent: 'filter/string', - }, - { - label: this.intl.t('column.code'), - valuePath: 'code', - hidden: true, - resizable: true, - sortable: true, - filterable: true, - filterParam: 'code', - filterComponent: 'filter/string', - }, - { - label: this.intl.t('column.description'), - valuePath: 'description', - hidden: true, - resizable: true, - sortable: false, - filterable: true, - filterParam: 'description', - filterComponent: 'filter/string', - }, - { - label: this.intl.t('column.updated-at'), - valuePath: 'updatedAt', - sortParam: 'updated_at', - filterParam: 'updated_at', - hidden: true, - resizable: true, - sortable: true, - filterable: true, - filterComponent: 'filter/date', - }, - // ── Row actions dropdown ───────────────────────────────────────── - { - label: '', - cellComponent: 'table/cell/dropdown', - ddButtonText: false, - ddButtonIcon: 'ellipsis-h', - ddButtonIconPrefix: 'fas', - ddMenuLabel: this.intl.t('common.resource-actions', { resource: this.intl.t('resource.gateway') }), - cellClassNames: 'overflow-visible', - wrapperClass: 'flex items-center justify-end mx-2', - sticky: 'right', - width: 60, - actions: [ - { - label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.gateway') }), - icon: 'eye', - fn: this.gatewayActions.transition.view, - permission: 'ledger view gateway', - }, - { - label: this.intl.t('common.edit-resource', { resource: this.intl.t('resource.gateway') }), - icon: 'pencil', - fn: this.gatewayActions.edit, - permission: 'ledger update gateway', - }, - { - label: this.intl.t('common.delete-resource', { resource: this.intl.t('resource.gateway') }), - icon: 'trash', - fn: this.gatewayActions.delete, - className: 'text-red-500 hover:text-red-700', - permission: 'ledger delete gateway', - }, - ], - sortable: false, - filterable: false, - resizable: false, - searchable: false, - }, - ]; + // Gateway::Hub renders its own column picker, so registered columns are merged here once. + return this.gatewayActions.mergeRegisteredColumns( + [ + // ── Default visible columns ────────────────────────────────────── + { + id: 'name', + sticky: true, + label: this.intl.t('column.name'), + valuePath: 'name', + cellComponent: 'table/cell/gateway-provider', + action: this.gatewayActions.transition.view, + width: 320, + resizable: true, + sortable: true, + filterable: true, + filterParam: 'name', + filterComponent: 'filter/string', + }, + { + id: 'driver', + label: this.intl.t('column.driver'), + valuePath: 'driver', + cellComponent: 'table/cell/base', + humanize: true, + width: 130, + resizable: true, + sortable: true, + filterable: true, + filterParam: 'driver', + filterComponent: 'filter/multi-option', + filterOptions: ['taler', 'stripe', 'cash', 'qpay'], + }, + { + id: 'environment', + label: this.intl.t('column.environment'), + valuePath: 'environment', + cellComponent: 'table/cell/base', + humanize: true, + width: 110, + resizable: true, + sortable: true, + filterable: true, + filterParam: 'environment', + filterComponent: 'filter/select', + filterOptionLabel: 'label', + filterOptionValue: 'value', + filterOptions: [ + { label: 'Live', value: 'live' }, + { label: 'Sandbox', value: 'sandbox' }, + ], + }, + { + id: 'status', + label: this.intl.t('column.status'), + valuePath: 'status', + cellComponent: 'table/cell/status', + width: 100, + resizable: true, + sortable: true, + filterable: true, + filterParam: 'status', + filterComponent: 'filter/select', + filterOptionLabel: 'label', + filterOptionValue: 'value', + filterOptions: [ + { label: 'Active', value: 'active' }, + { label: 'Inactive', value: 'inactive' }, + ], + }, + { + id: 'created-at', + label: this.intl.t('column.created-at'), + valuePath: 'createdAt', + sortParam: 'created_at', + filterParam: 'created_at', + width: 150, + resizable: true, + sortable: true, + filterable: true, + filterComponent: 'filter/date', + }, + // ── Hidden / toggleable columns ────────────────────────────────── + { + id: 'public-id', + label: this.intl.t('column.id'), + valuePath: 'public_id', + width: 120, + hidden: true, + resizable: true, + sortable: false, + filterable: true, + filterParam: 'public_id', + filterComponent: 'filter/string', + }, + { + id: 'code', + label: this.intl.t('column.code'), + valuePath: 'code', + hidden: true, + resizable: true, + sortable: true, + filterable: true, + filterParam: 'code', + filterComponent: 'filter/string', + }, + { + id: 'description', + label: this.intl.t('column.description'), + valuePath: 'description', + hidden: true, + resizable: true, + sortable: false, + filterable: true, + filterParam: 'description', + filterComponent: 'filter/string', + }, + { + id: 'updated-at', + label: this.intl.t('column.updated-at'), + valuePath: 'updatedAt', + sortParam: 'updated_at', + filterParam: 'updated_at', + hidden: true, + resizable: true, + sortable: true, + filterable: true, + filterComponent: 'filter/date', + }, + // ── Row actions dropdown ───────────────────────────────────────── + { + id: 'row-actions', + label: '', + cellComponent: 'table/cell/dropdown', + ddButtonText: false, + ddButtonIcon: 'ellipsis-h', + ddButtonIconPrefix: 'fas', + ddMenuLabel: this.intl.t('common.resource-actions', { resource: this.intl.t('resource.gateway') }), + cellClassNames: 'overflow-visible', + wrapperClass: 'flex items-center justify-end mx-2', + sticky: 'right', + width: 60, + actions: [ + { + id: 'view', + label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.gateway') }), + icon: 'eye', + fn: this.gatewayActions.transition.view, + permission: 'ledger view gateway', + }, + { + id: 'edit', + label: this.intl.t('common.edit-resource', { resource: this.intl.t('resource.gateway') }), + icon: 'pencil', + fn: this.gatewayActions.edit, + permission: 'ledger update gateway', + }, + { + id: 'delete', + label: this.intl.t('common.delete-resource', { resource: this.intl.t('resource.gateway') }), + icon: 'trash', + fn: this.gatewayActions.delete, + className: 'text-red-500 hover:text-red-700', + permission: 'ledger delete gateway', + }, + ], + sortable: false, + filterable: false, + resizable: false, + searchable: false, + }, + ], + { controller: this } + ); } @task({ restartable: true }) diff --git a/addon/controllers/payments/transactions/index.js b/addon/controllers/payments/transactions/index.js index 5d959bd..0deff15 100644 --- a/addon/controllers/payments/transactions/index.js +++ b/addon/controllers/payments/transactions/index.js @@ -7,7 +7,7 @@ export default class PaymentsTransactionsIndexController extends Controller { @service tableContext; @service intl; - @tracked queryParams = [ + @tracked queryParams = this.transactionActions.queryParamsFor([ 'page', 'limit', 'sort', @@ -27,7 +27,7 @@ export default class PaymentsTransactionsIndexController extends Controller { 'created_at', 'updated_at', 'settled_at', - ]; + ]); @tracked page = 1; @tracked limit = 30; @tracked sort = '-created_at'; @@ -52,6 +52,7 @@ export default class PaymentsTransactionsIndexController extends Controller { get actionButtons() { return [ { + id: 'refresh', icon: 'refresh', onClick: this.transactionActions.refresh, helpText: this.intl.t('common.refresh'), @@ -67,6 +68,7 @@ export default class PaymentsTransactionsIndexController extends Controller { return [ // ── Default visible columns ────────────────────────────────────── { + id: 'public-id', sticky: true, label: this.intl.t('column.id'), valuePath: 'public_id', @@ -80,6 +82,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'created-at', label: this.intl.t('column.date'), valuePath: 'createdAt', sortParam: 'created_at', @@ -90,6 +93,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/date', }, { + id: 'description', label: this.intl.t('column.description'), valuePath: 'description', resizable: true, @@ -99,6 +103,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'type', label: this.intl.t('column.type'), valuePath: 'type', resizable: true, @@ -109,6 +114,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterOptions: ['wallet_deposit', 'wallet_withdrawal', 'wallet_transfer', 'gateway_charge', 'gateway_refund', 'gateway_payout', 'adjustment', 'earning', 'fee'], }, { + id: 'direction', label: this.intl.t('column.direction'), valuePath: 'direction', cellComponent: 'table/cell/base', @@ -126,6 +132,7 @@ export default class PaymentsTransactionsIndexController extends Controller { ], }, { + id: 'amount', label: this.intl.t('column.amount'), valuePath: 'amount', cellComponent: 'table/cell/currency', @@ -135,6 +142,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterable: false, }, { + id: 'status', label: this.intl.t('column.status'), valuePath: 'status', cellComponent: 'table/cell/status', @@ -146,6 +154,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterOptions: ['pending', 'success', 'failed', 'cancelled', 'voided', 'reversed', 'expired'], }, { + id: 'settlement-status', label: 'Settlement', valuePath: 'settlement_status', cellComponent: 'table/cell/status', @@ -159,6 +168,7 @@ export default class PaymentsTransactionsIndexController extends Controller { }, // ── Hidden / toggleable columns ────────────────────────────────── { + id: 'net-amount', label: this.intl.t('column.net-amount'), valuePath: 'net_amount', cellComponent: 'table/cell/currency', @@ -169,6 +179,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterable: false, }, { + id: 'fee-amount', label: this.intl.t('column.fee'), valuePath: 'fee_amount', cellComponent: 'table/cell/currency', @@ -178,6 +189,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterable: false, }, { + id: 'tax-amount', label: this.intl.t('column.tax'), valuePath: 'tax_amount', cellComponent: 'table/cell/currency', @@ -187,6 +199,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterable: false, }, { + id: 'currency', label: this.intl.t('column.currency'), valuePath: 'currency', hidden: true, @@ -197,6 +210,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'gateway', label: this.intl.t('column.gateway'), valuePath: 'gateway', hidden: true, @@ -207,6 +221,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'payment-method', label: this.intl.t('column.payment-method'), valuePath: 'payment_method', hidden: true, @@ -225,6 +240,7 @@ export default class PaymentsTransactionsIndexController extends Controller { ], }, { + id: 'reference', label: this.intl.t('column.reference'), valuePath: 'reference', hidden: true, @@ -235,6 +251,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'period', label: this.intl.t('column.period'), valuePath: 'period', hidden: true, @@ -245,6 +262,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'settled-at', label: this.intl.t('column.settled-at'), valuePath: 'settled_at', sortParam: 'settled_at', @@ -256,6 +274,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/date', }, { + id: 'failure-reason', label: this.intl.t('column.failure-reason'), valuePath: 'failure_reason', hidden: true, @@ -266,6 +285,7 @@ export default class PaymentsTransactionsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'updated-at', label: this.intl.t('column.updated-at'), valuePath: 'updatedAt', sortParam: 'updated_at', @@ -278,6 +298,7 @@ export default class PaymentsTransactionsIndexController extends Controller { }, // ── Row actions dropdown ───────────────────────────────────────── { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -290,6 +311,7 @@ export default class PaymentsTransactionsIndexController extends Controller { width: 60, actions: [ { + id: 'view', label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.transaction') }), icon: 'eye', fn: this.transactionActions.transition.view, diff --git a/addon/controllers/payments/wallets/index.js b/addon/controllers/payments/wallets/index.js index 2b39819..865e8ee 100644 --- a/addon/controllers/payments/wallets/index.js +++ b/addon/controllers/payments/wallets/index.js @@ -7,7 +7,22 @@ export default class PaymentsWalletsIndexController extends Controller { @service tableContext; @service intl; - @tracked queryParams = ['page', 'limit', 'sort', 'query', 'public_id', 'name', 'type', 'status', 'currency', 'is_frozen', 'subject', 'subject_type', 'created_at', 'updated_at']; + @tracked queryParams = this.walletActions.queryParamsFor([ + 'page', + 'limit', + 'sort', + 'query', + 'public_id', + 'name', + 'type', + 'status', + 'currency', + 'is_frozen', + 'subject', + 'subject_type', + 'created_at', + 'updated_at', + ]); @tracked page = 1; @tracked limit = 30; @tracked sort = '-created_at'; @@ -27,6 +42,7 @@ export default class PaymentsWalletsIndexController extends Controller { get actionButtons() { return [ { + id: 'refresh', icon: 'refresh', onClick: this.walletActions.refresh, helpText: this.intl.t('common.refresh'), @@ -38,6 +54,7 @@ export default class PaymentsWalletsIndexController extends Controller { const selected = this.tableContext.getSelectedRows(); return [ { + id: 'bulk-delete', label: this.intl.t('common.delete-selected-count', { count: selected.length }), class: 'text-red-500', fn: this.walletActions.bulkDelete, @@ -49,6 +66,7 @@ export default class PaymentsWalletsIndexController extends Controller { return [ // ── Owner (subject) column ─────────────────────────────────────── { + id: 'subject-name', sticky: true, label: this.intl.t('column.owner'), valuePath: 'subject.name', @@ -63,6 +81,7 @@ export default class PaymentsWalletsIndexController extends Controller { }, // ── Default visible columns ────────────────────────────────────── { + id: 'name', label: this.intl.t('column.name'), valuePath: 'name', cellComponent: 'table/cell/anchor', @@ -75,6 +94,7 @@ export default class PaymentsWalletsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'type', label: this.intl.t('column.type'), valuePath: 'type', cellComponent: 'table/cell/base', @@ -95,6 +115,7 @@ export default class PaymentsWalletsIndexController extends Controller { ], }, { + id: 'currency', label: this.intl.t('column.currency'), valuePath: 'currency', width: 90, @@ -105,6 +126,7 @@ export default class PaymentsWalletsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'balance', label: this.intl.t('column.balance'), valuePath: 'balance', cellComponent: 'table/cell/currency', @@ -115,6 +137,7 @@ export default class PaymentsWalletsIndexController extends Controller { filterable: false, }, { + id: 'status', label: this.intl.t('column.status'), valuePath: 'status', cellComponent: 'table/cell/status', @@ -134,6 +157,7 @@ export default class PaymentsWalletsIndexController extends Controller { ], }, { + id: 'created-at', label: this.intl.t('column.created-at'), valuePath: 'createdAt', sortParam: 'created_at', @@ -146,6 +170,7 @@ export default class PaymentsWalletsIndexController extends Controller { }, // ── Hidden / toggleable columns ────────────────────────────────── { + id: 'public-id', label: this.intl.t('column.id'), valuePath: 'public_id', width: 120, @@ -157,6 +182,7 @@ export default class PaymentsWalletsIndexController extends Controller { filterComponent: 'filter/string', }, { + id: 'description', label: this.intl.t('column.description'), valuePath: 'description', width: 200, @@ -166,6 +192,7 @@ export default class PaymentsWalletsIndexController extends Controller { filterable: false, }, { + id: 'is-frozen-label', label: this.intl.t('column.frozen'), valuePath: 'is_frozen_label', cellComponent: 'table/cell/base', @@ -184,6 +211,7 @@ export default class PaymentsWalletsIndexController extends Controller { ], }, { + id: 'updated-at', label: this.intl.t('column.updated-at'), valuePath: 'updatedAt', sortParam: 'updated_at', @@ -196,6 +224,7 @@ export default class PaymentsWalletsIndexController extends Controller { }, // ── Row actions dropdown ───────────────────────────────────────── { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -208,12 +237,14 @@ export default class PaymentsWalletsIndexController extends Controller { width: 60, actions: [ { + id: 'view', label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.wallet') }), icon: 'eye', fn: this.walletActions.transition.view, permission: 'ledger view wallet', }, { + id: 'delete', label: this.intl.t('common.delete-resource', { resource: this.intl.t('resource.wallet') }), icon: 'trash', fn: this.walletActions.delete, diff --git a/addon/controllers/payments/wallets/index/details.js b/addon/controllers/payments/wallets/index/details.js index 70f9ead..32f1652 100644 --- a/addon/controllers/payments/wallets/index/details.js +++ b/addon/controllers/payments/wallets/index/details.js @@ -20,9 +20,10 @@ export default class PaymentsWalletsIndexDetailsController extends Controller { const frozen = wallet?.is_frozen; return [ - { label: 'Add Funds', icon: 'plus-circle', type: 'primary', helpText: 'Add funds to this wallet balance.', onClick: this.topUpWallet }, - { label: 'Transfer', icon: 'exchange-alt', helpText: 'Transfer funds from this wallet to another wallet.', onClick: this.transferFunds }, + { id: 'top-up-wallet', label: 'Add Funds', icon: 'plus-circle', type: 'primary', helpText: 'Add funds to this wallet balance.', onClick: this.topUpWallet }, + { id: 'transfer-funds', label: 'Transfer', icon: 'exchange-alt', helpText: 'Transfer funds from this wallet to another wallet.', onClick: this.transferFunds }, { + id: 'toggle-freeze', label: frozen ? 'Unfreeze' : 'Freeze', icon: frozen ? 'unlock' : 'lock', type: frozen ? 'default' : 'danger', diff --git a/addon/extension.js b/addon/extension.js index 1d7fc98..d316ece 100644 --- a/addon/extension.js +++ b/addon/extension.js @@ -133,6 +133,10 @@ export default { // Register dashboard and widgets this.registerWidgets(widgetService); + + // Declare the registries extensions can add table columns, actions and + // buttons to, e.g. `ledger:table:invoice:columns` or `ledger:details:invoice:menu`. + universe.getService('universe/resource-view-service')?.declare('ledger', ['account', 'gateway', 'invoice', 'invoice-template', 'journal', 'transaction', 'wallet']); }, registerWidgets(widgetService) { diff --git a/addon/services/invoice-template-actions.js b/addon/services/invoice-template-actions.js index 11df629..6097f18 100644 --- a/addon/services/invoice-template-actions.js +++ b/addon/services/invoice-template-actions.js @@ -7,6 +7,7 @@ export default class InvoiceTemplateActionsService extends ResourceActionService this.initialize('template', { permissionPrefix: 'ledger', mountPrefix: 'console.ledger', + registryResource: 'invoice-template', }); } diff --git a/addon/templates/accounting/accounts/index.hbs b/addon/templates/accounting/accounts/index.hbs index a34b343..b520625 100644 --- a/addon/templates/accounting/accounts/index.hbs +++ b/addon/templates/accounting/accounts/index.hbs @@ -1,4 +1,5 @@