diff --git a/addon/components/customer-panel.hbs b/addon/components/customer-panel.hbs index 92a1b94..85a06b7 100644 --- a/addon/components/customer-panel.hbs +++ b/addon/components/customer-panel.hbs @@ -1,4 +1,5 @@ `. + * + * @param {String} resource e.g. 'order' + * @param {Array} baseQueryParams + * @returns {Array} + */ + registeredQueryParams(resource, baseQueryParams = []) { + return lookupResourceView(getOwner(this))?.queryParamsFor('storefront', resource, baseQueryParams) ?? baseQueryParams; + } + + /** + * `columns` with the columns and row actions extensions registered under + * `storefront:table:` merged in, for views that render their own ``. + * + * @param {String} resource e.g. 'promotion' + * @param {Array} columns + * @returns {Array} + */ + mergeRegisteredColumns(resource, columns = []) { + const resourceView = lookupResourceView(getOwner(this)); + if (!resourceView) { + return columns; + } + + const registry = `storefront:table:${resource}`; + const context = { controller: this }; + return resourceView.mergeRowActions(registry, resourceView.mergeSlot(registry, 'columns', columns, context), context); + } + + /** + * The toolbar buttons extensions registered under `storefront:table::actions`, + * for views that lay out their own header. + * + * @param {String} resource + * @returns {Array} + */ + registeredTableActions(resource) { + return mergeHeaderButtons(lookupResourceView(getOwner(this)), `storefront:table:${resource}`, [], { controller: this }); + } } diff --git a/addon/controllers/customers/index.js b/addon/controllers/customers/index.js index 70e59bf..6242a73 100644 --- a/addon/controllers/customers/index.js +++ b/addon/controllers/customers/index.js @@ -53,7 +53,7 @@ export default class CustomersIndexController extends BaseController { * * @var {Array} */ - queryParams = ['page', 'limit', 'sort', 'query', 'public_id', 'internal_id', 'phone', 'email', 'address', 'created_by', 'updated_by', 'status']; + queryParams = this.registeredQueryParams('customer', ['page', 'limit', 'sort', 'query', 'public_id', 'internal_id', 'phone', 'email', 'address', 'created_by', 'updated_by', 'status']); /** * The current page of data being viewed @@ -118,6 +118,7 @@ export default class CustomersIndexController extends BaseController { */ @tracked columns = [ { + id: 'name', sticky: true, label: this.intl.t('storefront.common.name'), valuePath: 'name', @@ -130,6 +131,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'public-id', label: this.intl.t('storefront.common.id'), valuePath: 'public_id', cellComponent: 'click-to-copy', @@ -140,6 +142,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'internal-id', label: this.intl.t('storefront.customers.index.internal-id'), valuePath: 'internal_id', cellComponent: 'click-to-copy', @@ -150,6 +153,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'email', label: this.intl.t('storefront.common.email'), valuePath: 'email', cellComponent: 'table/cell/base', @@ -160,6 +164,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'phone', label: this.intl.t('storefront.common.phone'), valuePath: 'phone', cellComponent: 'table/cell/base', @@ -170,6 +175,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'address', label: this.intl.t('storefront.common.address'), valuePath: 'address', cellComponent: 'table/cell/anchor', @@ -182,6 +188,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'country', label: this.intl.t('storefront.common.country'), valuePath: 'country', cellComponent: 'table/cell/base', @@ -194,6 +201,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'created-at', label: this.intl.t('storefront.customers.index.create-at'), valuePath: 'createdAt', sortParam: 'created_at', @@ -204,6 +212,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/date', }, { + id: 'updated-at', label: this.intl.t('storefront.customers.index.update-at'), valuePath: 'updatedAt', sortParam: 'updated_at', @@ -215,6 +224,7 @@ export default class CustomersIndexController extends BaseController { filterComponent: 'filter/date', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -227,6 +237,7 @@ export default class CustomersIndexController extends BaseController { width: 60, actions: [ { + id: 'view-customer', label: this.intl.t('storefront.customers.index.view-customer-details'), fn: this.viewCustomer, }, diff --git a/addon/controllers/networks/index/network/customers.js b/addon/controllers/networks/index/network/customers.js index ea6eb00..da92881 100644 --- a/addon/controllers/networks/index/network/customers.js +++ b/addon/controllers/networks/index/network/customers.js @@ -1,6 +1,6 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; -import { tracked } from '@glimmer/tracking'; +import { tracked, cached } from '@glimmer/tracking'; import CustomersIndexController from '../../../customers'; /** @@ -11,6 +11,7 @@ export default class NetworksIndexNetworkCustomersController extends CustomersIn @tracked columns = [ { + id: 'name', label: this.intl.t('storefront.common.name'), valuePath: 'name', width: '25%', @@ -22,6 +23,7 @@ export default class NetworksIndexNetworkCustomersController extends CustomersIn filterComponent: 'filter/string', }, { + id: 'public-id', label: this.intl.t('storefront.common.id'), valuePath: 'public_id', cellComponent: 'click-to-copy', @@ -32,6 +34,7 @@ export default class NetworksIndexNetworkCustomersController extends CustomersIn filterComponent: 'filter/string', }, { + id: 'email', label: this.intl.t('storefront.common.email'), valuePath: 'email', cellComponent: 'table/cell/base', @@ -42,6 +45,7 @@ export default class NetworksIndexNetworkCustomersController extends CustomersIn filterComponent: 'filter/string', }, { + id: 'phone', label: this.intl.t('storefront.common.phone'), valuePath: 'phone', cellComponent: 'table/cell/base', @@ -52,6 +56,7 @@ export default class NetworksIndexNetworkCustomersController extends CustomersIn filterComponent: 'filter/string', }, { + id: 'created-at', label: this.intl.t('storefront.customers.index.create-at'), valuePath: 'createdAt', sortParam: 'created_at', @@ -66,4 +71,22 @@ export default class NetworksIndexNetworkCustomersController extends CustomersIn @action viewCustomer(customer) { this.contextPanel.focus(customer, 'viewing'); } + + /** + * The columns with what extensions registered under `storefront:table:network-customer` merged in. + * + * @var {Array} + */ + @cached get registeredColumns() { + return this.mergeRegisteredColumns('network-customer', this.columns); + } + + /** + * Toolbar buttons extensions registered under `storefront:table:network-customer:actions`. + * + * @var {Array} + */ + get registeredActionButtons() { + return this.registeredTableActions('network-customer'); + } } diff --git a/addon/controllers/networks/index/network/orders.js b/addon/controllers/networks/index/network/orders.js index 838feef..69199ef 100644 --- a/addon/controllers/networks/index/network/orders.js +++ b/addon/controllers/networks/index/network/orders.js @@ -1,11 +1,11 @@ -import Controller from '@ember/controller'; +import BaseController from '@fleetbase/storefront-engine/controllers/base-controller'; import { inject as service } from '@ember/service'; -import { tracked } from '@glimmer/tracking'; +import { tracked, cached } from '@glimmer/tracking'; import { isBlank } from '@ember/utils'; import { timeout, task } from 'ember-concurrency'; import { action } from '@ember/object'; -export default class NetworksIndexNetworkOrdersController extends Controller { +export default class NetworksIndexNetworkOrdersController extends BaseController { /** * Inject the `notifications` service * @@ -55,7 +55,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { * * @var {Array} */ - queryParams = []; + queryParams = this.registeredQueryParams('network-order', []); @tracked page = 1; @tracked limit; @@ -76,6 +76,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { @tracked columns = [ { + id: 'public-id', label: this.intl.t('storefront.common.id'), valuePath: 'public_id', width: '150px', @@ -87,6 +88,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { filterComponent: 'filter/string', }, { + id: 'internal-id', label: this.intl.t('storefront.orders.index.internal-id'), valuePath: 'internal_id', width: '125px', @@ -96,6 +98,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { filterComponent: 'filter/string', }, { + id: 'customer-name', label: this.intl.t('storefront.orders.index.customer'), valuePath: 'customer.name', cellComponent: 'table/cell/base', @@ -110,6 +113,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { model: 'customer', }, { + id: 'pickup-name', label: this.intl.t('storefront.common.pickup'), valuePath: 'pickupName', cellComponent: 'table/cell/base', @@ -123,6 +127,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { model: 'place', }, { + id: 'dropoff-name', label: this.intl.t('storefront.common.dropoff'), valuePath: 'dropoffName', cellComponent: 'table/cell/base', @@ -136,6 +141,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { model: 'place', }, { + id: 'scheduled-at', label: this.intl.t('storefront.orders.index.scheduled-at'), valuePath: 'scheduledAt', sortParam: 'scheduled_at', @@ -147,6 +153,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { filterComponent: 'filter/date', }, { + id: 'item-count', label: '# Items', cellComponent: 'table/cell/base', valuePath: 'item_count', @@ -155,6 +162,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { width: '50px', }, { + id: 'transaction-amount', label: this.intl.t('storefront.orders.index.transaction-total'), cellComponent: 'table/cell/base', valuePath: 'transaction_amount', @@ -164,6 +172,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { sortable: true, }, { + id: 'tracking-number-tracking-number', label: this.intl.t('storefront.orders.index.tracking-number'), cellComponent: 'table/cell/base', valuePath: 'tracking_number.tracking_number', @@ -174,6 +183,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { filterComponent: 'filter/string', }, { + id: 'driver-assigned', label: this.intl.t('storefront.orders.index.driver-assigned'), cellComponent: 'table/cell/driver-name', valuePath: 'driver_assigned', @@ -192,6 +202,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { }, }, { + id: 'type', label: this.intl.t('storefront.common.type'), cellComponent: 'cell/humanize', valuePath: 'type', @@ -201,6 +212,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { sortable: true, }, { + id: 'status', label: this.intl.t('storefront.common.status'), valuePath: 'status', cellComponent: 'table/cell/status', @@ -212,6 +224,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { // filterOptions: this.statusOptions, }, { + id: 'created-at', label: this.intl.t('storefront.orders.index.created-at'), valuePath: 'createdAt', sortParam: 'created_at', @@ -223,6 +236,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { filterComponent: 'filter/date', }, { + id: 'updated-at', label: this.intl.t('storefront.orders.index.updated-at'), valuePath: 'updatedAt', sortParam: 'updated_at', @@ -235,6 +249,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { filterComponent: 'filter/date', }, { + id: 'created-by-name', label: this.intl.t('storefront.orders.index.created-by'), valuePath: 'created_by_name', width: '125px', @@ -247,6 +262,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { model: 'user', }, { + id: 'updated-by-name', label: this.intl.t('storefront.orders.index.updated-by'), valuePath: 'updated_by_name', width: '125px', @@ -259,6 +275,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { model: 'user', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -270,11 +287,13 @@ export default class NetworksIndexNetworkOrdersController extends Controller { width: '12%', actions: [ { + id: 'view-order', label: this.intl.t('storefront.orders.index.view-order'), icon: 'eye', fn: this.viewOrder, }, { + id: 'cancel-order', label: this.intl.t('storefront.orders.index.cancel-order'), icon: 'ban', fn: this.cancelOrder, @@ -283,6 +302,7 @@ export default class NetworksIndexNetworkOrdersController extends Controller { separator: true, }, { + id: 'delete-order', label: this.intl.t('storefront.orders.index.delete-order'), icon: 'trash', fn: this.deleteOrder, @@ -322,4 +342,22 @@ export default class NetworksIndexNetworkOrdersController extends Controller { @action viewOrder(order) { return this.storefrontOrderActions.viewOrder(order); } + + /** + * The columns with what extensions registered under `storefront:table:network-order` merged in. + * + * @var {Array} + */ + @cached get registeredColumns() { + return this.mergeRegisteredColumns('network-order', this.columns); + } + + /** + * Toolbar buttons extensions registered under `storefront:table:network-order:actions`. + * + * @var {Array} + */ + get registeredActionButtons() { + return this.registeredTableActions('network-order'); + } } diff --git a/addon/controllers/networks/index/network/stores.js b/addon/controllers/networks/index/network/stores.js index 5774764..58c5b82 100644 --- a/addon/controllers/networks/index/network/stores.js +++ b/addon/controllers/networks/index/network/stores.js @@ -1,5 +1,5 @@ -import Controller from '@ember/controller'; -import { tracked } from '@glimmer/tracking'; +import BaseController from '@fleetbase/storefront-engine/controllers/base-controller'; +import { tracked, cached } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action, set } from '@ember/object'; import { isBlank } from '@ember/utils'; @@ -8,7 +8,7 @@ import createShareableLink from '../../../../utils/create-shareable-link'; import isEmail from '@fleetbase/ember-core/utils/is-email'; import isModel from '@fleetbase/ember-core/utils/is-model'; -export default class NetworksIndexNetworkStoresController extends Controller { +export default class NetworksIndexNetworkStoresController extends BaseController { @service notifications; @service intl; @service modalsManager; @@ -23,7 +23,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { * @var {Array} * @memberof NetworksIndexNetworkStoresController */ - queryParams = ['category', 'status', 'storeQuery']; + queryParams = this.registeredQueryParams('network-store', ['category', 'status', 'storeQuery']); /** * The current page of data being viewed @@ -100,6 +100,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { */ @tracked columns = [ { + id: 'name', label: this.intl.t('storefront.networks.index.network.stores.store'), valuePath: 'name', width: '130px', @@ -111,6 +112,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { cellClassNames: 'network-store-name-column', }, { + id: 'public-id', label: this.intl.t('storefront.common.id'), valuePath: 'public_id', cellComponent: 'click-to-copy', @@ -121,6 +123,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { filterComponent: 'filter/string', }, { + id: 'category-name', label: this.intl.t('storefront.common.category'), valuePath: 'category.name', cellComponent: 'table/cell/base', @@ -131,6 +134,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { filterComponent: 'filter/string', }, { + id: 'currency', label: this.intl.t('storefront.common.currency'), valuePath: 'currency', cellComponent: 'table/cell/base', @@ -141,6 +145,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { filterComponent: 'filter/string', }, { + id: 'created-at-short', label: this.intl.t('storefront.networks.index.network.stores.created-at'), valuePath: 'createdAtShort', sortParam: 'created_at', @@ -151,6 +156,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { filterComponent: 'filter/date', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -162,18 +168,22 @@ export default class NetworksIndexNetworkStoresController extends Controller { width: '50px', actions: [ { + id: 'view-store-details', label: this.intl.t('storefront.networks.index.network.stores.view-store-details'), fn: this.viewStoreDetails, }, { + id: 'edit-store', label: this.intl.t('storefront.networks.index.network.stores.edit-store'), fn: this.editStore, }, { + id: 'assign-store-to-category', label: this.intl.t('storefront.networks.index.network.stores.assign-category'), fn: this.assignStoreToCategory, }, { + id: 'remove-store-category', label: this.intl.t('storefront.networks.index.network.stores.remove-category'), fn: this.removeStoreCategory, isVisible: (store) => store.category, @@ -182,6 +192,7 @@ export default class NetworksIndexNetworkStoresController extends Controller { separator: true, }, { + id: 'remove-store', label: this.intl.t('storefront.networks.index.network.stores.remove-store-from-network'), fn: this.removeStore, }, @@ -646,4 +657,22 @@ export default class NetworksIndexNetworkStoresController extends Controller { }, }); } + + /** + * The columns with what extensions registered under `storefront:table:network-store` merged in. + * + * @var {Array} + */ + @cached get registeredColumns() { + return this.mergeRegisteredColumns('network-store', this.columns); + } + + /** + * Toolbar buttons extensions registered under `storefront:table:network-store:actions`. + * + * @var {Array} + */ + get registeredActionButtons() { + return this.registeredTableActions('network-store'); + } } diff --git a/addon/controllers/orders/index.js b/addon/controllers/orders/index.js index 8bf4552..e41165d 100644 --- a/addon/controllers/orders/index.js +++ b/addon/controllers/orders/index.js @@ -33,7 +33,7 @@ export default class OrdersIndexController extends BaseController { @tracked status; @tracked currency = 'USD'; - @tracked queryParams = [ + @tracked queryParams = this.registeredQueryParams('order', [ 'page', 'limit', 'sort', @@ -50,10 +50,61 @@ export default class OrdersIndexController extends BaseController { 'created_by', 'updated_by', 'status', - ]; + ]); + + /** + * Header buttons. Extensions add to them through `storefront:table:order:actions`. + * + * @var {Array} + */ + get actionButtons() { + return [ + { + id: 'export', + icon: 'long-arrow-up', + iconClass: 'rotate-icon-45', + text: this.intl.t('storefront.common.export'), + permission: 'storefront export order', + }, + ]; + } + + /** + * Bulk actions for the selected orders. Extensions add to them through `storefront:table:order:bulk-actions`. + * + * @var {Array} + */ + get bulkActions() { + return [ + { + id: 'bulk-cancel', + label: this.intl.t('storefront.component.widget.orders.cancel-orders'), + icon: 'ban', + fn: this.bulkCancelOrders, + permission: 'fleet-ops cancel order', + }, + { + id: 'bulk-delete', + label: this.intl.t('fleet-ops.operations.orders.index.delete-orders'), + icon: 'trash', + class: 'text-red-500', + fn: this.bulkDeleteOrders, + permission: 'fleet-ops delete order', + }, + { separator: true }, + { + id: 'bulk-dispatch', + label: this.intl.t('fleet-ops.operations.orders.index.dispatch-orders'), + icon: 'rocket', + fn: this.bulkDispatchOrders, + permission: 'fleet-ops dispatch order', + }, + ]; + } @tracked columns = [ { + id: 'public-id', sticky: true, label: this.intl.t('storefront.common.id'), valuePath: 'public_id', @@ -66,6 +117,7 @@ export default class OrdersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'internal-id', label: this.intl.t('storefront.orders.index.internal-id'), valuePath: 'internal_id', width: '125px', @@ -76,6 +128,7 @@ export default class OrdersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'customer-name', label: this.intl.t('storefront.orders.index.customer'), valuePath: 'customer.name', cellComponent: 'table/cell/base', @@ -90,6 +143,7 @@ export default class OrdersIndexController extends BaseController { model: 'customer', }, { + id: 'meta-total', label: this.intl.t('storefront.orders.index.total'), cellComponent: 'table/cell/currency', currency: this.currency, @@ -100,6 +154,7 @@ export default class OrdersIndexController extends BaseController { sortable: true, }, { + id: 'pickup-name', label: this.intl.t('storefront.common.pickup'), valuePath: 'pickupName', cellComponent: 'table/cell/base', @@ -113,6 +168,7 @@ export default class OrdersIndexController extends BaseController { model: 'place', }, { + id: 'dropoff-name', label: this.intl.t('storefront.common.dropoff'), valuePath: 'dropoffName', cellComponent: 'table/cell/base', @@ -126,6 +182,7 @@ export default class OrdersIndexController extends BaseController { model: 'place', }, { + id: 'driver-assigned', label: this.intl.t('storefront.orders.index.driver-assigned'), cellComponent: 'table/cell/driver-name', valuePath: 'driver_assigned', @@ -144,6 +201,7 @@ export default class OrdersIndexController extends BaseController { }, }, { + id: 'scheduled-at', label: this.intl.t('storefront.orders.index.scheduled-at'), valuePath: 'scheduledAt', sortParam: 'scheduled_at', @@ -156,6 +214,7 @@ export default class OrdersIndexController extends BaseController { filterComponent: 'filter/date', }, { + id: 'item-count', label: '# Items', cellComponent: 'table/cell/base', valuePath: 'item_count', @@ -164,6 +223,7 @@ export default class OrdersIndexController extends BaseController { width: '50px', }, { + id: 'tracking-number-tracking-number', label: this.intl.t('storefront.orders.index.tracking-number'), cellComponent: 'table/cell/base', valuePath: 'tracking_number.tracking_number', @@ -174,6 +234,7 @@ export default class OrdersIndexController extends BaseController { filterComponent: 'filter/string', }, { + id: 'type', label: this.intl.t('storefront.common.type'), cellComponent: 'cell/humanize', valuePath: 'type', @@ -183,6 +244,7 @@ export default class OrdersIndexController extends BaseController { sortable: true, }, { + id: 'status', label: this.intl.t('storefront.common.status'), valuePath: 'status', cellComponent: 'table/cell/status', @@ -194,6 +256,7 @@ export default class OrdersIndexController extends BaseController { // filterOptions: this.statusOptions, }, { + id: 'created-at-short', label: this.intl.t('storefront.orders.index.created-at'), valuePath: 'createdAtShort', sortParam: 'created_at', @@ -205,6 +268,7 @@ export default class OrdersIndexController extends BaseController { filterComponent: 'filter/date', }, { + id: 'updated-at-short', label: this.intl.t('storefront.orders.index.updated-at'), valuePath: 'updatedAtShort', sortParam: 'updated_at', @@ -217,6 +281,7 @@ export default class OrdersIndexController extends BaseController { filterComponent: 'filter/date', }, { + id: 'created-by-name', label: this.intl.t('storefront.orders.index.created-by'), valuePath: 'created_by_name', width: '125px', @@ -229,6 +294,7 @@ export default class OrdersIndexController extends BaseController { model: 'user', }, { + id: 'updated-by-name', label: this.intl.t('storefront.orders.index.updated-by'), valuePath: 'updated_by_name', width: '125px', @@ -241,6 +307,7 @@ export default class OrdersIndexController extends BaseController { model: 'user', }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -253,12 +320,14 @@ export default class OrdersIndexController extends BaseController { width: 60, actions: [ { + id: 'view-order', label: this.intl.t('fleet-ops.operations.orders.index.view-order'), icon: 'eye', fn: this.viewOrder, permission: 'fleet-ops view order', }, { + id: 'dispatch-order', label: this.intl.t('fleet-ops.operations.orders.index.dispatch-order'), icon: 'paper-plane', fn: this.dispatchOrder, @@ -266,6 +335,7 @@ export default class OrdersIndexController extends BaseController { isVisible: (order) => order.canBeDispatched, }, { + id: 'cancel-order', label: this.intl.t('storefront.component.widget.orders.cancel-order'), icon: 'ban', fn: this.cancelOrder, @@ -275,6 +345,7 @@ export default class OrdersIndexController extends BaseController { separator: true, }, { + id: 'delete-order', label: this.intl.t('fleet-ops.operations.orders.index.delete-order'), icon: 'trash', fn: this.deleteOrder, diff --git a/addon/controllers/promotions/campaigns.js b/addon/controllers/promotions/campaigns.js index feae920..811df00 100644 --- a/addon/controllers/promotions/campaigns.js +++ b/addon/controllers/promotions/campaigns.js @@ -1,10 +1,10 @@ -import Controller from '@ember/controller'; -import { tracked } from '@glimmer/tracking'; +import BaseController from '@fleetbase/storefront-engine/controllers/base-controller'; +import { tracked, cached } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { debounce } from '@ember/runloop'; -export default class PromotionsCampaignsController extends Controller { +export default class PromotionsCampaignsController extends BaseController { @service store; @service intl; @service storefront; @@ -13,7 +13,7 @@ export default class PromotionsCampaignsController extends Controller { @service crud; @service fetch; @service hostRouter; - queryParams = ['query', 'status', 'page']; + queryParams = this.registeredQueryParams('campaign', ['query', 'status', 'page']); @tracked query; @tracked status; @@ -22,6 +22,7 @@ export default class PromotionsCampaignsController extends Controller { get columns() { return [ { + id: 'name', label: this.intl.t('storefront.promotions.common.name'), valuePath: 'name', width: '22%', @@ -30,6 +31,7 @@ export default class PromotionsCampaignsController extends Controller { resizable: true, }, { + id: 'status', label: this.intl.t('storefront.promotions.common.status'), valuePath: 'status', width: '10%', @@ -37,30 +39,35 @@ export default class PromotionsCampaignsController extends Controller { resizable: true, }, { + id: 'segment-name', label: this.intl.t('storefront.promotions.campaigns.audience'), valuePath: 'segment_name', width: '16%', resizable: true, }, { + id: 'title', label: this.intl.t('storefront.promotions.campaigns.title-label'), valuePath: 'title', width: '20%', resizable: true, }, { + id: 'send-at-formatted', label: this.intl.t('storefront.promotions.campaigns.send-at'), valuePath: 'sendAtFormatted', width: '14%', resizable: true, }, { + id: 'targeted', label: this.intl.t('storefront.promotions.campaigns.targeted'), valuePath: 'targeted', width: '10%', resizable: true, }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -71,11 +78,12 @@ export default class PromotionsCampaignsController extends Controller { sticky: 'right', width: 60, actions: [ - { label: this.intl.t('storefront.promotions.common.edit'), fn: this.editCampaign, permission: 'storefront update campaign' }, - { label: this.intl.t('storefront.promotions.campaigns.send-now'), fn: this.sendCampaign, permission: 'storefront send campaign' }, - { label: this.intl.t('storefront.promotions.campaigns.cancel-campaign'), fn: this.cancelCampaign, permission: 'storefront cancel campaign' }, + { id: 'edit-campaign', label: this.intl.t('storefront.promotions.common.edit'), fn: this.editCampaign, permission: 'storefront update campaign' }, + { id: 'send-campaign', label: this.intl.t('storefront.promotions.campaigns.send-now'), fn: this.sendCampaign, permission: 'storefront send campaign' }, + { id: 'cancel-campaign', label: this.intl.t('storefront.promotions.campaigns.cancel-campaign'), fn: this.cancelCampaign, permission: 'storefront cancel campaign' }, { separator: true }, { + id: 'delete-campaign', label: this.intl.t('storefront.promotions.common.delete'), fn: this.deleteCampaign, class: 'text-red-700 hover:text-red-800', @@ -220,4 +228,22 @@ export default class PromotionsCampaignsController extends Controller { onSuccess: () => this.hostRouter.refresh(), }); } + + /** + * The columns with what extensions registered under `storefront:table:campaign` merged in. + * + * @var {Array} + */ + @cached get registeredColumns() { + return this.mergeRegisteredColumns('campaign', this.columns); + } + + /** + * Toolbar buttons extensions registered under `storefront:table:campaign:actions`. + * + * @var {Array} + */ + get registeredActionButtons() { + return this.registeredTableActions('campaign'); + } } diff --git a/addon/controllers/promotions/index.js b/addon/controllers/promotions/index.js index 733a8d3..5f2d13c 100644 --- a/addon/controllers/promotions/index.js +++ b/addon/controllers/promotions/index.js @@ -1,10 +1,10 @@ -import Controller from '@ember/controller'; -import { tracked } from '@glimmer/tracking'; +import BaseController from '@fleetbase/storefront-engine/controllers/base-controller'; +import { tracked, cached } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { debounce } from '@ember/runloop'; -export default class PromotionsIndexController extends Controller { +export default class PromotionsIndexController extends BaseController { @service store; @service intl; @service storefront; @@ -13,7 +13,7 @@ export default class PromotionsIndexController extends Controller { @service crud; @service fetch; @service hostRouter; - queryParams = ['query', 'status', 'page']; + queryParams = this.registeredQueryParams('promotion', ['query', 'status', 'page']); @tracked query; @tracked status; @@ -22,6 +22,7 @@ export default class PromotionsIndexController extends Controller { get columns() { return [ { + id: 'name', label: this.intl.t('storefront.promotions.common.name'), valuePath: 'name', width: '22%', @@ -30,6 +31,7 @@ export default class PromotionsIndexController extends Controller { resizable: true, }, { + id: 'type', label: this.intl.t('storefront.promotions.list.type'), valuePath: 'type', width: '14%', @@ -37,6 +39,7 @@ export default class PromotionsIndexController extends Controller { resizable: true, }, { + id: 'trigger', label: this.intl.t('storefront.promotions.list.trigger'), valuePath: 'trigger', width: '10%', @@ -44,6 +47,7 @@ export default class PromotionsIndexController extends Controller { resizable: true, }, { + id: 'status', label: this.intl.t('storefront.promotions.common.status'), valuePath: 'status', width: '10%', @@ -51,6 +55,7 @@ export default class PromotionsIndexController extends Controller { resizable: true, }, { + id: 'runs-from', label: this.intl.t('storefront.promotions.list.runs'), valuePath: 'runsFrom', width: '18%', @@ -58,12 +63,14 @@ export default class PromotionsIndexController extends Controller { resizable: true, }, { + id: 'redemptions-count', label: this.intl.t('storefront.promotions.list.redemptions'), valuePath: 'redemptionsCount', width: '10%', resizable: true, }, { + id: 'discount-given', label: this.intl.t('storefront.promotions.list.discount-given'), valuePath: 'discountGiven', width: '12%', @@ -72,6 +79,7 @@ export default class PromotionsIndexController extends Controller { resizable: true, }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -83,15 +91,31 @@ export default class PromotionsIndexController extends Controller { sticky: 'right', width: 60, actions: [ - { label: this.intl.t('storefront.promotions.common.edit'), fn: this.editPromotion, permission: 'storefront update promotion' }, - { label: this.intl.t('storefront.promotions.list.manage-codes'), fn: this.manageCodes, permission: 'storefront generate-codes promotion' }, - { label: this.intl.t('storefront.promotions.list.announce'), fn: this.announce, permission: 'storefront announce promotion' }, + { id: 'edit-promotion', label: this.intl.t('storefront.promotions.common.edit'), fn: this.editPromotion, permission: 'storefront update promotion' }, + { id: 'manage-codes', label: this.intl.t('storefront.promotions.list.manage-codes'), fn: this.manageCodes, permission: 'storefront generate-codes promotion' }, + { id: 'announce', label: this.intl.t('storefront.promotions.list.announce'), fn: this.announce, permission: 'storefront announce promotion' }, { separator: true }, - { label: this.intl.t('storefront.promotions.list.activate'), fn: (promotion) => this.setStatus(promotion, 'active'), permission: 'storefront update promotion' }, - { label: this.intl.t('storefront.promotions.list.pause'), fn: (promotion) => this.setStatus(promotion, 'paused'), permission: 'storefront update promotion' }, - { label: this.intl.t('storefront.promotions.list.end'), fn: (promotion) => this.setStatus(promotion, 'ended'), permission: 'storefront update promotion' }, + { + id: 'activate', + label: this.intl.t('storefront.promotions.list.activate'), + fn: (promotion) => this.setStatus(promotion, 'active'), + permission: 'storefront update promotion', + }, + { + id: 'pause', + label: this.intl.t('storefront.promotions.list.pause'), + fn: (promotion) => this.setStatus(promotion, 'paused'), + permission: 'storefront update promotion', + }, + { + id: 'end', + label: this.intl.t('storefront.promotions.list.end'), + fn: (promotion) => this.setStatus(promotion, 'ended'), + permission: 'storefront update promotion', + }, { separator: true }, { + id: 'delete-promotion', label: this.intl.t('storefront.promotions.common.delete'), fn: this.deletePromotion, class: 'text-red-700 hover:text-red-800', @@ -233,4 +257,22 @@ export default class PromotionsIndexController extends Controller { onSuccess: () => this.hostRouter.refresh(), }); } + + /** + * The columns with what extensions registered under `storefront:table:promotion` merged in. + * + * @var {Array} + */ + @cached get registeredColumns() { + return this.mergeRegisteredColumns('promotion', this.columns); + } + + /** + * Toolbar buttons extensions registered under `storefront:table:promotion:actions`. + * + * @var {Array} + */ + get registeredActionButtons() { + return this.registeredTableActions('promotion'); + } } diff --git a/addon/controllers/promotions/segments.js b/addon/controllers/promotions/segments.js index 601989c..0c24f4d 100644 --- a/addon/controllers/promotions/segments.js +++ b/addon/controllers/promotions/segments.js @@ -1,10 +1,10 @@ -import Controller from '@ember/controller'; -import { tracked } from '@glimmer/tracking'; +import BaseController from '@fleetbase/storefront-engine/controllers/base-controller'; +import { tracked, cached } from '@glimmer/tracking'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { debounce } from '@ember/runloop'; -export default class PromotionsSegmentsController extends Controller { +export default class PromotionsSegmentsController extends BaseController { @service store; @service intl; @service storefront; @@ -12,7 +12,7 @@ export default class PromotionsSegmentsController extends Controller { @service notifications; @service crud; @service hostRouter; - queryParams = ['query', 'page']; + queryParams = this.registeredQueryParams('customer-segment', ['query', 'page']); @tracked query; @tracked page = 1; @@ -20,6 +20,7 @@ export default class PromotionsSegmentsController extends Controller { get columns() { return [ { + id: 'name', label: this.intl.t('storefront.promotions.common.name'), valuePath: 'name', width: '30%', @@ -28,18 +29,21 @@ export default class PromotionsSegmentsController extends Controller { resizable: true, }, { + id: 'description', label: this.intl.t('storefront.promotions.common.description'), valuePath: 'description', width: '40%', resizable: true, }, { + id: 'rule-count', label: this.intl.t('storefront.promotions.segments.rules'), valuePath: 'ruleCount', width: '15%', resizable: true, }, { + id: 'row-actions', label: '', cellComponent: 'table/cell/dropdown', ddButtonText: false, @@ -50,8 +54,9 @@ export default class PromotionsSegmentsController extends Controller { sticky: 'right', width: 60, actions: [ - { label: this.intl.t('storefront.promotions.common.edit'), fn: this.editSegment, permission: 'storefront update customer-segment' }, + { id: 'edit-segment', label: this.intl.t('storefront.promotions.common.edit'), fn: this.editSegment, permission: 'storefront update customer-segment' }, { + id: 'delete-segment', label: this.intl.t('storefront.promotions.common.delete'), fn: this.deleteSegment, class: 'text-red-700 hover:text-red-800', @@ -126,4 +131,22 @@ export default class PromotionsSegmentsController extends Controller { onSuccess: () => this.hostRouter.refresh(), }); } + + /** + * The columns with what extensions registered under `storefront:table:customer-segment` merged in. + * + * @var {Array} + */ + @cached get registeredColumns() { + return this.mergeRegisteredColumns('customer-segment', this.columns); + } + + /** + * Toolbar buttons extensions registered under `storefront:table:customer-segment:actions`. + * + * @var {Array} + */ + get registeredActionButtons() { + return this.registeredTableActions('customer-segment'); + } } diff --git a/addon/extension.js b/addon/extension.js index ecf7173..d6717cb 100644 --- a/addon/extension.js +++ b/addon/extension.js @@ -234,5 +234,11 @@ export default { 'fleet-ops:template:operations:orders:new:entities-input', new ExtensionComponent('@fleetbase/storefront-engine', 'add-product-as-entity-button') ); + + // Declare the registries extensions can add table columns, actions and + // buttons to, e.g. `storefront:table:order:columns` or `storefront:details:order:menu`. + universe + .getService('universe/resource-view-service') + ?.declare('storefront', ['campaign', 'customer', 'customer-segment', 'network-customer', 'network-order', 'network-store', 'order', 'promotion']); }, }; diff --git a/addon/services/storefront-order-actions.js b/addon/services/storefront-order-actions.js index 398bcb0..0580946 100644 --- a/addon/services/storefront-order-actions.js +++ b/addon/services/storefront-order-actions.js @@ -31,6 +31,7 @@ export default class StorefrontOrderActionsService extends Service { header: 'storefront/order/panel-header', tabs: this.tabsFor(hydratedOrder), actionButtons: this.actionButtonsFor(hydratedOrder, options.onChange), + registry: 'storefront:details:order', width: '560px', size: 'sm', dismissible: false, @@ -75,6 +76,7 @@ export default class StorefrontOrderActionsService extends Service { actionItemsFor(order, callback) { const isTerminal = this.storefrontOrderWorkflow.isTerminal(order); const workflowItems = this.storefrontOrderWorkflow.primaryActionDescriptorsFor(order).map((descriptor) => ({ + id: 'perform-workflow-action', text: descriptor.text, icon: descriptor.icon, type: descriptor.type, @@ -93,6 +95,7 @@ export default class StorefrontOrderActionsService extends Service { separator: true, }, { + id: 'cancel-order', text: 'Cancel order', icon: 'ban', class: 'text-danger', diff --git a/addon/templates/customers/index.hbs b/addon/templates/customers/index.hbs index a61619e..937b94d 100644 --- a/addon/templates/customers/index.hbs +++ b/addon/templates/customers/index.hbs @@ -1,4 +1,5 @@ + + +
+ + +
+ @@ -34,5 +35,5 @@
+
\ No newline at end of file diff --git a/addon/templates/orders/index.hbs b/addon/templates/orders/index.hbs index 1538dc4..debc782 100644 --- a/addon/templates/orders/index.hbs +++ b/addon/templates/orders/index.hbs @@ -1,62 +1,16 @@ - - - - {{#if (safe-has this.table "selectedRows")}} - - - - {{/if}} -
- + {{outlet}} diff --git a/addon/templates/orders/index/view.hbs b/addon/templates/orders/index/view.hbs index 2a854e1..19ec9d9 100644 --- a/addon/templates/orders/index/view.hbs +++ b/addon/templates/orders/index/view.hbs @@ -1,4 +1,5 @@