@aglyn/plugins-commerce 1.0.0-beta.230 → 1.0.0-beta.232
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/index.d.ts +0 -1
- package/src/index.js +0 -1
- package/src/index.js.map +1 -1
- package/src/lib/barcode/barcode-decode.d.ts +71 -0
- package/src/lib/barcode/barcode-decode.js +498 -0
- package/src/lib/barcode/barcode-decode.js.map +1 -0
- package/src/lib/barcode/barcode-encode.d.ts +44 -0
- package/src/lib/barcode/barcode-encode.js +138 -0
- package/src/lib/barcode/barcode-encode.js.map +1 -0
- package/src/lib/barcode/barcode-scanner.component.d.ts +23 -0
- package/src/lib/barcode/barcode-scanner.component.js +260 -0
- package/src/lib/barcode/barcode-scanner.component.js.map +1 -0
- package/src/lib/barcode/scan-button.component.d.ts +10 -0
- package/src/lib/barcode/scan-button.component.js +62 -0
- package/src/lib/barcode/scan-button.component.js.map +1 -0
- package/src/lib/barcode/scanner-wedge.d.ts +35 -0
- package/src/lib/barcode/scanner-wedge.js +108 -0
- package/src/lib/barcode/scanner-wedge.js.map +1 -0
- package/src/lib/components/account.js +10 -1
- package/src/lib/components/account.js.map +1 -1
- package/src/lib/components/cart-extras.d.ts +45 -0
- package/src/lib/components/cart-extras.js +127 -0
- package/src/lib/components/cart-extras.js.map +1 -0
- package/src/lib/components/cart.js +82 -15
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/checkout-return-notice.d.ts +26 -0
- package/src/lib/components/checkout-return-notice.js +48 -0
- package/src/lib/components/checkout-return-notice.js.map +1 -0
- package/src/lib/components/commerce-console-page.js +124 -0
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/commerce-console-sections.d.ts +1 -1
- package/src/lib/components/commerce-console-sections.js +4 -0
- package/src/lib/components/commerce-console-sections.js.map +1 -1
- package/src/lib/components/console/bulk-fulfill.d.ts +44 -0
- package/src/lib/components/console/bulk-fulfill.js +84 -0
- package/src/lib/components/console/bulk-fulfill.js.map +1 -0
- package/src/lib/components/console/catalog-organization-card.component.js +1 -0
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
- package/src/lib/components/console/customer-notifications-card.component.d.ts +19 -0
- package/src/lib/components/console/customer-notifications-card.component.js +149 -0
- package/src/lib/components/console/customer-notifications-card.component.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.js +88 -6
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +164 -57
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +209 -61
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.d.ts +48 -0
- package/src/lib/components/console/order-fulfillment-panel.component.js +542 -0
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -0
- package/src/lib/components/console/order-invoice.component.d.ts +33 -0
- package/src/lib/components/console/order-invoice.component.js +81 -0
- package/src/lib/components/console/order-invoice.component.js.map +1 -0
- package/src/lib/components/console/order-receipt-resend.component.d.ts +21 -0
- package/src/lib/components/console/order-receipt-resend.component.js +203 -0
- package/src/lib/components/console/order-receipt-resend.component.js.map +1 -0
- package/src/lib/components/console/order-returns.component.d.ts +36 -0
- package/src/lib/components/console/order-returns.component.js +142 -0
- package/src/lib/components/console/order-returns.component.js.map +1 -0
- package/src/lib/components/console/order-shipping-actions.component.d.ts +54 -0
- package/src/lib/components/console/order-shipping-actions.component.js +149 -0
- package/src/lib/components/console/order-shipping-actions.component.js.map +1 -0
- package/src/lib/components/console/order-webhooks-card.component.d.ts +50 -0
- package/src/lib/components/console/order-webhooks-card.component.js +623 -0
- package/src/lib/components/console/order-webhooks-card.component.js.map +1 -0
- package/src/lib/components/console/order-zones.d.ts +155 -0
- package/src/lib/components/console/order-zones.js +40 -0
- package/src/lib/components/console/order-zones.js.map +1 -0
- package/src/lib/components/console/payment-methods-card.component.d.ts +67 -0
- package/src/lib/components/console/payment-methods-card.component.js +395 -0
- package/src/lib/components/console/payment-methods-card.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-api.d.ts +124 -0
- package/src/lib/components/console/pos/pos-api.js +101 -0
- package/src/lib/components/console/pos/pos-api.js.map +1 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +36 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js +160 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +39 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js +352 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-product-grid.component.d.ts +47 -0
- package/src/lib/components/console/pos/pos-product-grid.component.js +272 -0
- package/src/lib/components/console/pos/pos-product-grid.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.d.ts +27 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.js +282 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +53 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js +445 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +31 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js +640 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.d.ts +43 -0
- package/src/lib/components/console/pos/use-pos-display.js +161 -0
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -0
- package/src/lib/components/console/pos-devices-card.component.d.ts +14 -0
- package/src/lib/components/console/pos-devices-card.component.js +546 -0
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -0
- package/src/lib/components/console/pos-display/brand-screen.d.ts +33 -0
- package/src/lib/components/console/pos-display/brand-screen.js +85 -0
- package/src/lib/components/console/pos-display/brand-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/cart-screen.d.ts +29 -0
- package/src/lib/components/console/pos-display/cart-screen.js +166 -0
- package/src/lib/components/console/pos-display/cart-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/pairing-screen.d.ts +24 -0
- package/src/lib/components/console/pos-display/pairing-screen.js +127 -0
- package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/pos-display-api.d.ts +90 -0
- package/src/lib/components/console/pos-display/pos-display-api.js +140 -0
- package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -0
- package/src/lib/components/console/pos-display/receipt-screen.d.ts +35 -0
- package/src/lib/components/console/pos-display/receipt-screen.js +200 -0
- package/src/lib/components/console/pos-display/receipt-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/tip-screen.d.ts +36 -0
- package/src/lib/components/console/pos-display/tip-screen.js +211 -0
- package/src/lib/components/console/pos-display/tip-screen.js.map +1 -0
- package/src/lib/components/console/pos-display-page.component.d.ts +16 -0
- package/src/lib/components/console/pos-display-page.component.js +407 -0
- package/src/lib/components/console/pos-display-page.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +40 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +289 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.d.ts +34 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.js +61 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +38 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +129 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +37 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.js +60 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.d.ts +48 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.js +220 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.d.ts +63 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js +186 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-receipt.d.ts +39 -0
- package/src/lib/components/console/pos-ops/pos-receipt.js +182 -0
- package/src/lib/components/console/pos-ops/pos-receipt.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.d.ts +40 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.js +465 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.d.ts +29 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +253 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +34 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +494 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.d.ts +37 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.js +102 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.d.ts +30 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js +277 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/register-ops.d.ts +28 -0
- package/src/lib/components/console/pos-ops/register-ops.js +28 -0
- package/src/lib/components/console/pos-ops/register-ops.js.map +1 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.d.ts +46 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.js +128 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.js.map +1 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.d.ts +36 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js +64 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js.map +1 -0
- package/src/lib/components/console/pos-page.component.d.ts +13 -5
- package/src/lib/components/console/pos-page.component.js +756 -654
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/pos-zones.d.ts +23 -0
- package/src/lib/components/console/pos-zones.js +31 -0
- package/src/lib/components/console/pos-zones.js.map +1 -0
- package/src/lib/components/console/product-editor-dialog.component.js +61 -71
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-labels-dialog.component.d.ts +13 -0
- package/src/lib/components/console/product-labels-dialog.component.js +197 -0
- package/src/lib/components/console/product-labels-dialog.component.js.map +1 -0
- package/src/lib/components/console/product-register-fields.component.d.ts +19 -0
- package/src/lib/components/console/product-register-fields.component.js +241 -0
- package/src/lib/components/console/product-register-fields.component.js.map +1 -0
- package/src/lib/components/console/product-zones.d.ts +44 -0
- package/src/lib/components/console/product-zones.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +23 -20
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/register-hardware-card.component.d.ts +33 -0
- package/src/lib/components/console/register-hardware-card.component.js +826 -0
- package/src/lib/components/console/register-hardware-card.component.js.map +1 -0
- package/src/lib/components/console/registers-card.component.js +24 -5
- package/src/lib/components/console/registers-card.component.js.map +1 -1
- package/src/lib/components/console/return-actions.d.ts +38 -0
- package/src/lib/components/console/return-actions.js +76 -0
- package/src/lib/components/console/return-actions.js.map +1 -0
- package/src/lib/components/console/return-detail-dialog.component.d.ts +34 -0
- package/src/lib/components/console/return-detail-dialog.component.js +718 -0
- package/src/lib/components/console/return-detail-dialog.component.js.map +1 -0
- package/src/lib/components/console/return-settings-card.component.d.ts +34 -0
- package/src/lib/components/console/return-settings-card.component.js +195 -0
- package/src/lib/components/console/return-settings-card.component.js.map +1 -0
- package/src/lib/components/console/return-zones.d.ts +87 -0
- package/src/lib/components/console/return-zones.js +31 -0
- package/src/lib/components/console/return-zones.js.map +1 -0
- package/src/lib/components/console/returns-card.component.d.ts +45 -0
- package/src/lib/components/console/returns-card.component.js +159 -0
- package/src/lib/components/console/returns-card.component.js.map +1 -0
- package/src/lib/components/console/shipping-settings-card.component.js +161 -5
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.d.ts +59 -0
- package/src/lib/components/console/shippingeasy-card.component.js +378 -0
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -0
- package/src/lib/components/console/shipstation-card.component.d.ts +35 -0
- package/src/lib/components/console/shipstation-card.component.js +345 -0
- package/src/lib/components/console/shipstation-card.component.js.map +1 -0
- package/src/lib/components/console/start-return-dialog.component.d.ts +40 -0
- package/src/lib/components/console/start-return-dialog.component.js +243 -0
- package/src/lib/components/console/start-return-dialog.component.js.map +1 -0
- package/src/lib/components/console/stock-movements-card.component.js +2 -1
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +2 -66
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/store-zones.d.ts +42 -0
- package/src/lib/components/console/store-zones.js +39 -0
- package/src/lib/components/console/store-zones.js.map +1 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
- package/src/lib/components/order-status.d.ts +36 -0
- package/src/lib/components/order-status.js +502 -0
- package/src/lib/components/order-status.js.map +1 -0
- package/src/lib/components/product-detail.js +41 -2
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/return-request.d.ts +32 -0
- package/src/lib/components/return-request.js +576 -0
- package/src/lib/components/return-request.js.map +1 -0
- package/src/lib/components/storefront-payment-appearance.d.ts +32 -0
- package/src/lib/components/storefront-payment-appearance.js +114 -0
- package/src/lib/components/storefront-payment-appearance.js.map +1 -0
- package/src/lib/components/storefront-payment-element.d.ts +57 -1
- package/src/lib/components/storefront-payment-element.js +332 -33
- package/src/lib/components/storefront-payment-element.js.map +1 -1
- package/src/lib/constants/order-status.d.ts +22 -0
- package/src/lib/constants/order-status.js +22 -0
- package/src/lib/constants/order-status.js.map +1 -0
- package/src/lib/constants/return-request.d.ts +27 -0
- package/src/lib/constants/return-request.js +32 -0
- package/src/lib/constants/return-request.js.map +1 -0
- package/src/lib/constants/stock-movement-query.js +2 -1
- package/src/lib/constants/stock-movement-query.js.map +1 -1
- package/src/lib/declarations.console-server.js +9 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.server.d.ts +2 -1
- package/src/lib/declarations.server.js +68 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/buyer-notifications.d.ts +59 -0
- package/src/lib/model/buyer-notifications.js +97 -0
- package/src/lib/model/buyer-notifications.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +171 -2
- package/src/lib/model/commerce-orders.js +26 -16
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-payment-methods.d.ts +146 -0
- package/src/lib/model/commerce-payment-methods.js +247 -0
- package/src/lib/model/commerce-payment-methods.js.map +1 -0
- package/src/lib/model/commerce-pos-ops.d.ts +437 -0
- package/src/lib/model/commerce-pos-ops.js +721 -0
- package/src/lib/model/commerce-pos-ops.js.map +1 -0
- package/src/lib/model/commerce-pos.d.ts +263 -0
- package/src/lib/model/commerce-pos.js +375 -0
- package/src/lib/model/commerce-pos.js.map +1 -0
- package/src/lib/model/commerce-printers.d.ts +211 -0
- package/src/lib/model/commerce-printers.js +181 -0
- package/src/lib/model/commerce-printers.js.map +1 -0
- package/src/lib/model/commerce-receipt.d.ts +97 -0
- package/src/lib/model/commerce-receipt.js +180 -0
- package/src/lib/model/commerce-receipt.js.map +1 -0
- package/src/lib/model/commerce-returns.d.ts +207 -0
- package/src/lib/model/commerce-returns.js +300 -0
- package/src/lib/model/commerce-returns.js.map +1 -0
- package/src/lib/model/commerce-shipping.d.ts +44 -1
- package/src/lib/model/commerce-shipping.js +53 -9
- package/src/lib/model/commerce-shipping.js.map +1 -1
- package/src/lib/model/commerce-tax-engine.d.ts +90 -0
- package/src/lib/model/commerce-tax-engine.js +129 -0
- package/src/lib/model/commerce-tax-engine.js.map +1 -0
- package/src/lib/model/commerce.d.ts +91 -2
- package/src/lib/model/commerce.js +89 -1
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +16 -0
- package/src/lib/model/index.js +16 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-events.d.ts +154 -0
- package/src/lib/model/order-events.js +153 -0
- package/src/lib/model/order-events.js.map +1 -0
- package/src/lib/model/order-fulfillment.d.ts +117 -0
- package/src/lib/model/order-fulfillment.js +217 -0
- package/src/lib/model/order-fulfillment.js.map +1 -0
- package/src/lib/model/order-history.d.ts +58 -0
- package/src/lib/model/order-history.js +22 -0
- package/src/lib/model/order-history.js.map +1 -0
- package/src/lib/model/order-list-fields.d.ts +9 -1
- package/src/lib/model/order-list-fields.js +7 -1
- package/src/lib/model/order-list-fields.js.map +1 -1
- package/src/lib/model/order-shipping-export.d.ts +91 -0
- package/src/lib/model/order-shipping-export.js +192 -0
- package/src/lib/model/order-shipping-export.js.map +1 -0
- package/src/lib/model/order-status-view.d.ts +103 -0
- package/src/lib/model/order-status-view.js +156 -0
- package/src/lib/model/order-status-view.js.map +1 -0
- package/src/lib/model/order-webhooks.d.ts +142 -0
- package/src/lib/model/order-webhooks.js +91 -0
- package/src/lib/model/order-webhooks.js.map +1 -0
- package/src/lib/model/product-channel.d.ts +56 -0
- package/src/lib/model/product-channel.js +86 -0
- package/src/lib/model/product-channel.js.map +1 -0
- package/src/lib/model/product-modifiers.d.ts +80 -0
- package/src/lib/model/product-modifiers.js +140 -0
- package/src/lib/model/product-modifiers.js.map +1 -0
- package/src/lib/model/product-write.d.ts +186 -0
- package/src/lib/model/product-write.js +116 -0
- package/src/lib/model/product-write.js.map +1 -0
- package/src/lib/model/shippingeasy.d.ts +94 -0
- package/src/lib/model/shippingeasy.js +267 -0
- package/src/lib/model/shippingeasy.js.map +1 -0
- package/src/lib/model/shipstation.d.ts +125 -0
- package/src/lib/model/shipstation.js +399 -0
- package/src/lib/model/shipstation.js.map +1 -0
- package/src/lib/model/smart-collection-reads.d.ts +32 -0
- package/src/lib/model/smart-collection-reads.js +63 -0
- package/src/lib/model/smart-collection-reads.js.map +1 -0
- package/src/lib/model/tracking-url.d.ts +74 -0
- package/src/lib/model/tracking-url.js +159 -0
- package/src/lib/model/tracking-url.js.map +1 -0
- package/src/lib/plugin-config.d.ts +13 -0
- package/src/lib/plugin-config.js +77 -5
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +26 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/pos-ops-config.d.ts +39 -0
- package/src/lib/pos-ops-config.js +88 -0
- package/src/lib/pos-ops-config.js.map +1 -0
- package/src/lib/printing/print-document.d.ts +106 -0
- package/src/lib/printing/print-document.js +464 -0
- package/src/lib/printing/print-document.js.map +1 -0
- package/src/lib/printing/product-labels.d.ts +58 -0
- package/src/lib/printing/product-labels.js +116 -0
- package/src/lib/printing/product-labels.js.map +1 -0
- package/src/lib/printing/render-epson.d.ts +57 -0
- package/src/lib/printing/render-epson.js +135 -0
- package/src/lib/printing/render-epson.js.map +1 -0
- package/src/lib/printing/render-star.d.ts +45 -0
- package/src/lib/printing/render-star.js +100 -0
- package/src/lib/printing/render-star.js.map +1 -0
- package/src/lib/printing/render-text.d.ts +25 -0
- package/src/lib/printing/render-text.js +53 -0
- package/src/lib/printing/render-text.js.map +1 -0
- package/src/lib/server/api-v1/openapi.js +27 -4
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +67 -0
- package/src/lib/server/api-v1/order-view.js +120 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -0
- package/src/lib/server/api-v1/orders-and-products.js +58 -65
- package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
- package/src/lib/server/billing-webhook.js +135 -28
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cancel-order.js +29 -0
- package/src/lib/server/cancel-order.js.map +1 -1
- package/src/lib/server/carrier-rates-availability.d.ts +26 -0
- package/src/lib/server/carrier-rates-availability.js +76 -0
- package/src/lib/server/carrier-rates-availability.js.map +1 -0
- package/src/lib/server/carrier-shipping.d.ts +84 -0
- package/src/lib/server/carrier-shipping.js +135 -0
- package/src/lib/server/carrier-shipping.js.map +1 -0
- package/src/lib/server/cart-checkout.d.ts +2 -0
- package/src/lib/server/cart-checkout.js +226 -22
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/channel-order-updates.d.ts +50 -0
- package/src/lib/server/channel-order-updates.js +290 -0
- package/src/lib/server/channel-order-updates.js.map +1 -0
- package/src/lib/server/channel-orders.d.ts +85 -0
- package/src/lib/server/channel-orders.js +543 -0
- package/src/lib/server/channel-orders.js.map +1 -0
- package/src/lib/server/checkout-extras.d.ts +64 -0
- package/src/lib/server/checkout-extras.js +157 -0
- package/src/lib/server/checkout-extras.js.map +1 -0
- package/src/lib/server/{feed.d.ts → checkout-status.d.ts} +2 -7
- package/src/lib/server/checkout-status.js +111 -0
- package/src/lib/server/checkout-status.js.map +1 -0
- package/src/lib/server/checkout.js +86 -9
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/draft-order.js +34 -5
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +127 -10
- package/src/lib/server/fulfill-order.js +451 -49
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/legacy-feed.d.ts +28 -0
- package/src/lib/server/legacy-feed.js +52 -0
- package/src/lib/server/legacy-feed.js.map +1 -0
- package/src/lib/server/native-checkout.d.ts +34 -15
- package/src/lib/server/native-checkout.js +32 -17
- package/src/lib/server/native-checkout.js.map +1 -1
- package/src/lib/server/order-event-host-emit.d.ts +23 -0
- package/src/lib/server/order-event-host-emit.js +30 -0
- package/src/lib/server/order-event-host-emit.js.map +1 -0
- package/src/lib/server/order-event-triggers.d.ts +37 -0
- package/src/lib/server/order-event-triggers.js +119 -0
- package/src/lib/server/order-event-triggers.js.map +1 -0
- package/src/lib/server/order-events.d.ts +62 -0
- package/src/lib/server/order-events.js +109 -0
- package/src/lib/server/order-events.js.map +1 -0
- package/src/lib/server/order-history.d.ts +23 -0
- package/src/lib/server/order-history.js +65 -0
- package/src/lib/server/order-history.js.map +1 -0
- package/src/lib/server/order-notifications.d.ts +185 -0
- package/src/lib/server/order-notifications.js +567 -0
- package/src/lib/server/order-notifications.js.map +1 -0
- package/src/lib/server/order-receipt-send.d.ts +39 -0
- package/src/lib/server/order-receipt-send.js +151 -0
- package/src/lib/server/order-receipt-send.js.map +1 -0
- package/src/lib/server/order-status-token.d.ts +31 -0
- package/src/lib/server/order-status-token.js +75 -0
- package/src/lib/server/order-status-token.js.map +1 -0
- package/src/lib/server/order-status.d.ts +58 -0
- package/src/lib/server/order-status.js +141 -0
- package/src/lib/server/order-status.js.map +1 -0
- package/src/lib/server/order-webhooks.d.ts +108 -0
- package/src/lib/server/order-webhooks.js +622 -0
- package/src/lib/server/order-webhooks.js.map +1 -0
- package/src/lib/server/orders-shipping-prepare.d.ts +26 -0
- package/src/lib/server/orders-shipping-prepare.js +121 -0
- package/src/lib/server/orders-shipping-prepare.js.map +1 -0
- package/src/lib/server/payment-method-domains.d.ts +209 -0
- package/src/lib/server/payment-method-domains.js +444 -0
- package/src/lib/server/payment-method-domains.js.map +1 -0
- package/src/lib/server/payment-methods.d.ts +77 -0
- package/src/lib/server/payment-methods.js +284 -0
- package/src/lib/server/payment-methods.js.map +1 -0
- package/src/lib/server/pos-auth.d.ts +50 -0
- package/src/lib/server/pos-auth.js +121 -0
- package/src/lib/server/pos-auth.js.map +1 -0
- package/src/lib/server/pos-customer.d.ts +49 -0
- package/src/lib/server/pos-customer.js +257 -0
- package/src/lib/server/pos-customer.js.map +1 -0
- package/src/lib/server/pos-display.d.ts +57 -0
- package/src/lib/server/pos-display.js +419 -0
- package/src/lib/server/pos-display.js.map +1 -0
- package/src/lib/server/pos-fee-month.d.ts +34 -0
- package/src/lib/server/pos-fee-month.js +36 -0
- package/src/lib/server/pos-fee-month.js.map +1 -0
- package/src/lib/server/pos-ops-gate.d.ts +161 -0
- package/src/lib/server/pos-ops-gate.js +295 -0
- package/src/lib/server/pos-ops-gate.js.map +1 -0
- package/src/lib/server/pos-ops-routes.d.ts +22 -0
- package/src/lib/server/pos-ops-routes.js +36 -0
- package/src/lib/server/pos-ops-routes.js.map +1 -0
- package/src/lib/server/pos-order.d.ts +1 -18
- package/src/lib/server/pos-order.js +222 -42
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.d.ts +39 -0
- package/src/lib/server/pos-payment.js +922 -0
- package/src/lib/server/pos-payment.js.map +1 -0
- package/src/lib/server/pos-print.d.ts +82 -0
- package/src/lib/server/pos-print.js +193 -0
- package/src/lib/server/pos-print.js.map +1 -0
- package/src/lib/server/pos-readers.d.ts +27 -0
- package/src/lib/server/pos-readers.js +276 -0
- package/src/lib/server/pos-readers.js.map +1 -0
- package/src/lib/server/pos-refund-authority.d.ts +60 -0
- package/src/lib/server/pos-refund-authority.js +72 -0
- package/src/lib/server/pos-refund-authority.js.map +1 -0
- package/src/lib/server/pos-return.d.ts +58 -0
- package/src/lib/server/pos-return.js +891 -0
- package/src/lib/server/pos-return.js.map +1 -0
- package/src/lib/server/pos-sale-stamp.d.ts +66 -0
- package/src/lib/server/pos-sale-stamp.js +84 -0
- package/src/lib/server/pos-sale-stamp.js.map +1 -0
- package/src/lib/server/pos-sale.d.ts +170 -0
- package/src/lib/server/pos-sale.js +461 -0
- package/src/lib/server/pos-sale.js.map +1 -0
- package/src/lib/server/pos-shift.d.ts +45 -0
- package/src/lib/server/pos-shift.js +484 -0
- package/src/lib/server/pos-shift.js.map +1 -0
- package/src/lib/server/pos-staff-pin.d.ts +26 -0
- package/src/lib/server/pos-staff-pin.js +339 -0
- package/src/lib/server/pos-staff-pin.js.map +1 -0
- package/src/lib/server/pos-stripe.d.ts +91 -0
- package/src/lib/server/pos-stripe.js +129 -0
- package/src/lib/server/pos-stripe.js.map +1 -0
- package/src/lib/server/pos-terminal-connection-token.d.ts +48 -0
- package/src/lib/server/pos-terminal-connection-token.js +166 -0
- package/src/lib/server/pos-terminal-connection-token.js.map +1 -0
- package/src/lib/server/pos-terminal.d.ts +218 -0
- package/src/lib/server/pos-terminal.js +1014 -0
- package/src/lib/server/pos-terminal.js.map +1 -0
- package/src/lib/server/print-queue.d.ts +124 -0
- package/src/lib/server/print-queue.js +301 -0
- package/src/lib/server/print-queue.js.map +1 -0
- package/src/lib/server/printer-poll.d.ts +75 -0
- package/src/lib/server/printer-poll.js +325 -0
- package/src/lib/server/printer-poll.js.map +1 -0
- package/src/lib/server/printer-secret.d.ts +39 -0
- package/src/lib/server/printer-secret.js +56 -0
- package/src/lib/server/printer-secret.js.map +1 -0
- package/src/lib/server/printers.d.ts +114 -0
- package/src/lib/server/printers.js +546 -0
- package/src/lib/server/printers.js.map +1 -0
- package/src/lib/server/product-catalog.d.ts +42 -0
- package/src/lib/server/product-catalog.js +309 -0
- package/src/lib/server/product-catalog.js.map +1 -0
- package/src/lib/server/product-drafts.d.ts +5 -1
- package/src/lib/server/product-drafts.js +3 -3
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product-writer.d.ts +19 -0
- package/src/lib/server/product-writer.js +261 -0
- package/src/lib/server/product-writer.js.map +1 -0
- package/src/lib/server/products-write.d.ts +61 -0
- package/src/lib/server/products-write.js +375 -0
- package/src/lib/server/products-write.js.map +1 -0
- package/src/lib/server/refund.js +110 -45
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/reserve.js +2 -0
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/return-status-action.d.ts +37 -0
- package/src/lib/server/return-status-action.js +57 -0
- package/src/lib/server/return-status-action.js.map +1 -0
- package/src/lib/server/returns.d.ts +119 -0
- package/src/lib/server/returns.js +938 -0
- package/src/lib/server/returns.js.map +1 -0
- package/src/lib/server/shipment-records.d.ts +36 -0
- package/src/lib/server/shipment-records.js +289 -0
- package/src/lib/server/shipment-records.js.map +1 -0
- package/src/lib/server/shipping-connector-gates.d.ts +40 -0
- package/src/lib/server/shipping-connector-gates.js +97 -0
- package/src/lib/server/shipping-connector-gates.js.map +1 -0
- package/src/lib/server/shipping-connectors.d.ts +29 -0
- package/src/lib/server/shipping-connectors.js +236 -0
- package/src/lib/server/shipping-connectors.js.map +1 -0
- package/src/lib/server/shippingeasy.d.ts +238 -0
- package/src/lib/server/shippingeasy.js +787 -0
- package/src/lib/server/shippingeasy.js.map +1 -0
- package/src/lib/server/shipstation.d.ts +129 -0
- package/src/lib/server/shipstation.js +483 -0
- package/src/lib/server/shipstation.js.map +1 -0
- package/src/lib/server/site-page-resolver.d.ts +5 -0
- package/src/lib/server/site-page-resolver.js +156 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server/stock-levels.d.ts +21 -0
- package/src/lib/server/stock-levels.js +263 -0
- package/src/lib/server/stock-levels.js.map +1 -0
- package/src/lib/server/stripe-refund.d.ts +47 -0
- package/src/lib/server/stripe-refund.js +87 -0
- package/src/lib/server/stripe-refund.js.map +1 -0
- package/src/lib/server/supplier-update.js +46 -5
- package/src/lib/server/supplier-update.js.map +1 -1
- package/src/lib/server/tax-engine-quote.d.ts +47 -0
- package/src/lib/server/tax-engine-quote.js +105 -0
- package/src/lib/server/tax-engine-quote.js.map +1 -0
- package/src/lib/server.js +143 -2
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +12 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/subprocessors.d.ts +26 -0
- package/src/lib/subprocessors.js +50 -0
- package/src/lib/subprocessors.js.map +1 -0
- package/src/lib/tenant-emails.js +461 -3
- package/src/lib/tenant-emails.js.map +1 -1
- package/src/lib/testing/fake-firestore.d.ts +45 -0
- package/src/lib/testing/fake-firestore.js +189 -0
- package/src/lib/testing/fake-firestore.js.map +1 -0
- package/src/lib/testing/memory-firestore.d.ts +136 -0
- package/src/lib/testing/memory-firestore.js +282 -0
- package/src/lib/testing/memory-firestore.js.map +1 -0
- package/src/lib/testing/pos-ops-harness.d.ts +50 -0
- package/src/lib/testing/pos-ops-harness.js +184 -0
- package/src/lib/testing/pos-ops-harness.js.map +1 -0
- package/src/lib/testing/pos-ops-memory-firestore.d.ts +55 -0
- package/src/lib/testing/pos-ops-memory-firestore.js +376 -0
- package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -0
- package/src/lib/transfer/products.server.d.ts +5 -0
- package/src/lib/transfer/products.server.js +2 -2
- package/src/lib/transfer/products.server.js.map +1 -1
- package/src/lib/transfer/records-transfer.d.ts +11 -1
- package/src/lib/transfer/records-transfer.js +127 -5
- package/src/lib/transfer/records-transfer.js.map +1 -1
- package/src/lib/transfer/records.server.d.ts +9 -2
- package/src/lib/transfer/records.server.js +58 -7
- package/src/lib/transfer/records.server.js.map +1 -1
- package/src/lib/transfer/register-transfer-resources.js +40 -1
- package/src/lib/transfer/register-transfer-resources.js.map +1 -1
- package/src/lib/transfer/register-transfer-ui.js +4 -1
- package/src/lib/transfer/register-transfer-ui.js.map +1 -1
- package/src/lib/transfer/shipping-presets.d.ts +23 -0
- package/src/lib/transfer/shipping-presets.js +156 -0
- package/src/lib/transfer/shipping-presets.js.map +1 -0
- package/src/lib/transfer/tracking-transfer.d.ts +101 -0
- package/src/lib/transfer/tracking-transfer.js +546 -0
- package/src/lib/transfer/tracking-transfer.js.map +1 -0
- package/src/lib/transfer/tracking.server.d.ts +37 -0
- package/src/lib/transfer/tracking.server.js +393 -0
- package/src/lib/transfer/tracking.server.js.map +1 -0
- package/src/lib/transfer/transfer-keys.d.ts +2 -1
- package/src/lib/transfer/transfer-keys.js +6 -2
- package/src/lib/transfer/transfer-keys.js.map +1 -1
- package/src/lib/utils/code128.d.ts +37 -0
- package/src/lib/utils/code128.js +199 -0
- package/src/lib/utils/code128.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +55 -0
- package/src/lib/utils/order-invoice.js +127 -0
- package/src/lib/utils/order-invoice.js.map +1 -0
- package/src/lib/utils/use-checkout-return-notice.d.ts +34 -0
- package/src/lib/utils/use-checkout-return-notice.js +78 -0
- package/src/lib/utils/use-checkout-return-notice.js.map +1 -0
- package/src/lib/server/feed.js +0 -69
- package/src/lib/server/feed.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/catalog-organization-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Autocomplete,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Divider,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, deleteDoc, doc, setDoc } from 'firebase/firestore'\nimport {\n type Dispatch,\n type SetStateAction,\n memo,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestoreCollection,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport TransferHeaderActions from '../../transfer/transfer-header-actions.component'\nimport { COMMERCE_CATEGORIES_TRANSFER } from '../../transfer/transfer-keys'\n\n/**\n * How many category documents the card reads.\n *\n * A CEILING, not a page size — the walk below is a TREE and cannot be sliced\n * by document without orphaning children from parents on another page.\n */\nconst CATEGORY_CEILING = 250\n/** The same, for collections: the slug check needs every row to be correct. */\nconst COLLECTION_CEILING = 250\n/**\n * The catalog the match counts are computed over.\n *\n * Neither a page size nor a promise: it is how much of the catalog this card\n * is willing to read, and the counts below say \"at least\" once the probe finds\n * a product past it.\n */\nconst PRODUCT_CEILING = 500\n\nexport interface CatalogOrganizationCardProps {\n hostId: string\n}\n\ntype CategoryRow = CommerceModel.ProductCategory & { $id: string }\ntype CollectionRow = CommerceModel.HostCollection & { $id: string }\ntype ProductRow = CommerceModel.HostProduct & { $id: string }\n\nconst RULE_FIELDS: Array<{ value: CommerceModel.CollectionRuleField; label: string }> = [\n { value: 'tag', label: 'Tag' },\n { value: 'categoryId', label: 'Category' },\n { value: 'priceUsd', label: 'Price' },\n { value: 'name', label: 'Name' },\n { value: 'type', label: 'Type' },\n]\nconst RULE_OPS: Array<{ value: CommerceModel.CollectionRuleOp; label: string }> = [\n { value: 'eq', label: 'is' },\n { value: 'neq', label: 'is not' },\n { value: 'lt', label: 'below' },\n { value: 'gt', label: 'above' },\n { value: 'contains', label: 'contains' },\n]\n\nconst productLabel = (product: ProductRow): string => product.name\nconst sameProduct = (option: ProductRow, value: ProductRow): boolean =>\n option.$id === value.$id\n\n/**\n * A manual collection's products, as chips — MEMOIZED, and every prop it\n * takes is stable while the picked products are (AGL-3423).\n *\n * A multiple Autocomplete hands its input a new chip array as\n * `startAdornment` on every render, and MUI's `InputBase` copies that into\n * its `FormControl` from a passive effect: a state update left pending after\n * every commit the field takes part in. React 19 counts each such commit as a\n * nested update, and keystrokes delivered back to back commit one after\n * another with nothing to clear the count, so the fifty-first throws\n * \"Maximum update depth exceeded\" (#185). The collection's name, slug and\n * rules share one draft with this list, so each keystroke in them\n * re-rendered it; kept out of those renders, it commits only when it changes.\n */\nconst CollectionProductsField = memo(function CollectionProductsField(props: {\n products: readonly ProductRow[]\n value: readonly ProductRow[]\n onChange(picked: ProductRow[]): void\n}) {\n const { onChange } = props\n return (\n <Autocomplete\n multiple\n options={props.products as ProductRow[]}\n getOptionLabel={productLabel}\n isOptionEqualToValue={sameProduct}\n value={props.value as ProductRow[]}\n onChange={(_event, picked) => onChange(picked)}\n renderInput={(params) => (\n <TextField {...params} label=\"Products\" size=\"small\" />\n )}\n />\n )\n})\n\ntype CategoryDraft = { id: string | null; name: string; parentId: string }\ntype CollectionDraft = CommerceModel.HostCollection & { id: string | null }\ntype WalkedCategory = CategoryRow & { depth: number }\ntype DraftSetter<T> = Dispatch<SetStateAction<T | null>>\n\n/** A change to one rule, or `null` to remove it. */\ntype RuleChange = (\n index: number,\n patch: Partial<CommerceModel.CollectionRule> | null,\n) => void\n\ntype FieldChange = { target: { value: string } }\n\n/**\n * A text field that redraws only when what it shows changes (AGL-3423).\n *\n * Both editors keep their draft in this card, so a keystroke in either one\n * re-renders the card, and drawn whole that is both lists with a match count\n * per collection, their pagers, and every field of the dialog typed into.\n * When a keystroke costs that much, typed input queues and is processed back\n * to back, which is what lets a chip list's pending update climb to #185 (see\n * `CollectionProductsField`). The lists and each part of the dialogs are\n * memoized on what they show, and each field is this, handed a handler and\n * styles that keep their identity, so the keystroke redraws the field it\n * lands in.\n */\nconst MemoTextField = memo(TextField) as typeof TextField\n\nconst FIRST_FIELD = { mt: 1 } as const\nconst RULE_FIELD = { width: 130 } as const\nconst RULE_OP = { width: 110 } as const\nconst RULE_VALUE = { flex: 1 } as const\n\nconst MODE_MENU = [\n <MenuItem key=\"manual\" value=\"manual\">\n {'Manual — pick products'}\n </MenuItem>,\n <MenuItem key=\"smart\" value=\"smart\">\n {'Smart — rule based'}\n </MenuItem>,\n]\nconst RULE_FIELD_MENU = RULE_FIELDS.map((field) => (\n <MenuItem key={field.value} value={field.value}>\n {field.label}\n </MenuItem>\n))\nconst RULE_OP_MENU = RULE_OPS.map((op) => (\n <MenuItem key={op.value} value={op.value}>\n {op.label}\n </MenuItem>\n))\n\n/** The category tree, a page at a time, and the button that adds one. */\nconst CategoriesSection = memo(function CategoriesSection(props: {\n /** Every category, walked; the page is `visible`. */\n categories: readonly WalkedCategory[]\n visible: readonly WalkedCategory[]\n page: number\n pageSize: number\n truncated: boolean\n onPageChange(page: number): void\n onPageSizeChange(pageSize: number): void\n onEdit: DraftSetter<CategoryDraft>\n onDelete(category: CategoryRow): () => Promise<void>\n}) {\n const { categories, visible, onEdit, onDelete } = props\n return (\n <>\n <Typography variant=\"subtitle2\">{'Categories'}</Typography>\n {categories.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Group products into a browsable tree (e.g. Brakes → Pads).'}\n </Typography>\n ) : (\n visible.map((category) => (\n <Stack\n key={category.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', pl: category.depth * 2 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {category.name}\n <Typography\n component=\"span\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {` /${category.slug}`}\n </Typography>\n </Typography>\n <Button\n size=\"small\"\n onClick={() =>\n onEdit({\n id: category.$id,\n name: category.name,\n parentId: category.parentId ?? '',\n })\n }\n >\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={onDelete(category)}\n >\n {'Delete'}\n </Button>\n </Stack>\n ))\n )}\n {categories.length === 0 ? null : (\n <ListPagination\n page={props.page}\n pageSize={props.pageSize}\n rowCount={visible.length}\n // The categories the card HOLDS — a client slice of rows already\n // read, so the total is known exactly.\n count={categories.length}\n onPageChange={props.onPageChange}\n onPageSizeChange={props.onPageSizeChange}\n />\n )}\n {props.truncated ? (\n <Alert severity=\"info\">\n {`Showing ${CATEGORY_CEILING} categories, ordered by id. This ` +\n 'catalog has more — a category whose parent is past that ' +\n 'boundary is drawn at the top level here rather than under it.'}\n </Alert>\n ) : null}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => onEdit({ id: null, name: '', parentId: '' })}\n >\n {'Add category'}\n </Button>\n </>\n )\n})\n\n/** The catalog collections, a page at a time, and the button that adds one. */\nconst CollectionsSection = memo(function CollectionsSection(props: {\n /** Every catalog collection; the page is `visible`. */\n collections: readonly CollectionRow[]\n visible: readonly CollectionRow[]\n products: readonly ProductRow[]\n /** \"at least \" while the catalog window is short of the catalog. */\n countPrefix: string\n page: number\n pageSize: number\n truncated: boolean\n onPageChange(page: number): void\n onPageSizeChange(pageSize: number): void\n onEdit: DraftSetter<CollectionDraft>\n onDelete(row: CollectionRow): () => Promise<void>\n}) {\n const { collections, visible, products, countPrefix, onEdit, onDelete } = props\n /**\n * How many products a collection holds — over the CATALOG WINDOW.\n *\n * Rendered with \"at least\" when the probe found a product past the ceiling,\n * because past that point this is a lower bound and printing it as a total\n * is the count-that-is-a-window-length defect. The number itself is honest\n * either way; only the claim around it changes.\n */\n const collectionCount = (row: CollectionRow) =>\n products.filter((product) =>\n CommerceModel.matchesCollection(product, row, product.$id),\n ).length\n return (\n <>\n <Typography variant=\"subtitle2\">{'Collections'}</Typography>\n {visible.map((row) => (\n <Stack\n key={row.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {row.name}\n <Typography\n component=\"span\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {` · ${row.mode ?? 'manual'} · ${countPrefix}${collectionCount(\n row,\n )} products`}\n </Typography>\n </Typography>\n <Button\n size=\"small\"\n onClick={() =>\n onEdit({\n id: row.$id,\n name: row.name,\n slug: row.slug ?? '',\n mode: row.mode ?? 'manual',\n productIds: row.productIds ?? [],\n rules: row.rules ?? [],\n matchAll: row.matchAll !== false,\n })\n }\n >\n {'Edit'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={onDelete(row)}>\n {'Delete'}\n </Button>\n </Stack>\n ))}\n {collections.length === 0 ? null : (\n <ListPagination\n page={props.page}\n pageSize={props.pageSize}\n rowCount={visible.length}\n // The CATALOG collections, which is what this list is. Not the\n // window's length: content collections share the subcollection\n // and are classified out before this count is taken.\n count={collections.length}\n onPageChange={props.onPageChange}\n onPageSizeChange={props.onPageSizeChange}\n />\n )}\n {props.truncated ? (\n <Alert severity=\"info\">\n {`Showing ${COLLECTION_CEILING} collections, ordered by id. This ` +\n 'site has more — the slug check below only covers the ones ' +\n 'listed here, so an address may already be taken by one that ' +\n 'is not.'}\n </Alert>\n ) : null}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n onEdit({\n id: null,\n name: '',\n slug: '',\n mode: 'manual',\n productIds: [],\n rules: [],\n matchAll: true,\n })\n }\n >\n {'Add collection'}\n </Button>\n </>\n )\n})\n\n/** The category editor: a name and a parent. */\nconst CategoryDialog = memo(function CategoryDialog(props: {\n draft: CategoryDraft | null\n /** Every category, as the parent picker offers them. */\n categories: readonly WalkedCategory[]\n setDraft: DraftSetter<CategoryDraft>\n onSave(): void\n}) {\n const { draft, categories, setDraft } = props\n const draftId = draft?.id\n const close = useCallback(() => setDraft(null), [setDraft])\n const setName = useCallback(\n (event: FieldChange) =>\n setDraft((prev) => (prev ? { ...prev, name: event.target.value } : prev)),\n [setDraft],\n )\n const setParent = useCallback(\n (event: FieldChange) =>\n setDraft((prev) =>\n prev ? { ...prev, parentId: event.target.value } : prev,\n ),\n [setDraft],\n )\n const parentMenu = useMemo(\n () => [\n <MenuItem key=\"\" value=\"\">\n {'None (top level)'}\n </MenuItem>,\n // The whole set, not `visibleCategories` — a parent picker that\n // offered only the current page could not reparent a category under\n // one two pages away.\n ...categories\n .filter((category) => category.$id !== draftId)\n .map((category) => (\n <MenuItem key={category.$id} value={category.$id}>\n {`${'— '.repeat(category.depth)}${category.name}`}\n </MenuItem>\n )),\n ],\n [categories, draftId],\n )\n return (\n <Dialog open={Boolean(draft)} onClose={close} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{draft?.id ? 'Edit category' : 'New category'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <MemoTextField\n label=\"Name\"\n value={draft?.name ?? ''}\n onChange={setName}\n size=\"small\"\n autoFocus\n sx={FIRST_FIELD}\n />\n <MemoTextField\n label=\"Parent category\"\n value={draft?.parentId ?? ''}\n onChange={setParent}\n size=\"small\"\n select\n >\n {parentMenu}\n </MemoTextField>\n </DialogContent>\n <DialogActions>\n <Button onClick={close}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!draft?.name.trim()}\n onClick={props.onSave}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n})\n\nconst CollectionRuleRow = memo(function CollectionRuleRow(props: {\n index: number\n rule: CommerceModel.CollectionRule\n /** The category picker's entries, for a rule on a category. */\n categoryMenu: readonly JSX.Element[]\n onChange: RuleChange\n}) {\n const { index, rule, onChange } = props\n const setField = useCallback(\n (event: FieldChange) =>\n onChange(index, {\n field: event.target.value as CommerceModel.CollectionRuleField,\n }),\n [index, onChange],\n )\n const setOp = useCallback(\n (event: FieldChange) =>\n onChange(index, {\n op: event.target.value as CommerceModel.CollectionRuleOp,\n }),\n [index, onChange],\n )\n const setCategory = useCallback(\n (event: FieldChange) => onChange(index, { value: event.target.value }),\n [index, onChange],\n )\n const ruleField = rule.field\n const setValue = useCallback(\n (event: FieldChange) =>\n onChange(index, {\n value:\n ruleField === 'priceUsd'\n ? Number(event.target.value)\n : event.target.value,\n }),\n [index, onChange, ruleField],\n )\n return (\n <Stack direction=\"row\" spacing={1}>\n <MemoTextField\n value={rule.field}\n onChange={setField}\n size=\"small\"\n select\n sx={RULE_FIELD}\n >\n {RULE_FIELD_MENU}\n </MemoTextField>\n <MemoTextField\n value={rule.op}\n onChange={setOp}\n size=\"small\"\n select\n sx={RULE_OP}\n >\n {RULE_OP_MENU}\n </MemoTextField>\n {rule.field === 'categoryId' ? (\n <MemoTextField\n value={rule.value}\n onChange={setCategory}\n size=\"small\"\n select\n sx={RULE_VALUE}\n >\n {props.categoryMenu}\n </MemoTextField>\n ) : (\n <MemoTextField\n value={rule.value}\n onChange={setValue}\n size=\"small\"\n sx={RULE_VALUE}\n placeholder={rule.field === 'type' ? 'physical' : 'Value'}\n />\n )}\n <Button size=\"small\" color=\"error\" onClick={() => onChange(index, null)}>\n {'✕'}\n </Button>\n </Stack>\n )\n})\n\n/** Whether every rule must hold, or any one. */\nconst CollectionMatchSwitch = memo(function CollectionMatchSwitch(props: {\n matchAll: boolean\n setDraft: DraftSetter<CollectionDraft>\n}) {\n const { matchAll, setDraft } = props\n return (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{'Match'}</Typography>\n <Switch\n size=\"small\"\n checked={matchAll}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, matchAll: event.target.checked } : prev,\n )\n }\n />\n <Typography variant=\"body2\">\n {matchAll ? 'all rules' : 'any rule'}\n </Typography>\n </Stack>\n )\n})\n\n/** A smart collection's rules, whether all or any must hold, and the button that adds one. */\nconst CollectionRulesFields = memo(function CollectionRulesFields(props: {\n rules: readonly CommerceModel.CollectionRule[]\n matchAll: boolean\n categories: readonly WalkedCategory[]\n setDraft: DraftSetter<CollectionDraft>\n onRule: RuleChange\n}) {\n const { rules, matchAll, categories, setDraft, onRule } = props\n const categoryMenu = useMemo(\n () =>\n categories.map((category) => (\n <MenuItem key={category.$id} value={category.$id}>\n {category.name}\n </MenuItem>\n )),\n [categories],\n )\n return (\n <>\n <CollectionMatchSwitch matchAll={matchAll} setDraft={setDraft} />\n {rules.map((rule, index) => (\n <CollectionRuleRow\n key={index}\n index={index}\n rule={rule}\n categoryMenu={categoryMenu}\n onChange={onRule}\n />\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => onRule(rules.length, {})}\n >\n {'Add rule'}\n </Button>\n </>\n )\n})\n\n/** The products a draft collection holds, the first few by name. */\nconst CollectionPreview = memo(function CollectionPreview(props: {\n matches: readonly ProductRow[]\n countPrefix: string\n}) {\n const { matches, countPrefix } = props\n return (\n <Stack direction=\"row\" spacing={0.5} sx={{ flexWrap: 'wrap' }}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Matches ${countPrefix}${matches.length} products: `}\n </Typography>\n {matches.slice(0, 6).map((product) => (\n <Chip key={product.$id} label={product.name} size=\"small\" />\n ))}\n {matches.length > 6 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`+${matches.length - 6} more`}\n </Typography>\n ) : null}\n </Stack>\n )\n})\n\n/** The collection editor: a name, a mode, and the products or rules it holds. */\nconst CollectionDialog = memo(function CollectionDialog(props: {\n draft: CollectionDraft | null\n /** The address the draft will be saved at. */\n slug: string\n slugTaken: boolean\n error: string | null\n products: readonly ProductRow[]\n /** The draft's manual products, as rows. */\n picked: readonly ProductRow[]\n categories: readonly WalkedCategory[]\n matches: readonly ProductRow[]\n countPrefix: string\n setDraft: DraftSetter<CollectionDraft>\n onProducts(picked: ProductRow[]): void\n onRule: RuleChange\n onSave(): void\n}) {\n const { draft, error, setDraft } = props\n const close = useCallback(() => setDraft(null), [setDraft])\n const setName = useCallback(\n (event: FieldChange) =>\n setDraft((prev) => (prev ? { ...prev, name: event.target.value } : prev)),\n [setDraft],\n )\n const setMode = useCallback(\n (event: FieldChange) =>\n setDraft((prev) =>\n prev\n ? { ...prev, mode: event.target.value as 'manual' | 'smart' }\n : prev,\n ),\n [setDraft],\n )\n return (\n <Dialog open={Boolean(draft)} onClose={close} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{draft?.id ? 'Edit collection' : 'New collection'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <MemoTextField\n label=\"Name\"\n value={draft?.name ?? ''}\n onChange={setName}\n size=\"small\"\n autoFocus\n sx={FIRST_FIELD}\n error={props.slugTaken}\n helperText={draft?.name ? `/collections/${props.slug}` : undefined}\n />\n <MemoTextField\n label=\"Mode\"\n value={draft?.mode ?? 'manual'}\n onChange={setMode}\n size=\"small\"\n select\n >\n {MODE_MENU}\n </MemoTextField>\n {draft?.mode === 'manual' ? (\n <CollectionProductsField\n products={props.products}\n value={props.picked}\n onChange={props.onProducts}\n />\n ) : (\n <CollectionRulesFields\n rules={draft?.rules ?? NO_RULES}\n matchAll={draft?.matchAll !== false}\n categories={props.categories}\n setDraft={setDraft}\n onRule={props.onRule}\n />\n )}\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {draft ? (\n <CollectionPreview matches={props.matches} countPrefix={props.countPrefix} />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={close}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!draft?.name.trim() || Boolean(error)}\n onClick={props.onSave}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n})\n\n/** A collection with no rules yet, the same list on every render. */\nconst NO_RULES: readonly CommerceModel.CollectionRule[] = []\n\n/**\n * Categories & collections manager (AGL-280): category tree (parentId)\n * at `hosts/{hostId}/productCategories`, manual + smart collections at\n * `hosts/{hostId}/collections` with a live matched-product preview from\n * the same `matchesCollection` matcher the storefront uses.\n */\nexport function CatalogOrganizationCard(props: CatalogOrganizationCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const { data: user } = useUser()\n\n const {\n data: categoryDocs,\n status: categoriesStatus,\n /**\n * The category rows are unconfirmed by the server (AGL-1358). Two writes\n * are seeded from them: the editor carries `parentId` off the row whether\n * or not the author touched it, and the delete REPARENTS every child of\n * the deleted category — a set computed entirely from this cached read,\n * so a stale snapshot can miss children that should have been moved.\n * (The reparent write itself is narrow since AGL-1374; what remains\n * cache-dependent is WHICH rows it runs on.)\n */\n fromCache: categoriesFromCache,\n } = useFirestoreCollection<any>(\n /*\n * ORDERED AND CEILINGED, deliberately not paged by the query (AGL-2501).\n *\n * `limit(250)` alone is answered in DOCUMENT-ID order and the ids are\n * `createResourceUid()`, so the window was a pseudo-random sample. Naming\n * the order does not change WHICH 250 come back; what it changes is that\n * the obvious next edit is caught — `orderBy('name')` would HIDE every\n * category saved without one, and `orderBy('order')` is worse still, since\n * the walk below already treats a missing `order` as 0.\n *\n * The QUERY is not paged because this is a TREE. The walk puts parents\n * before children and collects orphans by scanning the whole set, so a\n * page boundary would separate a child from the parent that positions it\n * and re-label as an orphan every category whose parent is on another\n * page. The parent PICKER in the dialog reads the same rows for the same\n * reason.\n */\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'productCategories'),\n CATEGORY_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readCategories, truncated: categoriesTruncated } =\n ceilingedWindow<any>(categoryDocs, CATEGORY_CEILING)\n const {\n data: collectionDocs,\n status: collectionsStatus,\n /**\n * The collection rows are unconfirmed by the server (AGL-1358). The\n * editor copies a whole stored row and the save posts all of it — the\n * transport is the AGL-978 route rather than a client `setDoc`, but the\n * shape is identical, and so is the damage: `productIds` is the manual\n * membership, so a cached seed drops every product added since that\n * snapshot and the storefront blocks pointing at the collection go empty.\n */\n fromCache: collectionsFromCache,\n } = useFirestoreCollection<any>(\n /*\n * ORDERED AND CEILINGED, deliberately not paged (AGL-2501).\n *\n * The slug check below tests a draft against these rows — two catalog\n * collections answering `/collections/{slug}` is a storefront route that\n * resolves to whichever the server reaches first — so a ten-row server\n * page would compare a new collection against a tenth of the catalog and\n * create the collision the check exists to prevent.\n *\n * The rows are also filtered after reading: content collections (AGL-81)\n * share this subcollection and are classified out below, so a page of ten\n * documents arrives holding anywhere from zero to ten catalog collections.\n */\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'collections'),\n COLLECTION_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readCollections, truncated: collectionsTruncated } =\n ceilingedWindow<any>(collectionDocs, COLLECTION_CEILING)\n const { data: productDocs } = useFirestoreCollection<any>(\n /*\n * The catalog behind the MATCH COUNTS, ceilinged with a probe (AGL-2501).\n *\n * `collectionCount` and the smart-collection preview both count this array\n * and print the result as a number of products. It was a bare `limit(500)`,\n * so on a three-thousand-product catalog \"Matches 47 products\" meant \"47 of\n * the five hundred I read\" — a count that is a window length, on the\n * control that decides what a collection contains.\n *\n * It stays a window, because the honest alternative is reading the whole\n * catalog on every mount of this card. What changes is that the probe makes\n * the shortfall a FACT, and the counts say \"at least\" when it bit rather\n * than stating a total they cannot know.\n */\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readProducts, truncated: productsTruncated } =\n ceilingedWindow<any>(productDocs, PRODUCT_CEILING)\n const products: ProductRow[] = useMemo(\n () =>\n [...readProducts]\n .filter((product: any) => !product.deletedAt)\n .map((product: any) => ({\n ...CommerceModel.liftLegacyProduct(product),\n $id: product.$id,\n })),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [productDocs],\n )\n // Content collections (AGL-81) live in the same `hosts/{hostId}/collections`\n // subcollection. This used to keep them out by requiring a non-empty\n // `name`, which held only because the Content page happens to write\n // `displayName` — a content doc that ever acquired a `name` (an import, a\n // hand edit) would land in this list, and its Delete reached the\n // recursiveDelete route (AGL-947). The shared classifier is the real\n // check (AGL-954); the route re-checks it, and AGL-1324 now refuses any\n // collection with entries besides.\n const commerceCollections: CollectionRow[] = useMemo(\n () => readCollections.filter(Aglyn.isHostCollectionKind('catalog')),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [collectionDocs],\n )\n\n // Categories ordered as a walked tree: parents before children.\n const categories: Array<CategoryRow & { depth: number }> = useMemo(() => {\n const rows = [...readCategories] as CategoryRow[]\n rows.sort(\n (a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name),\n )\n const byParent = new Map<string | null, CategoryRow[]>()\n for (const row of rows) {\n const key = row.parentId ?? null\n byParent.set(key, [...(byParent.get(key) ?? []), row])\n }\n const walked: Array<CategoryRow & { depth: number }> = []\n const walk = (parentId: string | null, depth: number) => {\n for (const row of byParent.get(parentId) ?? []) {\n walked.push({ ...row, depth })\n if (depth < 4) walk(row.$id, depth + 1)\n }\n }\n walk(null, 0)\n // Orphans (parent deleted) still show, at the root.\n for (const row of rows) {\n if (!walked.some((item) => item.$id === row.$id)) {\n walked.push({ ...row, depth: 0 })\n }\n }\n return walked\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [categoryDocs])\n\n /*\n * Both pages are SLICES: the rows are already in hand, and the tree walk,\n * the parent picker and the slug check all need every one of them.\n */\n const [categoryPage, setCategoryPage] = useState(0)\n const [categoryPageSize, setCategoryPageSize] = useState(\n TABLE_PAGE_SIZE_DEFAULT,\n )\n const visibleCategories = useMemo(\n () =>\n categories.slice(\n categoryPage * categoryPageSize,\n categoryPage * categoryPageSize + categoryPageSize,\n ),\n [categories, categoryPage, categoryPageSize],\n )\n const [collectionPage, setCollectionPage] = useState(0)\n const [collectionPageSize, setCollectionPageSize] = useState(\n TABLE_PAGE_SIZE_DEFAULT,\n )\n const visibleCollections = useMemo(\n () =>\n commerceCollections.slice(\n collectionPage * collectionPageSize,\n collectionPage * collectionPageSize + collectionPageSize,\n ),\n [commerceCollections, collectionPage, collectionPageSize],\n )\n\n const [categoryDraft, setCategoryDraft] = useState<CategoryDraft | null>(null)\n const [collectionDraft, setCollectionDraft] = useState<CollectionDraft | null>(\n null,\n )\n\n const handleCategorySave = useCallback(async () => {\n if (!categoryDraft?.name.trim()) return\n const id = categoryDraft.id ?? Aglyn.createResourceUid()\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n *\n * The payload is narrower than most sites in this issue, but `parentId`\n * is carried off the seeded row on every save whether or not the author\n * opened the parent picker — so renaming a category against a cached read\n * can move it, and the whole subtree under it, back to a parent someone\n * had already reorganised away from.\n *\n * Only the edit path. A NEW category is built from blanks at a fresh uid\n * and can overwrite nothing, and the first snapshot of any listener is\n * `fromCache: true`, so guarding a create would refuse a save that was\n * never unsafe.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'category',\n unreadable: Boolean(categoryDraft.id) && categoriesStatus === 'error',\n fromCache: Boolean(categoryDraft.id) && categoriesFromCache,\n },\n async () => {\n /**\n * `merge: true`, because this payload is NARROWER than the document\n * (AGL-1372). A replacing write deletes every stored key the form\n * does not send, and this form sends four:\n *\n * - `order` — the tree position. Both sorts read it (the walk above,\n * and `queryPublicCatalog`'s facet chips), and nothing in this card\n * can set it, so it is not the form's to send: a rename dropped it\n * and the category fell back to `?? 0`, reordering the storefront's\n * filter chips.\n * - `createdAt` — written at creation by the seeder, never by this\n * card, so a rename erased it too.\n *\n * Carrying `order` in the payload would have fixed the field we\n * happened to notice and left `createdAt`, plus whatever the document\n * grows next. Merging fixes the shape. Nothing here needs delete\n * semantics — `parentId` clears to an explicit `null`, which merges.\n */\n await setDoc(\n doc(firestore, 'hosts', hostId, 'productCategories', id),\n {\n name: categoryDraft.name.trim().slice(0, 80),\n slug: CommerceModel.commerceSlug(categoryDraft.name),\n parentId: categoryDraft.parentId || null,\n updatedAt: Timestamp.now(),\n },\n { merge: true },\n )\n },\n )\n // Before `setCategoryDraft(null)`, so a refusal keeps the dialog open\n // with what was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setCategoryDraft(null)\n enqueueSnackbar('Category saved', { variant: 'success', persist: false })\n }, [\n categoryDraft,\n firestore,\n hostId,\n enqueueSnackbar,\n categoriesStatus,\n categoriesFromCache,\n ])\n\n const handleCategoryDelete = useCallback(\n (category: CategoryRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this category?',\n description:\n `Products keep their other categories; children of ` +\n `\"${category.name}\" move to the top level.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n /**\n * Refuse the whole sequence when the rows it reads were never confirmed\n * by the server (AGL-1358).\n *\n * The reparent is this issue's shape at its least visible: the author\n * opened no editor, so there is nothing on screen to look stale, and\n * `children` is read straight out of the cache. The write itself is\n * now narrow (see below), so a stale row can no longer be copied back\n * over a child — but WHICH rows are children is still a cached answer.\n *\n * The guard encloses the DELETE as well, for the reason the site\n * details rename did: `children` is computed from the same seed, so a\n * cached read can miss children entirely, and deleting the parent\n * without reparenting them leaves them pointing at a category that no\n * longer exists. Half of this sequence is worse than none of it.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'categories',\n unreadable: categoriesStatus === 'error',\n fromCache: categoriesFromCache,\n },\n async () => {\n // Reparent children to root, then remove.\n const children = (categoryDocs ?? []).filter(\n (row: any) => row.parentId === category.$id,\n )\n /**\n * Write only the field the reparent changes (AGL-1374).\n *\n * `{...child}` spread the listener row, and that row carries a\n * SYNTHETIC `$id` — `idField: '$id'` stamps the document id onto\n * the in-memory object, where nothing persists it. Spreading it\n * into the payload persisted it: every delete-with-children added\n * a real `$id` key to each child, permanently, and a child\n * reparented twice could end up carrying one that no longer\n * matched its own document id. Nothing reads it, so nothing broke\n * — it is the listener's bookkeeping leaking into storage, and\n * once there no reader can tell it from a real field.\n *\n * `{ parentId: null }` with `{ merge: true }` is what the reparent\n * actually means, and it is the stronger fix than stripping `$id`\n * off a full spread: the old write had no options argument, so it\n * was also a whole-document REPLACE of a cached row — the AGL-1358\n * hazard in miniature (and AGL-1372's, one collection over). A\n * narrow merge cannot carry a synthetic key it never names, and\n * cannot rewrite a field the delete has no business touching.\n *\n * `null` is a value, not a deletion, so it merges: the child ends\n * up at the top level, which is what the confirmation promised.\n */\n for (const child of children) {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'productCategories', child.$id),\n { parentId: null },\n { merge: true },\n )\n }\n await deleteDoc(\n doc(firestore, 'hosts', hostId, 'productCategories', category.$id),\n )\n },\n )\n // This path had no report of its own. A delete that silently does\n // nothing after the user confirmed it reads as a delete that worked.\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n },\n [\n confirm,\n categoryDocs,\n firestore,\n hostId,\n enqueueSnackbar,\n categoriesStatus,\n categoriesFromCache,\n ],\n )\n\n const draftSlug = collectionDraft\n ? collectionDraft.slug || CommerceModel.commerceSlug(collectionDraft.name)\n : ''\n // The slug is the collection's public address at /collections/{slug} and\n // nothing enforced uniqueness (AGL-957) — a second one at the same slug\n // made the first unreachable with no error anywhere. Checked against the\n // catalog collections only; content collections have their own namespace.\n const slugTaken =\n collectionDraft !== null &&\n Aglyn.isCollectionSlugTaken(\n draftSlug,\n 'catalog',\n commerceCollections,\n collectionDraft.id,\n )\n const collectionError = collectionDraft\n ? collectionDraft.name\n ? (CommerceModel.validateCollection({\n ...collectionDraft,\n slug: draftSlug,\n }) ??\n (slugTaken\n ? `Another collection already serves /collections/${draftSlug}`\n : null))\n : null\n : null\n\n const collectionProductIds = collectionDraft?.productIds\n const collectionProducts = useMemo(\n () =>\n products.filter((product) =>\n (collectionProductIds ?? []).includes(product.$id),\n ),\n [products, collectionProductIds],\n )\n const setCollectionProducts = useCallback(\n (picked: ProductRow[]) =>\n setCollectionDraft((prev) =>\n prev ? { ...prev, productIds: picked.map((item) => item.$id) } : prev,\n ),\n [],\n )\n\n /*\n * Recomputed when what decides membership changes — the mode, the picked\n * products, the rules and whether all must hold — and not on a keystroke in\n * the name, which no match reads (AGL-3423).\n */\n const draftMode = collectionDraft?.mode\n const draftRules = collectionDraft?.rules\n const draftMatchAll = collectionDraft?.matchAll\n const previewMatches = useMemo(() => {\n if (!draftMode) return []\n const candidate = {\n mode: draftMode,\n productIds: collectionProductIds,\n rules: draftRules,\n matchAll: draftMatchAll,\n } as CommerceModel.HostCollection\n return products.filter((product) =>\n CommerceModel.matchesCollection(product, candidate, product.$id),\n )\n }, [draftMode, collectionProductIds, draftRules, draftMatchAll, products])\n\n /**\n * Which products a smart collection holds is stored on the products\n * (`collectionIds`, AGL-3321), so a save or a delete re-stamps them —\n * bounded passes through the commerce route until it says it is done.\n * A failure is reported, not thrown: the collection itself saved.\n */\n const restampMembership = useCallback(\n async (collectionId: string) => {\n try {\n let after: string | undefined\n for (let pass = 0; pass < 100; pass += 1) {\n const response = await authorizedFetch(\n user,\n '/api/commerce/collection-membership',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, collectionId, ...(after ? { after } : {}) }),\n },\n )\n const result = await response.json().catch(() => ({}))\n if (!response.ok) throw new Error(result?.error ?? 'Membership update failed')\n if (result.done || !result.after) return\n after = result.after\n }\n } catch (error: any) {\n enqueueSnackbar(\n `${error?.message ?? 'Membership update failed'} — the storefront may list this collection's old products until it is saved again.`,\n { variant: 'warning' },\n )\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n const handleCollectionSave = useCallback(async () => {\n if (!collectionDraft?.name.trim() || collectionError) return\n const { id: draftId, ...data } = collectionDraft\n // Both create and rename go through the route (AGL-978): this save writes\n // the whole document including `slug`, which is the collection's public\n // address and is claimed transactionally there. Rules deny a client\n // create, and freeze `slug`/`kind` on update.\n /**\n * Refuse an UPDATE whose seed the server never confirmed (AGL-1358).\n *\n * The transport is the route rather than a client `setDoc`, but the shape\n * is the one this issue is about: `data` is the whole stored row copied\n * out of the listener, and the route writes what it is sent. `productIds`\n * is the manual membership, so a cached seed drops every product added\n * since that snapshot and the storefront blocks pointing at the\n * collection go empty; `rules` and `matchAll` do the same for a smart\n * collection. That the write leaves the browser over HTTP changes who\n * performs it, not what it destroys.\n *\n * Only the update path. A CREATE is built from blanks, and the route\n * claims a fresh slug transactionally, so it can overwrite nothing.\n *\n * The guard WRAPS the request — an early return is a shape you can keep\n * while losing the protection.\n */\n let verdict: Awaited<ReturnType<typeof writeGuardedBySeed>>\n let savedId = ''\n try {\n verdict = await writeGuardedBySeed(\n {\n subject: 'collection',\n unreadable: Boolean(draftId) && collectionsStatus === 'error',\n fromCache: Boolean(draftId) && collectionsFromCache,\n },\n async () => {\n const response = await authorizedFetch(\n user,\n '/api/hosts/collections',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n action: draftId ? 'update' : 'create',\n kind: 'catalog',\n ...(draftId ? { id: draftId } : {}),\n data: {\n ...data,\n name: collectionDraft.name.trim().slice(0, 80),\n slug: draftSlug,\n },\n }),\n },\n )\n const result = await response.json().catch(() => ({}))\n if (!response.ok) {\n throw new Error(result?.error ?? 'Collection save failed')\n }\n savedId = String(draftId ?? result?.id ?? '')\n },\n )\n } catch (error: any) {\n return void enqueueSnackbar(error?.message ?? 'Collection save failed', {\n variant: 'error',\n })\n }\n // Before `setCollectionDraft(null)`, so a refusal keeps the dialog open\n // with the membership that was picked.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setCollectionDraft(null)\n enqueueSnackbar('Collection saved', { variant: 'success', persist: false })\n if (savedId) await restampMembership(savedId)\n }, [\n restampMembership,\n collectionDraft,\n collectionError,\n draftSlug,\n user,\n hostId,\n enqueueSnackbar,\n collectionsStatus,\n collectionsFromCache,\n ])\n\n const handleCollectionDelete = useCallback(\n (row: CollectionRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this collection?',\n // NOT a cascade any more (AGL-1324/AGL-1336): the route REFUSES a\n // collection anything still depends on. Promising to take the\n // entries with it was the opposite of what happens, and the two\n // blockers are the whole point of the feature — say them before the\n // button is armed, not in a 409 afterwards.\n description:\n `Storefront blocks pointing at \"${row.name}\" go empty. The delete ` +\n 'is refused while the collection still has entries, or while a ' +\n 'page uses it as a list or entry template — empty it and detach ' +\n 'those pages first. It also takes the site admin role.',\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n // A collection owns an `entries` subcollection (the Content page's\n // published entries) and Firestore doesn't cascade, so deleting the\n // doc from here stranded them — still editor-writable through the\n // host catch-all rule, just unreachable. recursiveDelete is\n // Admin-SDK-only, hence the route (AGL-947). Since AGL-1324 the route\n // also refuses rather than cascading; the 409's message names the\n // blockers and is surfaced by the catch below.\n try {\n const response = await authorizedFetch(user, '/api/resources/erase', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n scope: 'hosts',\n scopeId: hostId,\n kind: 'collections',\n id: row.$id,\n // The route re-checks this server-side (AGL-954): a stale client\n // must not be able to recursiveDelete a content collection's\n // entries through the catalog card.\n collectionKind: 'catalog',\n }),\n })\n const result = await response.json().catch(() => ({}))\n if (!response.ok) {\n throw new Error(result?.error ?? 'Collection delete failed')\n }\n } catch (error: any) {\n return void enqueueSnackbar(error?.message ?? 'Collection delete failed', {\n variant: 'error',\n })\n }\n enqueueSnackbar('Collection deleted', { variant: 'success', persist: false })\n await restampMembership(row.$id)\n },\n [confirm, user, hostId, enqueueSnackbar, restampMembership],\n )\n\n /*\n * \"at least\", or nothing at all.\n *\n * Every count on this card is computed over `products`, which is the catalog\n * WINDOW rather than the catalog. While the probe finds a product past the\n * ceiling those counts are lower bounds, and printing one as a total is the\n * count-that-is-a-window-length defect this ceiling exists to make visible.\n *\n * Keyed on the probe rather than on `products.length >= PRODUCT_CEILING`,\n * because a catalog of exactly the ceiling is complete: nothing is missing,\n * and a comparison would put \"at least\" on a number that is exact.\n */\n const countPrefix = productsTruncated ? 'at least ' : ''\n\n /*\n * A rule's change, written over the collection draft as last rendered, from\n * a handler that keeps its identity so a rule row redraws only when its own\n * rule changes (AGL-3423).\n */\n const latestCollectionDraft = useRef(collectionDraft)\n latestCollectionDraft.current = collectionDraft\n const updateRule = useCallback<RuleChange>((index, patch) => {\n const draft = latestCollectionDraft.current\n if (!draft) return\n const rules = [...(draft.rules ?? [])]\n if (patch === null) rules.splice(index, 1)\n else\n rules[index] = {\n field: 'tag',\n op: 'eq',\n value: '',\n ...rules[index],\n ...patch,\n }\n setCollectionDraft({ ...draft, rules })\n }, [])\n\n return (\n <CardDisplay\n header={'Categories & collections'}\n HeaderProps={{\n action: (\n <TransferHeaderActions\n resource={COMMERCE_CATEGORIES_TRANSFER}\n hostId={hostId}\n noun=\"categories\"\n />\n ),\n }}\n help={pluginDocsHelp('catalog', { anchor: '#categories-and-tags' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <CategoriesSection\n categories={categories}\n visible={visibleCategories}\n page={categoryPage}\n pageSize={categoryPageSize}\n truncated={categoriesTruncated}\n onPageChange={setCategoryPage}\n onPageSizeChange={setCategoryPageSize}\n onEdit={setCategoryDraft}\n onDelete={handleCategoryDelete}\n />\n\n <Divider sx={{ my: 1 }} />\n <CollectionsSection\n collections={commerceCollections}\n visible={visibleCollections}\n products={products}\n countPrefix={countPrefix}\n page={collectionPage}\n pageSize={collectionPageSize}\n truncated={collectionsTruncated}\n onPageChange={setCollectionPage}\n onPageSizeChange={setCollectionPageSize}\n onEdit={setCollectionDraft}\n onDelete={handleCollectionDelete}\n />\n </Stack>\n\n <CategoryDialog\n draft={categoryDraft}\n categories={categories}\n setDraft={setCategoryDraft}\n onSave={handleCategorySave}\n />\n\n <CollectionDialog\n draft={collectionDraft}\n slug={draftSlug}\n slugTaken={slugTaken}\n error={collectionError}\n products={products}\n picked={collectionProducts}\n categories={categories}\n matches={previewMatches}\n countPrefix={countPrefix}\n setDraft={setCollectionDraft}\n onProducts={setCollectionProducts}\n onRule={updateRule}\n onSave={handleCollectionSave}\n />\n </CardDisplay>\n )\n}\nCatalogOrganizationCard.displayName = 'CatalogOrganizationCard'\n\nexport default CatalogOrganizationCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","useSnackbar","Timestamp","Alert","Autocomplete","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Divider","MenuItem","Stack","Switch","TextField","Typography","collection","deleteDoc","doc","setDoc","memo","useCallback","useMemo","useRef","useState","useFirestore","useUser","ceilingedWindow","collectionCeiling","useFirestoreCollection","writeGuardedBySeed","ListPagination","TABLE_PAGE_SIZE_DEFAULT","pluginDocsHelp","authorizedFetch","TransferHeaderActions","COMMERCE_CATEGORIES_TRANSFER","CATEGORY_CEILING","COLLECTION_CEILING","PRODUCT_CEILING","RULE_FIELDS","value","label","RULE_OPS","productLabel","product","name","sameProduct","option","$id","CollectionProductsField","props","onChange","multiple","options","products","getOptionLabel","isOptionEqualToValue","_event","picked","renderInput","params","size","MemoTextField","FIRST_FIELD","mt","RULE_FIELD","width","RULE_OP","RULE_VALUE","flex","MODE_MENU","RULE_FIELD_MENU","map","field","RULE_OP_MENU","op","CategoriesSection","categories","visible","onEdit","onDelete","variant","length","color","category","direction","spacing","sx","alignItems","pl","depth","noWrap","component","slug","onClick","id","parentId","page","pageSize","rowCount","count","onPageChange","onPageSizeChange","truncated","severity","alignSelf","CollectionsSection","collections","countPrefix","collectionCount","row","filter","matchesCollection","mode","productIds","rules","matchAll","CategoryDialog","draft","setDraft","draftId","close","setName","event","prev","target","setParent","parentMenu","repeat","open","Boolean","onClose","maxWidth","fullWidth","display","flexDirection","gap","autoFocus","select","disabled","trim","onSave","CollectionRuleRow","index","rule","setField","setOp","setCategory","ruleField","setValue","Number","categoryMenu","placeholder","CollectionMatchSwitch","checked","CollectionRulesFields","onRule","CollectionPreview","matches","flexWrap","slice","CollectionDialog","error","setMode","slugTaken","helperText","undefined","onProducts","NO_RULES","CatalogOrganizationCard","hostId","firestore","enqueueSnackbar","confirm","data","user","categoryDocs","status","categoriesStatus","fromCache","categoriesFromCache","idField","rows","readCategories","categoriesTruncated","collectionDocs","collectionsStatus","collectionsFromCache","readCollections","collectionsTruncated","productDocs","readProducts","productsTruncated","deletedAt","liftLegacyProduct","commerceCollections","isHostCollectionKind","sort","a","b","order","localeCompare","byParent","Map","key","set","get","walked","walk","push","some","item","categoryPage","setCategoryPage","categoryPageSize","setCategoryPageSize","visibleCategories","collectionPage","setCollectionPage","collectionPageSize","setCollectionPageSize","visibleCollections","categoryDraft","setCategoryDraft","collectionDraft","setCollectionDraft","handleCategorySave","createResourceUid","verdict","subject","unreadable","commerceSlug","updatedAt","now","merge","ok","message","persist","handleCategoryDelete","confirmed","title","description","confirmationText","confirmationButtonProps","then","catch","children","child","draftSlug","isCollectionSlugTaken","collectionError","validateCollection","collectionProductIds","collectionProducts","includes","setCollectionProducts","draftMode","draftRules","draftMatchAll","previewMatches","candidate","restampMembership","collectionId","after","pass","response","method","headers","body","JSON","stringify","result","json","Error","done","handleCollectionSave","savedId","action","kind","String","handleCollectionDelete","scope","scopeId","collectionKind","latestCollectionDraft","current","updateRule","patch","splice","header","HeaderProps","resource","noun","help","anchor","contentGutterX","contentGutterY","my","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/catalog-organization-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { Timestamp } from '@aglyn/shared-util-timestamp'\nimport {\n Alert,\n Autocomplete,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Divider,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, deleteDoc, doc, setDoc } from 'firebase/firestore'\nimport {\n type Dispatch,\n type SetStateAction,\n memo,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestoreCollection,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport TransferHeaderActions from '../../transfer/transfer-header-actions.component'\nimport { COMMERCE_CATEGORIES_TRANSFER } from '../../transfer/transfer-keys'\n\n/**\n * How many category documents the card reads.\n *\n * A CEILING, not a page size — the walk below is a TREE and cannot be sliced\n * by document without orphaning children from parents on another page.\n */\nconst CATEGORY_CEILING = 250\n/** The same, for collections: the slug check needs every row to be correct. */\nconst COLLECTION_CEILING = 250\n/**\n * The catalog the match counts are computed over.\n *\n * Neither a page size nor a promise: it is how much of the catalog this card\n * is willing to read, and the counts below say \"at least\" once the probe finds\n * a product past it.\n */\nconst PRODUCT_CEILING = 500\n\nexport interface CatalogOrganizationCardProps {\n hostId: string\n}\n\ntype CategoryRow = CommerceModel.ProductCategory & { $id: string }\ntype CollectionRow = CommerceModel.HostCollection & { $id: string }\ntype ProductRow = CommerceModel.HostProduct & { $id: string }\n\nconst RULE_FIELDS: Array<{ value: CommerceModel.CollectionRuleField; label: string }> = [\n { value: 'tag', label: 'Tag' },\n { value: 'categoryId', label: 'Category' },\n { value: 'priceUsd', label: 'Price' },\n { value: 'name', label: 'Name' },\n { value: 'type', label: 'Type' },\n]\nconst RULE_OPS: Array<{ value: CommerceModel.CollectionRuleOp; label: string }> = [\n { value: 'eq', label: 'is' },\n { value: 'neq', label: 'is not' },\n { value: 'lt', label: 'below' },\n { value: 'gt', label: 'above' },\n { value: 'contains', label: 'contains' },\n]\n\nconst productLabel = (product: ProductRow): string => product.name\nconst sameProduct = (option: ProductRow, value: ProductRow): boolean =>\n option.$id === value.$id\n\n/**\n * A manual collection's products, as chips — MEMOIZED, and every prop it\n * takes is stable while the picked products are (AGL-3423).\n *\n * A multiple Autocomplete hands its input a new chip array as\n * `startAdornment` on every render, and MUI's `InputBase` copies that into\n * its `FormControl` from a passive effect: a state update left pending after\n * every commit the field takes part in. React 19 counts each such commit as a\n * nested update, and keystrokes delivered back to back commit one after\n * another with nothing to clear the count, so the fifty-first throws\n * \"Maximum update depth exceeded\" (#185). The collection's name, slug and\n * rules share one draft with this list, so each keystroke in them\n * re-rendered it; kept out of those renders, it commits only when it changes.\n */\nconst CollectionProductsField = memo(function CollectionProductsField(props: {\n products: readonly ProductRow[]\n value: readonly ProductRow[]\n onChange(picked: ProductRow[]): void\n}) {\n const { onChange } = props\n return (\n <Autocomplete\n multiple\n options={props.products as ProductRow[]}\n getOptionLabel={productLabel}\n isOptionEqualToValue={sameProduct}\n value={props.value as ProductRow[]}\n onChange={(_event, picked) => onChange(picked)}\n renderInput={(params) => (\n <TextField {...params} label=\"Products\" size=\"small\" />\n )}\n />\n )\n})\n\ntype CategoryDraft = { id: string | null; name: string; parentId: string }\ntype CollectionDraft = CommerceModel.HostCollection & { id: string | null }\ntype WalkedCategory = CategoryRow & { depth: number }\ntype DraftSetter<T> = Dispatch<SetStateAction<T | null>>\n\n/** A change to one rule, or `null` to remove it. */\ntype RuleChange = (\n index: number,\n patch: Partial<CommerceModel.CollectionRule> | null,\n) => void\n\ntype FieldChange = { target: { value: string } }\n\n/**\n * A text field that redraws only when what it shows changes (AGL-3423).\n *\n * Both editors keep their draft in this card, so a keystroke in either one\n * re-renders the card, and drawn whole that is both lists with a match count\n * per collection, their pagers, and every field of the dialog typed into.\n * When a keystroke costs that much, typed input queues and is processed back\n * to back, which is what lets a chip list's pending update climb to #185 (see\n * `CollectionProductsField`). The lists and each part of the dialogs are\n * memoized on what they show, and each field is this, handed a handler and\n * styles that keep their identity, so the keystroke redraws the field it\n * lands in.\n */\nconst MemoTextField = memo(TextField) as typeof TextField\n\nconst FIRST_FIELD = { mt: 1 } as const\nconst RULE_FIELD = { width: 130 } as const\nconst RULE_OP = { width: 110 } as const\nconst RULE_VALUE = { flex: 1 } as const\n\nconst MODE_MENU = [\n <MenuItem key=\"manual\" value=\"manual\">\n {'Manual — pick products'}\n </MenuItem>,\n <MenuItem key=\"smart\" value=\"smart\">\n {'Smart — rule based'}\n </MenuItem>,\n]\nconst RULE_FIELD_MENU = RULE_FIELDS.map((field) => (\n <MenuItem key={field.value} value={field.value}>\n {field.label}\n </MenuItem>\n))\nconst RULE_OP_MENU = RULE_OPS.map((op) => (\n <MenuItem key={op.value} value={op.value}>\n {op.label}\n </MenuItem>\n))\n\n/** The category tree, a page at a time, and the button that adds one. */\nconst CategoriesSection = memo(function CategoriesSection(props: {\n /** Every category, walked; the page is `visible`. */\n categories: readonly WalkedCategory[]\n visible: readonly WalkedCategory[]\n page: number\n pageSize: number\n truncated: boolean\n onPageChange(page: number): void\n onPageSizeChange(pageSize: number): void\n onEdit: DraftSetter<CategoryDraft>\n onDelete(category: CategoryRow): () => Promise<void>\n}) {\n const { categories, visible, onEdit, onDelete } = props\n return (\n <>\n <Typography variant=\"subtitle2\">{'Categories'}</Typography>\n {categories.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Group products into a browsable tree (e.g. Brakes → Pads).'}\n </Typography>\n ) : (\n visible.map((category) => (\n <Stack\n key={category.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', pl: category.depth * 2 }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {category.name}\n <Typography\n component=\"span\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {` /${category.slug}`}\n </Typography>\n </Typography>\n <Button\n size=\"small\"\n onClick={() =>\n onEdit({\n id: category.$id,\n name: category.name,\n parentId: category.parentId ?? '',\n })\n }\n >\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={onDelete(category)}\n >\n {'Delete'}\n </Button>\n </Stack>\n ))\n )}\n {categories.length === 0 ? null : (\n <ListPagination\n page={props.page}\n pageSize={props.pageSize}\n rowCount={visible.length}\n // The categories the card HOLDS — a client slice of rows already\n // read, so the total is known exactly.\n count={categories.length}\n onPageChange={props.onPageChange}\n onPageSizeChange={props.onPageSizeChange}\n />\n )}\n {props.truncated ? (\n <Alert severity=\"info\">\n {`Showing ${CATEGORY_CEILING} categories, ordered by id. This ` +\n 'catalog has more — a category whose parent is past that ' +\n 'boundary is drawn at the top level here rather than under it.'}\n </Alert>\n ) : null}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => onEdit({ id: null, name: '', parentId: '' })}\n >\n {'Add category'}\n </Button>\n </>\n )\n})\n\n/** The catalog collections, a page at a time, and the button that adds one. */\nconst CollectionsSection = memo(function CollectionsSection(props: {\n /** Every catalog collection; the page is `visible`. */\n collections: readonly CollectionRow[]\n visible: readonly CollectionRow[]\n products: readonly ProductRow[]\n /** \"at least \" while the catalog window is short of the catalog. */\n countPrefix: string\n page: number\n pageSize: number\n truncated: boolean\n onPageChange(page: number): void\n onPageSizeChange(pageSize: number): void\n onEdit: DraftSetter<CollectionDraft>\n onDelete(row: CollectionRow): () => Promise<void>\n}) {\n const { collections, visible, products, countPrefix, onEdit, onDelete } = props\n /**\n * How many products a collection holds — over the CATALOG WINDOW.\n *\n * Rendered with \"at least\" when the probe found a product past the ceiling,\n * because past that point this is a lower bound and printing it as a total\n * is the count-that-is-a-window-length defect. The number itself is honest\n * either way; only the claim around it changes.\n */\n const collectionCount = (row: CollectionRow) =>\n products.filter((product) =>\n CommerceModel.matchesCollection(product, row, product.$id),\n ).length\n return (\n <>\n <Typography variant=\"subtitle2\">{'Collections'}</Typography>\n {visible.map((row) => (\n <Stack\n key={row.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Typography variant=\"body2\" sx={{ flex: 1 }} noWrap>\n {row.name}\n <Typography\n component=\"span\"\n variant=\"caption\"\n color=\"text.secondary\"\n >\n {` · ${row.mode ?? 'manual'} · ${countPrefix}${collectionCount(\n row,\n )} products`}\n </Typography>\n </Typography>\n <Button\n size=\"small\"\n onClick={() =>\n onEdit({\n id: row.$id,\n name: row.name,\n slug: row.slug ?? '',\n mode: row.mode ?? 'manual',\n productIds: row.productIds ?? [],\n rules: row.rules ?? [],\n matchAll: row.matchAll !== false,\n })\n }\n >\n {'Edit'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={onDelete(row)}>\n {'Delete'}\n </Button>\n </Stack>\n ))}\n {collections.length === 0 ? null : (\n <ListPagination\n page={props.page}\n pageSize={props.pageSize}\n rowCount={visible.length}\n // The CATALOG collections, which is what this list is. Not the\n // window's length: content collections share the subcollection\n // and are classified out before this count is taken.\n count={collections.length}\n onPageChange={props.onPageChange}\n onPageSizeChange={props.onPageSizeChange}\n />\n )}\n {props.truncated ? (\n <Alert severity=\"info\">\n {`Showing ${COLLECTION_CEILING} collections, ordered by id. This ` +\n 'site has more — the slug check below only covers the ones ' +\n 'listed here, so an address may already be taken by one that ' +\n 'is not.'}\n </Alert>\n ) : null}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n onEdit({\n id: null,\n name: '',\n slug: '',\n mode: 'manual',\n productIds: [],\n rules: [],\n matchAll: true,\n })\n }\n >\n {'Add collection'}\n </Button>\n </>\n )\n})\n\n/** The category editor: a name and a parent. */\nconst CategoryDialog = memo(function CategoryDialog(props: {\n draft: CategoryDraft | null\n /** Every category, as the parent picker offers them. */\n categories: readonly WalkedCategory[]\n setDraft: DraftSetter<CategoryDraft>\n onSave(): void\n}) {\n const { draft, categories, setDraft } = props\n const draftId = draft?.id\n const close = useCallback(() => setDraft(null), [setDraft])\n const setName = useCallback(\n (event: FieldChange) =>\n setDraft((prev) => (prev ? { ...prev, name: event.target.value } : prev)),\n [setDraft],\n )\n const setParent = useCallback(\n (event: FieldChange) =>\n setDraft((prev) =>\n prev ? { ...prev, parentId: event.target.value } : prev,\n ),\n [setDraft],\n )\n const parentMenu = useMemo(\n () => [\n <MenuItem key=\"\" value=\"\">\n {'None (top level)'}\n </MenuItem>,\n // The whole set, not `visibleCategories` — a parent picker that\n // offered only the current page could not reparent a category under\n // one two pages away.\n ...categories\n .filter((category) => category.$id !== draftId)\n .map((category) => (\n <MenuItem key={category.$id} value={category.$id}>\n {`${'— '.repeat(category.depth)}${category.name}`}\n </MenuItem>\n )),\n ],\n [categories, draftId],\n )\n return (\n <Dialog open={Boolean(draft)} onClose={close} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{draft?.id ? 'Edit category' : 'New category'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <MemoTextField\n label=\"Name\"\n value={draft?.name ?? ''}\n onChange={setName}\n size=\"small\"\n autoFocus\n sx={FIRST_FIELD}\n />\n <MemoTextField\n label=\"Parent category\"\n value={draft?.parentId ?? ''}\n onChange={setParent}\n size=\"small\"\n select\n >\n {parentMenu}\n </MemoTextField>\n </DialogContent>\n <DialogActions>\n <Button onClick={close}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!draft?.name.trim()}\n onClick={props.onSave}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n})\n\nconst CollectionRuleRow = memo(function CollectionRuleRow(props: {\n index: number\n rule: CommerceModel.CollectionRule\n /** The category picker's entries, for a rule on a category. */\n categoryMenu: readonly JSX.Element[]\n onChange: RuleChange\n}) {\n const { index, rule, onChange } = props\n const setField = useCallback(\n (event: FieldChange) =>\n onChange(index, {\n field: event.target.value as CommerceModel.CollectionRuleField,\n }),\n [index, onChange],\n )\n const setOp = useCallback(\n (event: FieldChange) =>\n onChange(index, {\n op: event.target.value as CommerceModel.CollectionRuleOp,\n }),\n [index, onChange],\n )\n const setCategory = useCallback(\n (event: FieldChange) => onChange(index, { value: event.target.value }),\n [index, onChange],\n )\n const ruleField = rule.field\n const setValue = useCallback(\n (event: FieldChange) =>\n onChange(index, {\n value:\n ruleField === 'priceUsd'\n ? Number(event.target.value)\n : event.target.value,\n }),\n [index, onChange, ruleField],\n )\n return (\n <Stack direction=\"row\" spacing={1}>\n <MemoTextField\n value={rule.field}\n onChange={setField}\n size=\"small\"\n select\n sx={RULE_FIELD}\n >\n {RULE_FIELD_MENU}\n </MemoTextField>\n <MemoTextField\n value={rule.op}\n onChange={setOp}\n size=\"small\"\n select\n sx={RULE_OP}\n >\n {RULE_OP_MENU}\n </MemoTextField>\n {rule.field === 'categoryId' ? (\n <MemoTextField\n value={rule.value}\n onChange={setCategory}\n size=\"small\"\n select\n sx={RULE_VALUE}\n >\n {props.categoryMenu}\n </MemoTextField>\n ) : (\n <MemoTextField\n value={rule.value}\n onChange={setValue}\n size=\"small\"\n sx={RULE_VALUE}\n placeholder={rule.field === 'type' ? 'physical' : 'Value'}\n />\n )}\n <Button size=\"small\" color=\"error\" onClick={() => onChange(index, null)}>\n {'✕'}\n </Button>\n </Stack>\n )\n})\n\n/** Whether every rule must hold, or any one. */\nconst CollectionMatchSwitch = memo(function CollectionMatchSwitch(props: {\n matchAll: boolean\n setDraft: DraftSetter<CollectionDraft>\n}) {\n const { matchAll, setDraft } = props\n return (\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Typography variant=\"body2\">{'Match'}</Typography>\n <Switch\n size=\"small\"\n checked={matchAll}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, matchAll: event.target.checked } : prev,\n )\n }\n />\n <Typography variant=\"body2\">\n {matchAll ? 'all rules' : 'any rule'}\n </Typography>\n </Stack>\n )\n})\n\n/** A smart collection's rules, whether all or any must hold, and the button that adds one. */\nconst CollectionRulesFields = memo(function CollectionRulesFields(props: {\n rules: readonly CommerceModel.CollectionRule[]\n matchAll: boolean\n categories: readonly WalkedCategory[]\n setDraft: DraftSetter<CollectionDraft>\n onRule: RuleChange\n}) {\n const { rules, matchAll, categories, setDraft, onRule } = props\n const categoryMenu = useMemo(\n () =>\n categories.map((category) => (\n <MenuItem key={category.$id} value={category.$id}>\n {category.name}\n </MenuItem>\n )),\n [categories],\n )\n return (\n <>\n <CollectionMatchSwitch matchAll={matchAll} setDraft={setDraft} />\n {rules.map((rule, index) => (\n <CollectionRuleRow\n key={index}\n index={index}\n rule={rule}\n categoryMenu={categoryMenu}\n onChange={onRule}\n />\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => onRule(rules.length, {})}\n >\n {'Add rule'}\n </Button>\n </>\n )\n})\n\n/** The products a draft collection holds, the first few by name. */\nconst CollectionPreview = memo(function CollectionPreview(props: {\n matches: readonly ProductRow[]\n countPrefix: string\n}) {\n const { matches, countPrefix } = props\n return (\n <Stack useFlexGap direction=\"row\" spacing={0.5} sx={{ flexWrap: 'wrap' }}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Matches ${countPrefix}${matches.length} products: `}\n </Typography>\n {matches.slice(0, 6).map((product) => (\n <Chip key={product.$id} label={product.name} size=\"small\" />\n ))}\n {matches.length > 6 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`+${matches.length - 6} more`}\n </Typography>\n ) : null}\n </Stack>\n )\n})\n\n/** The collection editor: a name, a mode, and the products or rules it holds. */\nconst CollectionDialog = memo(function CollectionDialog(props: {\n draft: CollectionDraft | null\n /** The address the draft will be saved at. */\n slug: string\n slugTaken: boolean\n error: string | null\n products: readonly ProductRow[]\n /** The draft's manual products, as rows. */\n picked: readonly ProductRow[]\n categories: readonly WalkedCategory[]\n matches: readonly ProductRow[]\n countPrefix: string\n setDraft: DraftSetter<CollectionDraft>\n onProducts(picked: ProductRow[]): void\n onRule: RuleChange\n onSave(): void\n}) {\n const { draft, error, setDraft } = props\n const close = useCallback(() => setDraft(null), [setDraft])\n const setName = useCallback(\n (event: FieldChange) =>\n setDraft((prev) => (prev ? { ...prev, name: event.target.value } : prev)),\n [setDraft],\n )\n const setMode = useCallback(\n (event: FieldChange) =>\n setDraft((prev) =>\n prev\n ? { ...prev, mode: event.target.value as 'manual' | 'smart' }\n : prev,\n ),\n [setDraft],\n )\n return (\n <Dialog open={Boolean(draft)} onClose={close} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{draft?.id ? 'Edit collection' : 'New collection'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <MemoTextField\n label=\"Name\"\n value={draft?.name ?? ''}\n onChange={setName}\n size=\"small\"\n autoFocus\n sx={FIRST_FIELD}\n error={props.slugTaken}\n helperText={draft?.name ? `/collections/${props.slug}` : undefined}\n />\n <MemoTextField\n label=\"Mode\"\n value={draft?.mode ?? 'manual'}\n onChange={setMode}\n size=\"small\"\n select\n >\n {MODE_MENU}\n </MemoTextField>\n {draft?.mode === 'manual' ? (\n <CollectionProductsField\n products={props.products}\n value={props.picked}\n onChange={props.onProducts}\n />\n ) : (\n <CollectionRulesFields\n rules={draft?.rules ?? NO_RULES}\n matchAll={draft?.matchAll !== false}\n categories={props.categories}\n setDraft={setDraft}\n onRule={props.onRule}\n />\n )}\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {draft ? (\n <CollectionPreview matches={props.matches} countPrefix={props.countPrefix} />\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={close}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!draft?.name.trim() || Boolean(error)}\n onClick={props.onSave}\n >\n {'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n )\n})\n\n/** A collection with no rules yet, the same list on every render. */\nconst NO_RULES: readonly CommerceModel.CollectionRule[] = []\n\n/**\n * Categories & collections manager (AGL-280): category tree (parentId)\n * at `hosts/{hostId}/productCategories`, manual + smart collections at\n * `hosts/{hostId}/collections` with a live matched-product preview from\n * the same `matchesCollection` matcher the storefront uses.\n */\nexport function CatalogOrganizationCard(props: CatalogOrganizationCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n const { data: user } = useUser()\n\n const {\n data: categoryDocs,\n status: categoriesStatus,\n /**\n * The category rows are unconfirmed by the server (AGL-1358). Two writes\n * are seeded from them: the editor carries `parentId` off the row whether\n * or not the author touched it, and the delete REPARENTS every child of\n * the deleted category — a set computed entirely from this cached read,\n * so a stale snapshot can miss children that should have been moved.\n * (The reparent write itself is narrow since AGL-1374; what remains\n * cache-dependent is WHICH rows it runs on.)\n */\n fromCache: categoriesFromCache,\n } = useFirestoreCollection<any>(\n /*\n * ORDERED AND CEILINGED, deliberately not paged by the query (AGL-2501).\n *\n * `limit(250)` alone is answered in DOCUMENT-ID order and the ids are\n * `createResourceUid()`, so the window was a pseudo-random sample. Naming\n * the order does not change WHICH 250 come back; what it changes is that\n * the obvious next edit is caught — `orderBy('name')` would HIDE every\n * category saved without one, and `orderBy('order')` is worse still, since\n * the walk below already treats a missing `order` as 0.\n *\n * The QUERY is not paged because this is a TREE. The walk puts parents\n * before children and collects orphans by scanning the whole set, so a\n * page boundary would separate a child from the parent that positions it\n * and re-label as an orphan every category whose parent is on another\n * page. The parent PICKER in the dialog reads the same rows for the same\n * reason.\n */\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'productCategories'),\n CATEGORY_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readCategories, truncated: categoriesTruncated } =\n ceilingedWindow<any>(categoryDocs, CATEGORY_CEILING)\n const {\n data: collectionDocs,\n status: collectionsStatus,\n /**\n * The collection rows are unconfirmed by the server (AGL-1358). The\n * editor copies a whole stored row and the save posts all of it — the\n * transport is the AGL-978 route rather than a client `setDoc`, but the\n * shape is identical, and so is the damage: `productIds` is the manual\n * membership, so a cached seed drops every product added since that\n * snapshot and the storefront blocks pointing at the collection go empty.\n */\n fromCache: collectionsFromCache,\n } = useFirestoreCollection<any>(\n /*\n * ORDERED AND CEILINGED, deliberately not paged (AGL-2501).\n *\n * The slug check below tests a draft against these rows — two catalog\n * collections answering `/collections/{slug}` is a storefront route that\n * resolves to whichever the server reaches first — so a ten-row server\n * page would compare a new collection against a tenth of the catalog and\n * create the collision the check exists to prevent.\n *\n * The rows are also filtered after reading: content collections (AGL-81)\n * share this subcollection and are classified out below, so a page of ten\n * documents arrives holding anywhere from zero to ten catalog collections.\n */\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'collections'),\n COLLECTION_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readCollections, truncated: collectionsTruncated } =\n ceilingedWindow<any>(collectionDocs, COLLECTION_CEILING)\n const { data: productDocs } = useFirestoreCollection<any>(\n /*\n * The catalog behind the MATCH COUNTS, ceilinged with a probe (AGL-2501).\n *\n * `collectionCount` and the smart-collection preview both count this array\n * and print the result as a number of products. It was a bare `limit(500)`,\n * so on a three-thousand-product catalog \"Matches 47 products\" meant \"47 of\n * the five hundred I read\" — a count that is a window length, on the\n * control that decides what a collection contains.\n *\n * It stays a window, because the honest alternative is reading the whole\n * catalog on every mount of this card. What changes is that the probe makes\n * the shortfall a FACT, and the counts say \"at least\" when it bit rather\n * than stating a total they cannot know.\n */\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_CEILING,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const { rows: readProducts, truncated: productsTruncated } =\n ceilingedWindow<any>(productDocs, PRODUCT_CEILING)\n const products: ProductRow[] = useMemo(\n () =>\n [...readProducts]\n .filter((product: any) => !product.deletedAt)\n .map((product: any) => ({\n ...CommerceModel.liftLegacyProduct(product),\n $id: product.$id,\n })),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [productDocs],\n )\n // Content collections (AGL-81) live in the same `hosts/{hostId}/collections`\n // subcollection. This used to keep them out by requiring a non-empty\n // `name`, which held only because the Content page happens to write\n // `displayName` — a content doc that ever acquired a `name` (an import, a\n // hand edit) would land in this list, and its Delete reached the\n // recursiveDelete route (AGL-947). The shared classifier is the real\n // check (AGL-954); the route re-checks it, and AGL-1324 now refuses any\n // collection with entries besides.\n const commerceCollections: CollectionRow[] = useMemo(\n () => readCollections.filter(Aglyn.isHostCollectionKind('catalog')),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [collectionDocs],\n )\n\n // Categories ordered as a walked tree: parents before children.\n const categories: Array<CategoryRow & { depth: number }> = useMemo(() => {\n const rows = [...readCategories] as CategoryRow[]\n rows.sort(\n (a, b) => (a.order ?? 0) - (b.order ?? 0) || a.name.localeCompare(b.name),\n )\n const byParent = new Map<string | null, CategoryRow[]>()\n for (const row of rows) {\n const key = row.parentId ?? null\n byParent.set(key, [...(byParent.get(key) ?? []), row])\n }\n const walked: Array<CategoryRow & { depth: number }> = []\n const walk = (parentId: string | null, depth: number) => {\n for (const row of byParent.get(parentId) ?? []) {\n walked.push({ ...row, depth })\n if (depth < 4) walk(row.$id, depth + 1)\n }\n }\n walk(null, 0)\n // Orphans (parent deleted) still show, at the root.\n for (const row of rows) {\n if (!walked.some((item) => item.$id === row.$id)) {\n walked.push({ ...row, depth: 0 })\n }\n }\n return walked\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [categoryDocs])\n\n /*\n * Both pages are SLICES: the rows are already in hand, and the tree walk,\n * the parent picker and the slug check all need every one of them.\n */\n const [categoryPage, setCategoryPage] = useState(0)\n const [categoryPageSize, setCategoryPageSize] = useState(\n TABLE_PAGE_SIZE_DEFAULT,\n )\n const visibleCategories = useMemo(\n () =>\n categories.slice(\n categoryPage * categoryPageSize,\n categoryPage * categoryPageSize + categoryPageSize,\n ),\n [categories, categoryPage, categoryPageSize],\n )\n const [collectionPage, setCollectionPage] = useState(0)\n const [collectionPageSize, setCollectionPageSize] = useState(\n TABLE_PAGE_SIZE_DEFAULT,\n )\n const visibleCollections = useMemo(\n () =>\n commerceCollections.slice(\n collectionPage * collectionPageSize,\n collectionPage * collectionPageSize + collectionPageSize,\n ),\n [commerceCollections, collectionPage, collectionPageSize],\n )\n\n const [categoryDraft, setCategoryDraft] = useState<CategoryDraft | null>(null)\n const [collectionDraft, setCollectionDraft] = useState<CollectionDraft | null>(\n null,\n )\n\n const handleCategorySave = useCallback(async () => {\n if (!categoryDraft?.name.trim()) return\n const id = categoryDraft.id ?? Aglyn.createResourceUid()\n /**\n * Refuse an EDIT whose seed the server never confirmed (AGL-1358).\n *\n * The payload is narrower than most sites in this issue, but `parentId`\n * is carried off the seeded row on every save whether or not the author\n * opened the parent picker — so renaming a category against a cached read\n * can move it, and the whole subtree under it, back to a parent someone\n * had already reorganised away from.\n *\n * Only the edit path. A NEW category is built from blanks at a fresh uid\n * and can overwrite nothing, and the first snapshot of any listener is\n * `fromCache: true`, so guarding a create would refuse a save that was\n * never unsafe.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'category',\n unreadable: Boolean(categoryDraft.id) && categoriesStatus === 'error',\n fromCache: Boolean(categoryDraft.id) && categoriesFromCache,\n },\n async () => {\n /**\n * `merge: true`, because this payload is NARROWER than the document\n * (AGL-1372). A replacing write deletes every stored key the form\n * does not send, and this form sends four:\n *\n * - `order` — the tree position. Both sorts read it (the walk above,\n * and `queryPublicCatalog`'s facet chips), and nothing in this card\n * can set it, so it is not the form's to send: a rename dropped it\n * and the category fell back to `?? 0`, reordering the storefront's\n * filter chips.\n * - `createdAt` — written at creation by the seeder, never by this\n * card, so a rename erased it too.\n *\n * Carrying `order` in the payload would have fixed the field we\n * happened to notice and left `createdAt`, plus whatever the document\n * grows next. Merging fixes the shape. Nothing here needs delete\n * semantics — `parentId` clears to an explicit `null`, which merges.\n */\n await setDoc(\n doc(firestore, 'hosts', hostId, 'productCategories', id),\n {\n name: categoryDraft.name.trim().slice(0, 80),\n slug: CommerceModel.commerceSlug(categoryDraft.name),\n parentId: categoryDraft.parentId || null,\n updatedAt: Timestamp.now(),\n },\n { merge: true },\n )\n },\n )\n // Before `setCategoryDraft(null)`, so a refusal keeps the dialog open\n // with what was typed.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setCategoryDraft(null)\n enqueueSnackbar('Category saved', { variant: 'success', persist: false })\n }, [\n categoryDraft,\n firestore,\n hostId,\n enqueueSnackbar,\n categoriesStatus,\n categoriesFromCache,\n ])\n\n const handleCategoryDelete = useCallback(\n (category: CategoryRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this category?',\n description:\n `Products keep their other categories; children of ` +\n `\"${category.name}\" move to the top level.`,\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n /**\n * Refuse the whole sequence when the rows it reads were never confirmed\n * by the server (AGL-1358).\n *\n * The reparent is this issue's shape at its least visible: the author\n * opened no editor, so there is nothing on screen to look stale, and\n * `children` is read straight out of the cache. The write itself is\n * now narrow (see below), so a stale row can no longer be copied back\n * over a child — but WHICH rows are children is still a cached answer.\n *\n * The guard encloses the DELETE as well, for the reason the site\n * details rename did: `children` is computed from the same seed, so a\n * cached read can miss children entirely, and deleting the parent\n * without reparenting them leaves them pointing at a category that no\n * longer exists. Half of this sequence is worse than none of it.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'categories',\n unreadable: categoriesStatus === 'error',\n fromCache: categoriesFromCache,\n },\n async () => {\n // Reparent children to root, then remove.\n const children = (categoryDocs ?? []).filter(\n (row: any) => row.parentId === category.$id,\n )\n /**\n * Write only the field the reparent changes (AGL-1374).\n *\n * `{...child}` spread the listener row, and that row carries a\n * SYNTHETIC `$id` — `idField: '$id'` stamps the document id onto\n * the in-memory object, where nothing persists it. Spreading it\n * into the payload persisted it: every delete-with-children added\n * a real `$id` key to each child, permanently, and a child\n * reparented twice could end up carrying one that no longer\n * matched its own document id. Nothing reads it, so nothing broke\n * — it is the listener's bookkeeping leaking into storage, and\n * once there no reader can tell it from a real field.\n *\n * `{ parentId: null }` with `{ merge: true }` is what the reparent\n * actually means, and it is the stronger fix than stripping `$id`\n * off a full spread: the old write had no options argument, so it\n * was also a whole-document REPLACE of a cached row — the AGL-1358\n * hazard in miniature (and AGL-1372's, one collection over). A\n * narrow merge cannot carry a synthetic key it never names, and\n * cannot rewrite a field the delete has no business touching.\n *\n * `null` is a value, not a deletion, so it merges: the child ends\n * up at the top level, which is what the confirmation promised.\n */\n for (const child of children) {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'productCategories', child.$id),\n { parentId: null },\n { merge: true },\n )\n }\n await deleteDoc(\n doc(firestore, 'hosts', hostId, 'productCategories', category.$id),\n )\n },\n )\n // This path had no report of its own. A delete that silently does\n // nothing after the user confirmed it reads as a delete that worked.\n if (!verdict.ok) {\n enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n },\n [\n confirm,\n categoryDocs,\n firestore,\n hostId,\n enqueueSnackbar,\n categoriesStatus,\n categoriesFromCache,\n ],\n )\n\n const draftSlug = collectionDraft\n ? collectionDraft.slug || CommerceModel.commerceSlug(collectionDraft.name)\n : ''\n // The slug is the collection's public address at /collections/{slug} and\n // nothing enforced uniqueness (AGL-957) — a second one at the same slug\n // made the first unreachable with no error anywhere. Checked against the\n // catalog collections only; content collections have their own namespace.\n const slugTaken =\n collectionDraft !== null &&\n Aglyn.isCollectionSlugTaken(\n draftSlug,\n 'catalog',\n commerceCollections,\n collectionDraft.id,\n )\n const collectionError = collectionDraft\n ? collectionDraft.name\n ? (CommerceModel.validateCollection({\n ...collectionDraft,\n slug: draftSlug,\n }) ??\n (slugTaken\n ? `Another collection already serves /collections/${draftSlug}`\n : null))\n : null\n : null\n\n const collectionProductIds = collectionDraft?.productIds\n const collectionProducts = useMemo(\n () =>\n products.filter((product) =>\n (collectionProductIds ?? []).includes(product.$id),\n ),\n [products, collectionProductIds],\n )\n const setCollectionProducts = useCallback(\n (picked: ProductRow[]) =>\n setCollectionDraft((prev) =>\n prev ? { ...prev, productIds: picked.map((item) => item.$id) } : prev,\n ),\n [],\n )\n\n /*\n * Recomputed when what decides membership changes — the mode, the picked\n * products, the rules and whether all must hold — and not on a keystroke in\n * the name, which no match reads (AGL-3423).\n */\n const draftMode = collectionDraft?.mode\n const draftRules = collectionDraft?.rules\n const draftMatchAll = collectionDraft?.matchAll\n const previewMatches = useMemo(() => {\n if (!draftMode) return []\n const candidate = {\n mode: draftMode,\n productIds: collectionProductIds,\n rules: draftRules,\n matchAll: draftMatchAll,\n } as CommerceModel.HostCollection\n return products.filter((product) =>\n CommerceModel.matchesCollection(product, candidate, product.$id),\n )\n }, [draftMode, collectionProductIds, draftRules, draftMatchAll, products])\n\n /**\n * Which products a smart collection holds is stored on the products\n * (`collectionIds`, AGL-3321), so a save or a delete re-stamps them —\n * bounded passes through the commerce route until it says it is done.\n * A failure is reported, not thrown: the collection itself saved.\n */\n const restampMembership = useCallback(\n async (collectionId: string) => {\n try {\n let after: string | undefined\n for (let pass = 0; pass < 100; pass += 1) {\n const response = await authorizedFetch(\n user,\n '/api/commerce/collection-membership',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, collectionId, ...(after ? { after } : {}) }),\n },\n )\n const result = await response.json().catch(() => ({}))\n if (!response.ok) throw new Error(result?.error ?? 'Membership update failed')\n if (result.done || !result.after) return\n after = result.after\n }\n } catch (error: any) {\n enqueueSnackbar(\n `${error?.message ?? 'Membership update failed'} — the storefront may list this collection's old products until it is saved again.`,\n { variant: 'warning' },\n )\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n const handleCollectionSave = useCallback(async () => {\n if (!collectionDraft?.name.trim() || collectionError) return\n const { id: draftId, ...data } = collectionDraft\n // Both create and rename go through the route (AGL-978): this save writes\n // the whole document including `slug`, which is the collection's public\n // address and is claimed transactionally there. Rules deny a client\n // create, and freeze `slug`/`kind` on update.\n /**\n * Refuse an UPDATE whose seed the server never confirmed (AGL-1358).\n *\n * The transport is the route rather than a client `setDoc`, but the shape\n * is the one this issue is about: `data` is the whole stored row copied\n * out of the listener, and the route writes what it is sent. `productIds`\n * is the manual membership, so a cached seed drops every product added\n * since that snapshot and the storefront blocks pointing at the\n * collection go empty; `rules` and `matchAll` do the same for a smart\n * collection. That the write leaves the browser over HTTP changes who\n * performs it, not what it destroys.\n *\n * Only the update path. A CREATE is built from blanks, and the route\n * claims a fresh slug transactionally, so it can overwrite nothing.\n *\n * The guard WRAPS the request — an early return is a shape you can keep\n * while losing the protection.\n */\n let verdict: Awaited<ReturnType<typeof writeGuardedBySeed>>\n let savedId = ''\n try {\n verdict = await writeGuardedBySeed(\n {\n subject: 'collection',\n unreadable: Boolean(draftId) && collectionsStatus === 'error',\n fromCache: Boolean(draftId) && collectionsFromCache,\n },\n async () => {\n const response = await authorizedFetch(\n user,\n '/api/hosts/collections',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n action: draftId ? 'update' : 'create',\n kind: 'catalog',\n ...(draftId ? { id: draftId } : {}),\n data: {\n ...data,\n name: collectionDraft.name.trim().slice(0, 80),\n slug: draftSlug,\n },\n }),\n },\n )\n const result = await response.json().catch(() => ({}))\n if (!response.ok) {\n throw new Error(result?.error ?? 'Collection save failed')\n }\n savedId = String(draftId ?? result?.id ?? '')\n },\n )\n } catch (error: any) {\n return void enqueueSnackbar(error?.message ?? 'Collection save failed', {\n variant: 'error',\n })\n }\n // Before `setCollectionDraft(null)`, so a refusal keeps the dialog open\n // with the membership that was picked.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setCollectionDraft(null)\n enqueueSnackbar('Collection saved', { variant: 'success', persist: false })\n if (savedId) await restampMembership(savedId)\n }, [\n restampMembership,\n collectionDraft,\n collectionError,\n draftSlug,\n user,\n hostId,\n enqueueSnackbar,\n collectionsStatus,\n collectionsFromCache,\n ])\n\n const handleCollectionDelete = useCallback(\n (row: CollectionRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this collection?',\n // NOT a cascade any more (AGL-1324/AGL-1336): the route REFUSES a\n // collection anything still depends on. Promising to take the\n // entries with it was the opposite of what happens, and the two\n // blockers are the whole point of the feature — say them before the\n // button is armed, not in a 409 afterwards.\n description:\n `Storefront blocks pointing at \"${row.name}\" go empty. The delete ` +\n 'is refused while the collection still has entries, or while a ' +\n 'page uses it as a list or entry template — empty it and detach ' +\n 'those pages first. It also takes the site admin role.',\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n // A collection owns an `entries` subcollection (the Content page's\n // published entries) and Firestore doesn't cascade, so deleting the\n // doc from here stranded them — still editor-writable through the\n // host catch-all rule, just unreachable. recursiveDelete is\n // Admin-SDK-only, hence the route (AGL-947). Since AGL-1324 the route\n // also refuses rather than cascading; the 409's message names the\n // blockers and is surfaced by the catch below.\n try {\n const response = await authorizedFetch(user, '/api/resources/erase', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n scope: 'hosts',\n scopeId: hostId,\n kind: 'collections',\n id: row.$id,\n // The route re-checks this server-side (AGL-954): a stale client\n // must not be able to recursiveDelete a content collection's\n // entries through the catalog card.\n collectionKind: 'catalog',\n }),\n })\n const result = await response.json().catch(() => ({}))\n if (!response.ok) {\n throw new Error(result?.error ?? 'Collection delete failed')\n }\n } catch (error: any) {\n return void enqueueSnackbar(error?.message ?? 'Collection delete failed', {\n variant: 'error',\n })\n }\n enqueueSnackbar('Collection deleted', { variant: 'success', persist: false })\n await restampMembership(row.$id)\n },\n [confirm, user, hostId, enqueueSnackbar, restampMembership],\n )\n\n /*\n * \"at least\", or nothing at all.\n *\n * Every count on this card is computed over `products`, which is the catalog\n * WINDOW rather than the catalog. While the probe finds a product past the\n * ceiling those counts are lower bounds, and printing one as a total is the\n * count-that-is-a-window-length defect this ceiling exists to make visible.\n *\n * Keyed on the probe rather than on `products.length >= PRODUCT_CEILING`,\n * because a catalog of exactly the ceiling is complete: nothing is missing,\n * and a comparison would put \"at least\" on a number that is exact.\n */\n const countPrefix = productsTruncated ? 'at least ' : ''\n\n /*\n * A rule's change, written over the collection draft as last rendered, from\n * a handler that keeps its identity so a rule row redraws only when its own\n * rule changes (AGL-3423).\n */\n const latestCollectionDraft = useRef(collectionDraft)\n latestCollectionDraft.current = collectionDraft\n const updateRule = useCallback<RuleChange>((index, patch) => {\n const draft = latestCollectionDraft.current\n if (!draft) return\n const rules = [...(draft.rules ?? [])]\n if (patch === null) rules.splice(index, 1)\n else\n rules[index] = {\n field: 'tag',\n op: 'eq',\n value: '',\n ...rules[index],\n ...patch,\n }\n setCollectionDraft({ ...draft, rules })\n }, [])\n\n return (\n <CardDisplay\n header={'Categories & collections'}\n HeaderProps={{\n action: (\n <TransferHeaderActions\n resource={COMMERCE_CATEGORIES_TRANSFER}\n hostId={hostId}\n noun=\"categories\"\n />\n ),\n }}\n help={pluginDocsHelp('catalog', { anchor: '#categories-and-tags' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <CategoriesSection\n categories={categories}\n visible={visibleCategories}\n page={categoryPage}\n pageSize={categoryPageSize}\n truncated={categoriesTruncated}\n onPageChange={setCategoryPage}\n onPageSizeChange={setCategoryPageSize}\n onEdit={setCategoryDraft}\n onDelete={handleCategoryDelete}\n />\n\n <Divider sx={{ my: 1 }} />\n <CollectionsSection\n collections={commerceCollections}\n visible={visibleCollections}\n products={products}\n countPrefix={countPrefix}\n page={collectionPage}\n pageSize={collectionPageSize}\n truncated={collectionsTruncated}\n onPageChange={setCollectionPage}\n onPageSizeChange={setCollectionPageSize}\n onEdit={setCollectionDraft}\n onDelete={handleCollectionDelete}\n />\n </Stack>\n\n <CategoryDialog\n draft={categoryDraft}\n categories={categories}\n setDraft={setCategoryDraft}\n onSave={handleCategorySave}\n />\n\n <CollectionDialog\n draft={collectionDraft}\n slug={draftSlug}\n slugTaken={slugTaken}\n error={collectionError}\n products={products}\n picked={collectionProducts}\n categories={categories}\n matches={previewMatches}\n countPrefix={countPrefix}\n setDraft={setCollectionDraft}\n onProducts={setCollectionProducts}\n onRule={updateRule}\n onSave={handleCollectionSave}\n />\n </CardDisplay>\n )\n}\nCatalogOrganizationCard.displayName = 'CatalogOrganizationCard'\n\nexport default CatalogOrganizationCard\n"],"names":["Aglyn","CommerceModel","CardDisplay","useConfirmationContext","useSnackbar","Timestamp","Alert","Autocomplete","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Divider","MenuItem","Stack","Switch","TextField","Typography","collection","deleteDoc","doc","setDoc","memo","useCallback","useMemo","useRef","useState","useFirestore","useUser","ceilingedWindow","collectionCeiling","useFirestoreCollection","writeGuardedBySeed","ListPagination","TABLE_PAGE_SIZE_DEFAULT","pluginDocsHelp","authorizedFetch","TransferHeaderActions","COMMERCE_CATEGORIES_TRANSFER","CATEGORY_CEILING","COLLECTION_CEILING","PRODUCT_CEILING","RULE_FIELDS","value","label","RULE_OPS","productLabel","product","name","sameProduct","option","$id","CollectionProductsField","props","onChange","multiple","options","products","getOptionLabel","isOptionEqualToValue","_event","picked","renderInput","params","size","MemoTextField","FIRST_FIELD","mt","RULE_FIELD","width","RULE_OP","RULE_VALUE","flex","MODE_MENU","RULE_FIELD_MENU","map","field","RULE_OP_MENU","op","CategoriesSection","categories","visible","onEdit","onDelete","variant","length","color","category","direction","spacing","sx","alignItems","pl","depth","noWrap","component","slug","onClick","id","parentId","page","pageSize","rowCount","count","onPageChange","onPageSizeChange","truncated","severity","alignSelf","CollectionsSection","collections","countPrefix","collectionCount","row","filter","matchesCollection","mode","productIds","rules","matchAll","CategoryDialog","draft","setDraft","draftId","close","setName","event","prev","target","setParent","parentMenu","repeat","open","Boolean","onClose","maxWidth","fullWidth","display","flexDirection","gap","autoFocus","select","disabled","trim","onSave","CollectionRuleRow","index","rule","setField","setOp","setCategory","ruleField","setValue","Number","categoryMenu","placeholder","CollectionMatchSwitch","checked","CollectionRulesFields","onRule","CollectionPreview","matches","useFlexGap","flexWrap","slice","CollectionDialog","error","setMode","slugTaken","helperText","undefined","onProducts","NO_RULES","CatalogOrganizationCard","hostId","firestore","enqueueSnackbar","confirm","data","user","categoryDocs","status","categoriesStatus","fromCache","categoriesFromCache","idField","rows","readCategories","categoriesTruncated","collectionDocs","collectionsStatus","collectionsFromCache","readCollections","collectionsTruncated","productDocs","readProducts","productsTruncated","deletedAt","liftLegacyProduct","commerceCollections","isHostCollectionKind","sort","a","b","order","localeCompare","byParent","Map","key","set","get","walked","walk","push","some","item","categoryPage","setCategoryPage","categoryPageSize","setCategoryPageSize","visibleCategories","collectionPage","setCollectionPage","collectionPageSize","setCollectionPageSize","visibleCollections","categoryDraft","setCategoryDraft","collectionDraft","setCollectionDraft","handleCategorySave","createResourceUid","verdict","subject","unreadable","commerceSlug","updatedAt","now","merge","ok","message","persist","handleCategoryDelete","confirmed","title","description","confirmationText","confirmationButtonProps","then","catch","children","child","draftSlug","isCollectionSlugTaken","collectionError","validateCollection","collectionProductIds","collectionProducts","includes","setCollectionProducts","draftMode","draftRules","draftMatchAll","previewMatches","candidate","restampMembership","collectionId","after","pass","response","method","headers","body","JSON","stringify","result","json","Error","done","handleCollectionSave","savedId","action","kind","String","handleCollectionDelete","scope","scopeId","collectionKind","latestCollectionDraft","current","updateRule","patch","splice","header","HeaderProps","resource","noun","help","anchor","contentGutterX","contentGutterY","my","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CustomerNotificationsCardProps {
|
|
2
|
+
hostId: string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Which order messages the store sends its buyers (AGL-3610), on
|
|
6
|
+
* `hosts/{hostId}/settings/store.buyerNotifications`. Every moment is ON
|
|
7
|
+
* unless switched off here — transaction messages are on by default for every
|
|
8
|
+
* store — and each switch writes only its own key, so this card can never
|
|
9
|
+
* overwrite the store settings card's fields beside it.
|
|
10
|
+
*
|
|
11
|
+
* The "Also send as texts" switch appears only when the platform can send
|
|
12
|
+
* texts (the receipt route's channel answer), so the card never offers a
|
|
13
|
+
* channel that does not exist.
|
|
14
|
+
*/
|
|
15
|
+
export declare function CustomerNotificationsCard(props: CustomerNotificationsCardProps): import("react").JSX.Element;
|
|
16
|
+
export declare namespace CustomerNotificationsCard {
|
|
17
|
+
var displayName: string;
|
|
18
|
+
}
|
|
19
|
+
export default CustomerNotificationsCard;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
19
|
+
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
20
|
+
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
21
|
+
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
22
|
+
import { useFirestore, useFirestoreDoc, useUser } from "@aglyn/tenant-feature-instance";
|
|
23
|
+
import { FormControlLabel, Stack, Switch, Typography } from "@mui/material";
|
|
24
|
+
import { doc, setDoc } from "firebase/firestore";
|
|
25
|
+
import { useCallback, useEffect, useState } from "react";
|
|
26
|
+
import * as CommerceModel from "../../model/index.js";
|
|
27
|
+
/**
|
|
28
|
+
* Which order messages the store sends its buyers (AGL-3610), on
|
|
29
|
+
* `hosts/{hostId}/settings/store.buyerNotifications`. Every moment is ON
|
|
30
|
+
* unless switched off here — transaction messages are on by default for every
|
|
31
|
+
* store — and each switch writes only its own key, so this card can never
|
|
32
|
+
* overwrite the store settings card's fields beside it.
|
|
33
|
+
*
|
|
34
|
+
* The "Also send as texts" switch appears only when the platform can send
|
|
35
|
+
* texts (the receipt route's channel answer), so the card never offers a
|
|
36
|
+
* channel that does not exist.
|
|
37
|
+
*/ export function CustomerNotificationsCard(props) {
|
|
38
|
+
const { hostId } = props;
|
|
39
|
+
const firestore = useFirestore();
|
|
40
|
+
const { data: user } = useUser();
|
|
41
|
+
const { enqueueSnackbar } = useSnackbar();
|
|
42
|
+
const { data: store } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId, 'settings', 'store'), [
|
|
43
|
+
firestore,
|
|
44
|
+
hostId
|
|
45
|
+
]);
|
|
46
|
+
const [smsAvailable, setSmsAvailable] = useState(false);
|
|
47
|
+
const [saving, setSaving] = useState(null);
|
|
48
|
+
useEffect(()=>{
|
|
49
|
+
let live = true;
|
|
50
|
+
void authorizedFetch(user, `/api/commerce/order-receipt-send?hostId=${encodeURIComponent(hostId)}`).then((response)=>response.ok ? response.json() : {}).then((payload)=>{
|
|
51
|
+
if (live) setSmsAvailable((payload == null ? void 0 : payload.sms) === true);
|
|
52
|
+
}).catch(()=>undefined);
|
|
53
|
+
return ()=>{
|
|
54
|
+
live = false;
|
|
55
|
+
};
|
|
56
|
+
}, [
|
|
57
|
+
hostId,
|
|
58
|
+
user
|
|
59
|
+
]);
|
|
60
|
+
const settings = store == null ? void 0 : store.buyerNotifications;
|
|
61
|
+
const handleToggle = useCallback(async (key, on)=>{
|
|
62
|
+
setSaving(key);
|
|
63
|
+
try {
|
|
64
|
+
await setDoc(doc(firestore, 'hosts', hostId, 'settings', 'store'), {
|
|
65
|
+
buyerNotifications: {
|
|
66
|
+
[key]: on
|
|
67
|
+
}
|
|
68
|
+
}, {
|
|
69
|
+
merge: true
|
|
70
|
+
});
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(error);
|
|
73
|
+
enqueueSnackbar('That setting could not be saved. Try again.', {
|
|
74
|
+
variant: 'error'
|
|
75
|
+
});
|
|
76
|
+
} finally{
|
|
77
|
+
setSaving(null);
|
|
78
|
+
}
|
|
79
|
+
}, [
|
|
80
|
+
enqueueSnackbar,
|
|
81
|
+
firestore,
|
|
82
|
+
hostId
|
|
83
|
+
]);
|
|
84
|
+
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
85
|
+
header: 'Customer notifications',
|
|
86
|
+
help: pluginDocsHelp('commerce'),
|
|
87
|
+
contentGutterX: true,
|
|
88
|
+
contentGutterY: true,
|
|
89
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
90
|
+
spacing: 1,
|
|
91
|
+
children: [
|
|
92
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
93
|
+
variant: "body2",
|
|
94
|
+
color: "text.secondary",
|
|
95
|
+
children: 'The emails your customers get about their orders. Each one links ' + 'to a private order status page with shipments and tracking. ' + 'Change their wording and colors under Emails.'
|
|
96
|
+
}),
|
|
97
|
+
CommerceModel.BUYER_NOTIFICATION_EVENTS.map((event)=>{
|
|
98
|
+
const label = CommerceModel.BUYER_NOTIFICATION_LABELS[event];
|
|
99
|
+
return /*#__PURE__*/ _jsx(FormControlLabel, {
|
|
100
|
+
control: /*#__PURE__*/ _jsx(Switch, {
|
|
101
|
+
size: "small",
|
|
102
|
+
checked: CommerceModel.buyerNotificationEnabled(settings, event),
|
|
103
|
+
disabled: saving === event,
|
|
104
|
+
onChange: (change)=>handleToggle(event, change.target.checked)
|
|
105
|
+
}),
|
|
106
|
+
label: /*#__PURE__*/ _jsxs(Stack, {
|
|
107
|
+
children: [
|
|
108
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
109
|
+
variant: "body2",
|
|
110
|
+
children: label.label
|
|
111
|
+
}),
|
|
112
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
113
|
+
variant: "caption",
|
|
114
|
+
color: "text.secondary",
|
|
115
|
+
children: label.description
|
|
116
|
+
})
|
|
117
|
+
]
|
|
118
|
+
})
|
|
119
|
+
}, event);
|
|
120
|
+
}),
|
|
121
|
+
smsAvailable ? /*#__PURE__*/ _jsx(FormControlLabel, {
|
|
122
|
+
control: /*#__PURE__*/ _jsx(Switch, {
|
|
123
|
+
size: "small",
|
|
124
|
+
checked: CommerceModel.buyerNotificationEnabled(settings, 'texts'),
|
|
125
|
+
disabled: saving === 'texts',
|
|
126
|
+
onChange: (change)=>handleToggle('texts', change.target.checked)
|
|
127
|
+
}),
|
|
128
|
+
label: /*#__PURE__*/ _jsxs(Stack, {
|
|
129
|
+
children: [
|
|
130
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
131
|
+
variant: "body2",
|
|
132
|
+
children: 'Also send as texts'
|
|
133
|
+
}),
|
|
134
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
135
|
+
variant: "caption",
|
|
136
|
+
color: "text.secondary",
|
|
137
|
+
children: 'When an order has the customer’s phone number for updates. ' + 'Customers can reply STOP to opt out.'
|
|
138
|
+
})
|
|
139
|
+
]
|
|
140
|
+
})
|
|
141
|
+
}) : null
|
|
142
|
+
]
|
|
143
|
+
})
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
CustomerNotificationsCard.displayName = 'CustomerNotificationsCard';
|
|
147
|
+
export default CustomerNotificationsCard;
|
|
148
|
+
|
|
149
|
+
//# sourceMappingURL=customer-notifications-card.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/customer-notifications-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n useFirestore,\n useFirestoreDoc,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { FormControlLabel, Stack, Switch, Typography } from '@mui/material'\nimport { doc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport * as CommerceModel from '../../model'\n\nexport interface CustomerNotificationsCardProps {\n hostId: string\n}\n\n/**\n * Which order messages the store sends its buyers (AGL-3610), on\n * `hosts/{hostId}/settings/store.buyerNotifications`. Every moment is ON\n * unless switched off here — transaction messages are on by default for every\n * store — and each switch writes only its own key, so this card can never\n * overwrite the store settings card's fields beside it.\n *\n * The \"Also send as texts\" switch appears only when the platform can send\n * texts (the receipt route's channel answer), so the card never offers a\n * channel that does not exist.\n */\nexport function CustomerNotificationsCard(props: CustomerNotificationsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { data: store } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const [smsAvailable, setSmsAvailable] = useState(false)\n const [saving, setSaving] = useState<string | null>(null)\n\n useEffect(() => {\n let live = true\n void authorizedFetch(\n user,\n `/api/commerce/order-receipt-send?hostId=${encodeURIComponent(hostId)}`,\n )\n .then((response) => (response.ok ? response.json() : {}))\n .then((payload: any) => {\n if (live) setSmsAvailable(payload?.sms === true)\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n\n const settings = store?.buyerNotifications\n const handleToggle = useCallback(\n async (key: CommerceModel.BuyerNotificationEvent | 'texts', on: boolean) => {\n setSaving(key)\n try {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'settings', 'store'),\n { buyerNotifications: { [key]: on } },\n { merge: true },\n )\n } catch (error) {\n console.error(error)\n enqueueSnackbar('That setting could not be saved. Try again.', {\n variant: 'error',\n })\n } finally {\n setSaving(null)\n }\n },\n [enqueueSnackbar, firestore, hostId],\n )\n\n return (\n <CardDisplay\n header={'Customer notifications'}\n help={pluginDocsHelp('commerce')}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The emails your customers get about their orders. Each one links ' +\n 'to a private order status page with shipments and tracking. ' +\n 'Change their wording and colors under Emails.'}\n </Typography>\n {CommerceModel.BUYER_NOTIFICATION_EVENTS.map((event) => {\n const label = CommerceModel.BUYER_NOTIFICATION_LABELS[event]\n return (\n <FormControlLabel\n key={event}\n control={\n <Switch\n size=\"small\"\n checked={CommerceModel.buyerNotificationEnabled(settings, event)}\n disabled={saving === event}\n onChange={(change) => handleToggle(event, change.target.checked)}\n />\n }\n label={\n <Stack>\n <Typography variant=\"body2\">{label.label}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {label.description}\n </Typography>\n </Stack>\n }\n />\n )\n })}\n {smsAvailable ? (\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={CommerceModel.buyerNotificationEnabled(settings, 'texts')}\n disabled={saving === 'texts'}\n onChange={(change) => handleToggle('texts', change.target.checked)}\n />\n }\n label={\n <Stack>\n <Typography variant=\"body2\">{'Also send as texts'}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'When an order has the customer’s phone number for updates. ' +\n 'Customers can reply STOP to opt out.'}\n </Typography>\n </Stack>\n }\n />\n ) : null}\n </Stack>\n </CardDisplay>\n )\n}\nCustomerNotificationsCard.displayName = 'CustomerNotificationsCard'\n\nexport default CustomerNotificationsCard\n"],"names":["pluginDocsHelp","CardDisplay","useSnackbar","authorizedFetch","useFirestore","useFirestoreDoc","useUser","FormControlLabel","Stack","Switch","Typography","doc","setDoc","useCallback","useEffect","useState","CommerceModel","CustomerNotificationsCard","props","hostId","firestore","data","user","enqueueSnackbar","store","smsAvailable","setSmsAvailable","saving","setSaving","live","encodeURIComponent","then","response","ok","json","payload","sms","catch","undefined","settings","buyerNotifications","handleToggle","key","on","merge","error","console","variant","header","help","contentGutterX","contentGutterY","spacing","color","BUYER_NOTIFICATION_EVENTS","map","event","label","BUYER_NOTIFICATION_LABELS","control","size","checked","buyerNotificationEnabled","disabled","onChange","change","target","description","displayName"],"mappings":"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"}
|