Broadleaf Microservices
  • v1.0.0-latest-prod

Unified Admin Release Notes for 1.10.1

Important Updates

  • Now supports Node 18. Node 14 has reached end-of-life and we recommend upgrading to Node 18 as soon as possible since Node 16 reaches end of life in September 2023.

    • Upgrading Node is not required to use this update.

Features & Enhancements

  • Display shipment info for a fulfillment if present at OrderFulfillment.shipment

  • Added support for rendering the Catalog Selector as part of the basic ListGrid Form Row Action similar to what the Clone action does for Products

    • This allows users to define arbitrary form actions besides cloning on list grids using metadata for Catalog entities.

  • Added "moreThan" and "lessThan" validation methods for number fields to support validating that a field is more/less than a set value.

  • Added various accessibility enhancements such as missing aria attributes and motion-reduce styles

  • Added 404 component when a route is not found rather than redirecting the user to the homepage and obfuscating the issue

  • Added the ability to manage content items from within other items.

    • Also, allows the user to create items "inline" that are considered "embedded" within the parent item

  • Added ability to enable Dev Settings mode per-user instead of only globally

    • Dev Settings allows users of the Admin to view the Metadata JSON used to render various components

    • Globally, this can be enabled using VITE_ENABLE_DEV_SETTINGS. Setting this to true will enable dev settings for all users.

    • The specific security scope required for a user to be able to use Dev Settings when VITE_ENABLE_DEV_SETTINGS is false can be specified using VITE_DEV_SETTINGS_SCOPE.

      • Default is DEV_SETTINGS. A user would then need to have the ALL_DEV_SETTINGS permission to view dev settings.

  • Added new custom form actions for updating Order Fulfillment properties.

New Modal Form Action for Fulfillments

We introduced a new form action to allow defining custom actions against an Order Fulfillment. Examples are defining in metadata an action to update the Shipment info on the Fulfillment or updating a Fulfillment’s Address. This can be referenced for the FulfillmentView (type FULFILLMENT) or ItemsOrderViewSection (type ORDER_ITEM) using the action type of MODAL_FORM.

Example configuration for an action to add Shipment info to Order Fulfillment
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.broadleafcommerce.metadata.dsl.core.OperationType;
import com.broadleafcommerce.metadata.dsl.core.extension.fields.DateField;
import com.broadleafcommerce.metadata.dsl.core.utils.Actions;
import com.broadleafcommerce.metadata.dsl.core.utils.Conditionals;
import com.broadleafcommerce.metadata.dsl.core.utils.Endpoints;
import com.broadleafcommerce.metadata.dsl.core.utils.Fields;
import com.broadleafcommerce.metadata.dsl.core.utils.ValidationMethods;
import com.broadleafcommerce.metadata.dsl.core.utils.ValidationSchemas;
import com.broadleafcommerce.metadata.dsl.registry.ComponentSource;
import com.broadleafcommerce.order.metadata.autoconfigure.OrderServicesMetadataAutoConfiguration;
import com.broadleafcommerce.order.metadata.dsl.extension.views.FulfillmentView;
import com.broadleafcommerce.order.metadata.dsl.extension.views.OrderView;
import com.broadleafcommerce.order.metadata.support.OrderFulfillmentStatuses;
import com.broadleafcommerce.order.metadata.support.OrderIds;
import com.broadleafcommerce.order.metadata.support.OrderPaths;
import com.broadleafcommerce.order.metadata.support.OrderProps;
import com.broadleafcommerce.order.metadata.support.OrderScopes;

import java.util.List;

import lombok.RequiredArgsConstructor;
import lombok.experimental.UtilityClass;

@Configuration
@RequiredArgsConstructor
@AutoConfigureAfter(OrderServicesMetadataAutoConfiguration.class)
public class OrderMetadataAutoConfiguration {

    @UtilityClass
    static class ShipmentProps {
        public final String SHIPMENT = "shipment";
        public final String TRACKING_NUMBER = SHIPMENT +  ".trackingNumber";
        public final String TRACKING_URL = SHIPMENT + ".trackingUrl";
        public final String SHIP_DATE = SHIPMENT + ".shipDate";
        public final String SHIPPER_TYPE = SHIPMENT + ".shipperType";
    }

    @Bean
    public ComponentSource addCustomFulfillmentAction() {
        return registry -> {
            FulfillmentView<?> fulfillmentView =
                    (FulfillmentView<?>) registry.get(OrderIds.FULFILLMENT_DETAILS);
            fulfillmentView
                    .addAction("MODAL_FORM", Actions.action()
                            .label("order.actions.add-shipment.label")
                            .operationType(OperationType.UPDATE)
                            .placement("PRIMARY")
                            .type("MODAL_FORM")
                            // since actions don't normally have conditionals, they need to be
                            // as an attribute if needed added
                            .attribute("conditionals", List.of(
                                    Conditionals.when(OrderProps.OrderFulfillment.STATUS)
                                            .equalTo(OrderFulfillmentStatuses.FULFILLED)))
                            .addEndpoint("UPDATE", Endpoints.put()
                                    .uri(OrderPaths.ORDER_FULFILLMENTS + "/${fulfillment.id}")
                                    .scope(OrderScopes.ORDER_FULFILLMENT))
                            .addComponent(ShipmentProps.TRACKING_NUMBER, Fields.string()
                                    .name(ShipmentProps.TRACKING_NUMBER)
                                    .label("order.fields.shipment.tracking-number.label")
                                    .order(1))
                            .addComponent(ShipmentProps.TRACKING_URL, Fields.string()
                                    .name(ShipmentProps.TRACKING_URL)
                                    .label("order.fields.shipment.tracking-url.label")
                                    .order(2)
                                    .validationSchema(ValidationSchemas.string()
                                            .method(ValidationMethods.urlAbsoluteOrRelative(
                                                    "order.fields.shipment.tracking-url.valid"))))
                            .addComponent(ShipmentProps.SHIP_DATE, Fields.date()
                                    .name(ShipmentProps.SHIP_DATE)
                                    .label("order.fields.shipment.ship-date.label")
                                    .defaultStartOf(DateField.DateStartUnit.DAY)
                                    .order(3))
                            .addComponent(ShipmentProps.SHIPPER_TYPE, Fields.string()
                                    .name(ShipmentProps.SHIPPER_TYPE)
                                    .label("order.fields.shipment.shipper-type.label")
                                    .order(4)));
        };
    }

    @Bean
    public ComponentSource addCustomItemOrderAction() {
        return registry -> {
            OrderView<?> orderView =
                    (OrderView<?>) registry.get(OrderIds.DETAILS);
            orderView
                    .getItemsSection()
                    .addAction("MODAL_FORM", Actions.action()
                            .label("order.actions.add-shipment.label")
                            .operationType(OperationType.UPDATE)
                            .placement("CARD")
                            .type("MODAL_FORM")
                            // since actions don't normally have conditionals, they need to be added
                            // as an attribute if needed
                            .attribute("conditionals", List.of(
                                    Conditionals.when(OrderProps.OrderFulfillment.STATUS)
                                            .equalTo(OrderFulfillmentStatuses.FULFILLED)))
                            .addEndpoint("UPDATE", Endpoints.put()
                                    .uri(OrderPaths.ORDER_FULFILLMENTS + "/${fulfillment.id}")
                                    .scope(OrderScopes.ORDER_FULFILLMENT))
                            .addComponent(ShipmentProps.TRACKING_NUMBER, Fields.string()
                                    .name(ShipmentProps.TRACKING_NUMBER)
                                    .label("order.fields.shipment.tracking-number.label")
                                    .order(1))
                            .addComponent(ShipmentProps.TRACKING_URL, Fields.string()
                                    .name(ShipmentProps.TRACKING_URL)
                                    .label("order.fields.shipment.tracking-url.label")
                                    .order(2)
                                    .validationSchema(ValidationSchemas.string()
                                            .method(ValidationMethods.urlAbsoluteOrRelative(
                                                    "order.fields.shipment.tracking-url.valid"))))
                            .addComponent(ShipmentProps.SHIP_DATE, Fields.date()
                                    .name(ShipmentProps.SHIP_DATE)
                                    .label("order.fields.shipment.ship-date.label")
                                    .defaultStartOf(DateField.DateStartUnit.DAY)
                                    .order(3))
                            .addComponent(ShipmentProps.SHIPPER_TYPE, Fields.string()
                                    .name(ShipmentProps.SHIPPER_TYPE)
                                    .label("order.fields.shipment.shipper-type.label")
                                    .order(4)));
        };
    }

}
Example translatable text
order.fields.shipment.tracking-number.label=Tracking Number
order.fields.shipment.tracking-url.label=Tracking URL
order.fields.shipment.tracking-url.valid=Must be a valid url
order.fields.shipment.ship-date.label=Ship Date
order.fields.shipment.shipper-type.label=Carrier

order.actions.add-shipment.label=Add Shipping Info

Bug Fixes

  • Fixed issue with inline filters are covered by listgrid headers

  • Fixed Content Item translations including non-translatable fields

  • Fixed cache name conflict between auth state and transaction caches

  • Fixed not reloading content items and models when Profile is changed

  • Fixed errors for resident grid collections not rendering correctly when there’s a field validation error for one of the collection’s fields

  • Fixed error when trying to select a Variation for a "Single Variation" Item Choice Product Option

  • Fixed content item dropdown not populating options when field type is reference

  • Fixed tooltip for the Applications in the Application Selector not rendering correctly

  • Fixed support for populating a Currency field’s value based on the value of another field

    • For example, Offer#currency specifies the currency of the offer, which will be used as currencyCode for the other money fields, but, if the Offer#currency field is being edited, an error will be thrown immediately for the other money fields since the currencyCode is not yet complete and invalid.

  • Fixed not being upload assets for Theme Fields.

  • Fixed issue where a Date column in a resident grid or similar component would throw an error when the entity was updated.

  • Fixed issue with implicit filters not applying introduced in 1.9.2.

  • Addressed some typescript type import issues and access to the useContextParams hook for the Fulfillment and Order Items view components

  • Fixed validation of URLs that can be absolute or relative. Before, we were not validating that relative paths had to start with a slash.