@aglyn/plugins-commerce 1.0.0-beta.221 → 1.0.0-beta.222
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/README.md +4 -2
- package/package.json +14 -13
- package/src/lib/components/console/catalog-organization-card.component.js +9 -0
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
- package/src/lib/components/console/discounts-card.component.js +8 -0
- package/src/lib/components/console/discounts-card.component.js.map +1 -1
- package/src/lib/components/console/gift-cards-card.component.js +8 -0
- package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
- package/src/lib/components/console/host-coupons-card.component.js +8 -0
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
- package/src/lib/components/console/host-orders-card.component.js +34 -70
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/product-zones.d.ts +7 -6
- package/src/lib/components/console/product-zones.js +5 -4
- package/src/lib/components/console/product-zones.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +86 -284
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +38 -0
- package/src/lib/declarations.console-server.js +67 -0
- package/src/lib/declarations.console-server.js.map +1 -0
- package/src/lib/model/commerce-gift-cards.d.ts +26 -0
- package/src/lib/model/commerce-gift-cards.js +42 -0
- package/src/lib/model/commerce-gift-cards.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +0 -57
- package/src/lib/model/commerce-orders.js +0 -80
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/index.d.ts +0 -1
- package/src/lib/model/index.js +0 -1
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/plugin.js +5 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/api-v1/openapi.d.ts +27 -0
- package/src/lib/server/api-v1/openapi.js +219 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -0
- package/src/lib/server/api-v1/orders-and-products.d.ts +19 -0
- package/src/lib/server/api-v1/orders-and-products.js +419 -0
- package/src/lib/server/api-v1/orders-and-products.js.map +1 -0
- package/src/lib/server/billing-webhook.js +10 -13
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/download.js +4 -1
- package/src/lib/server/download.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +52 -11
- package/src/lib/server/fulfill-order.js +6 -9
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/gift-card-issue.d.ts +57 -0
- package/src/lib/server/gift-card-issue.js +155 -0
- package/src/lib/server/gift-card-issue.js.map +1 -0
- package/src/lib/server/gift-cards.js +25 -84
- package/src/lib/server/gift-cards.js.map +1 -1
- package/src/lib/server/membership-register.js +9 -8
- package/src/lib/server/membership-register.js.map +1 -1
- package/src/lib/server/newsletter.js +7 -5
- package/src/lib/server/newsletter.js.map +1 -1
- package/src/lib/server/paid-media-delivery.d.ts +21 -0
- package/src/lib/server/paid-media-delivery.js +23 -5
- package/src/lib/server/paid-media-delivery.js.map +1 -1
- package/src/lib/server/paid-media-serve-count.d.ts +28 -0
- package/src/lib/server/paid-media-serve-count.js +46 -0
- package/src/lib/server/paid-media-serve-count.js.map +1 -0
- package/src/lib/server/person-eraser.d.ts +45 -0
- package/src/lib/server/person-eraser.js +57 -0
- package/src/lib/server/person-eraser.js.map +1 -0
- package/src/lib/server/refund.js +15 -15
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/restock-flag.d.ts +2 -2
- package/src/lib/server/restock-flag.js +1 -1
- package/src/lib/server/restock-flag.js.map +1 -1
- package/src/lib/server/stream.js +4 -1
- package/src/lib/server/stream.js.map +1 -1
- package/src/lib/server.js +5 -21
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/stand-in-person-records.d.ts +43 -0
- package/src/lib/testing/stand-in-person-records.js +63 -0
- package/src/lib/testing/stand-in-person-records.js.map +1 -0
- package/src/lib/transfer/gift-card-confirm-step.component.d.ts +22 -0
- package/src/lib/transfer/gift-card-confirm-step.component.js +259 -0
- package/src/lib/transfer/gift-card-confirm-step.component.js.map +1 -0
- package/src/lib/transfer/gift-cards-plan.d.ts +29 -0
- package/src/lib/transfer/gift-cards-plan.js +40 -0
- package/src/lib/transfer/gift-cards-plan.js.map +1 -0
- package/src/lib/transfer/gift-cards.server.d.ts +34 -0
- package/src/lib/transfer/gift-cards.server.js +296 -0
- package/src/lib/transfer/gift-cards.server.js.map +1 -0
- package/src/lib/transfer/list-filter.d.ts +29 -0
- package/src/lib/transfer/list-filter.js +112 -0
- package/src/lib/transfer/list-filter.js.map +1 -0
- package/src/lib/transfer/product-import-results.d.ts +29 -0
- package/src/lib/transfer/product-import-results.js +49 -0
- package/src/lib/transfer/product-import-results.js.map +1 -0
- package/src/lib/transfer/product-import-step.component.d.ts +30 -0
- package/src/lib/transfer/product-import-step.component.js +99 -0
- package/src/lib/transfer/product-import-step.component.js.map +1 -0
- package/src/lib/transfer/product-transfer.d.ts +211 -0
- package/src/lib/transfer/product-transfer.js +1594 -0
- package/src/lib/transfer/product-transfer.js.map +1 -0
- package/src/lib/transfer/products.server.d.ts +44 -0
- package/src/lib/transfer/products.server.js +550 -0
- package/src/lib/transfer/products.server.js.map +1 -0
- package/src/lib/transfer/records-transfer.d.ts +182 -0
- package/src/lib/transfer/records-transfer.js +1153 -0
- package/src/lib/transfer/records-transfer.js.map +1 -0
- package/src/lib/transfer/records.server.d.ts +68 -0
- package/src/lib/transfer/records.server.js +455 -0
- package/src/lib/transfer/records.server.js.map +1 -0
- package/src/lib/transfer/register-transfer-resources.d.ts +17 -0
- package/src/lib/transfer/register-transfer-resources.js +171 -0
- package/src/lib/transfer/register-transfer-resources.js.map +1 -0
- package/src/lib/transfer/register-transfer-ui.d.ts +23 -0
- package/src/lib/transfer/register-transfer-ui.js +78 -0
- package/src/lib/transfer/register-transfer-ui.js.map +1 -0
- package/src/lib/transfer/server-common.d.ts +76 -0
- package/src/lib/transfer/server-common.js +165 -0
- package/src/lib/transfer/server-common.js.map +1 -0
- package/src/lib/transfer/transfer-header-actions.component.d.ts +39 -0
- package/src/lib/transfer/transfer-header-actions.component.js +79 -0
- package/src/lib/transfer/transfer-header-actions.component.js.map +1 -0
- package/src/lib/transfer/transfer-keys.d.ts +31 -0
- package/src/lib/transfer/transfer-keys.js +56 -0
- package/src/lib/transfer/transfer-keys.js.map +1 -0
- package/src/lib/model/commerce-io.d.ts +0 -36
- package/src/lib/model/commerce-io.js +0 -245
- package/src/lib/model/commerce-io.js.map +0 -1
- package/src/lib/server/contact-refund.d.ts +0 -117
- package/src/lib/server/contact-refund.js +0 -196
- package/src/lib/server/contact-refund.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/host-orders-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\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { hiddenFilterVisibility } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport {\n type ListFilterClause,\n listFilterGridColumns,\n upsertListFilterClause,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { checkEntitlement, pluginDocsHelp } from '@aglyn/aglyn'\nimport { ORDERS_CUSTOMER_PARAM, ORDERS_ORDER_PARAM } from '../../model/commerce-record-routes'\nimport {\n Alert,\n AlertTitle,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n doc,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n type QueryDocumentSnapshot,\n startAfter,\n where,\n} from 'firebase/firestore'\nimport { useSearchParams } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestore,\n useOrgPlan,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport {\n listQueryConstraints,\n useListQuery,\n} from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nimport {\n OPEN_DISPUTE_CLAUSE,\n ORDER_CHANNEL_OPTIONS,\n ORDER_DISPUTE_OPTIONS,\n ORDER_LIST_FIELDS,\n ORDER_LIST_HEADERS,\n ORDER_LIST_QUERY,\n ORDER_LIST_SELECT_FIELDS,\n ORDER_STATUS_OPTIONS,\n ordersCustomerClause,\n} from '../../constants/orders-list-query'\nimport CommerceStatTile from './commerce-stat-tile.component'\nimport OrderDetailDialog, {\n DISPUTE_COLOR,\n} from './order-detail-dialog.component'\n\n/**\n * The products the card reads to NAME a legacy row and the CSV, to offer the\n * Product filter's choices and to fill the draft dialog's picker. A picker's\n * reach, not the list's: the orders themselves are paged by their query and\n * the Product filter asks it for any product id, named here or not.\n */\nconst PRODUCT_NAME_WINDOW = 100\n\n/**\n * How many open disputes the banner reads. Its own query, `disputeKey ==\n * 'open'`, so a deadline is raised whatever the list is filtered to and\n * however deep in the store the order sits; a store past this many open\n * cases is told \"more than\".\n */\nconst OPEN_DISPUTE_CEILING = 50\n\n/**\n * The money tiles read the last sixty days (thirty, and the thirty before it)\n * on their own bounded query. They are analytics over a period, not a filter\n * over the list, and share the analytics card's bound.\n */\nconst STATS_WINDOW_MS = 60 * 24 * 60 * 60 * 1000\nconst STATS_ORDER_CEILING = 500\n\n/**\n * Export CSV walks the list's own query, page by page, up to this many\n * orders, and says so when the matches run past it.\n */\nconst ORDERS_EXPORT_CEILING = 5000\nconst ORDERS_EXPORT_PAGE = 500\n\n/** The columns the grid draws; every other declared field is a hidden filter column. */\nconst ORDER_VISIBLE_COLUMNS = [\n 'orderLabel',\n 'customerEmail',\n 'channelKey',\n 'statusKey',\n 'createdAtMs',\n]\nconst ORDER_HIDDEN_COLUMNS = hiddenFilterVisibility(\n ORDER_LIST_FIELDS,\n ORDER_VISIBLE_COLUMNS,\n)\n\nexport interface HostOrdersCardProps {\n hostId: string\n}\n\n/**\n * Orders console (AGL-287): filterable list over webhook-written order\n * docs with a detail dialog (timeline, fulfill, refund, cancel, notes,\n * packing slip) and draft orders that send the buyer a payment link.\n *\n * ## Every filter and the search are on the query (AGL-3321)\n *\n * The list used to read the newest two hundred orders and match its Filters\n * panel and search over them, so a Status filter on a busy store answered\n * \"none\" about every order past the two hundredth. Now each clause and the\n * search word are predicates on ONE Firestore query (`ORDER_LIST_QUERY`),\n * over the fields every writer stamps (`orderListFields`), and the footer\n * pages that query's answer. A combination one query cannot hold is refused\n * by name above the grid and never applied to some rows only.\n */\nexport function HostOrdersCard(props: HostOrdersCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const ordersRef = useMemo(\n () => collection(firestore, 'hosts', hostId, 'orders'),\n [firestore, hostId],\n )\n const { data: productDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_NAME_WINDOW,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const productWindow = useMemo(\n () => ceilingedWindow<any>(productDocs ?? undefined, PRODUCT_NAME_WINDOW),\n [productDocs],\n )\n const productNames = useMemo(() => {\n const map: Record<string, string> = {}\n for (const product of productWindow.rows) {\n map[product.$id] = product.name ?? product.$id\n }\n return map\n }, [productWindow])\n\n /**\n * The money tiles are the `commerceAnalytics` surface (AGL-1938,\n * AGL-2056), and this is the third place they appear. Rendering them\n * ungated here would undo both of those passes and hand a Starter org\n * the Pro figures one tab away from the upgrade prompt that refuses\n * them. The TABLE below is not gated — a list of your own orders is not\n * a paid feature.\n */\n const { org, ready: orgReady } = useOrgPlan(hostId)\n const showStats =\n orgReady && checkEntitlement(org as never, 'commerceAnalytics')\n\n /*\n * The grid's clauses (AGL-96, AGL-3317), held here so two things outside\n * the grid can set one: the CRM's contact page links with the buyer's\n * address to answer \"what has this customer ordered\" (AGL-2622) — a whole\n * address is that buyer, a domain every buyer at a company\n * (`ordersCustomerClause`) — and the dispute banner's \"Show them\".\n */\n const searchParams = useSearchParams()\n const [clauses, setClauses] = useState<ListFilterClause[]>(() => {\n const customer = ordersCustomerClause(\n searchParams?.get(ORDERS_CUSTOMER_PARAM) ?? '',\n )\n return customer ? [customer] : []\n })\n const gridFilter = useListGridFilter({\n clauses,\n onChange: setClauses,\n selectFields: ORDER_LIST_SELECT_FIELDS,\n })\n const filtering =\n clauses.length > 0 || gridFilter.searchWords.some((word) => word.trim())\n\n /*\n * The open disputes, on their OWN query (AGL-1796). Raised whatever the\n * list is filtered to — a merchant filtered to \"delivered\" still has a\n * deadline running on a `paid` order, and the evidence window is days —\n * and however far back the order is. Asked before the list so the list's\n * plan is the one a spec reads last.\n */\n const {\n rows: disputedRows,\n hasMore: moreDisputes,\n } = useListQuery<any>({\n collection: ordersRef,\n declaration: ORDER_LIST_QUERY,\n request: { clauses: [OPEN_DISPUTE_CLAUSE] },\n deps: [firestore, hostId],\n idField: '$id',\n pageSize: OPEN_DISPUTE_CEILING,\n // A dispute that closes leaves this query; confirm it before believing it.\n confirmDisappearances: true,\n })\n\n /*\n * THE LIST: every clause and the search word on one query, newest first,\n * paged by the footer. A status, a dispute or a paid draft's address can\n * move an order out of the filtered query mid-session, so a disappearance\n * is confirmed against the server before it is believed (AGL-1196).\n */\n const {\n rows: orders,\n data: loadedOrders,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status: listStatus,\n } = useListQuery<any>({\n collection: ordersRef,\n declaration: ORDER_LIST_QUERY,\n request: { clauses, search: gridFilter.searchWords },\n deps: [firestore, hostId],\n idField: '$id',\n confirmDisappearances: true,\n })\n\n const [selectedId, setSelectedId] = useState<string | null>(null)\n /*\n * An order named in the URL opens in its dialog on arrival (AGL-2622). A\n * contact's timeline names the order that made the person a customer,\n * and the address it links to is this list with `?order={id}`. The list\n * shows one page, and an order from last year is not on it, so the\n * document is read once by id and held beside the page for the dialog; an\n * order that IS loaded is found there first and the read is skipped. Once\n * per id — the ref keeps a re-render from reopening a dialog the merchant\n * has since closed — and the landing read is judged against the ref\n * rather than an effect cleanup, because the `setSelectedId` above\n * re-renders before the read lands and a cleanup keyed on any dep would\n * cancel the answer to the question just asked.\n */\n const seededOrderId = searchParams?.get(ORDERS_ORDER_PARAM) ?? null\n const [seededOrder, setSeededOrder] = useState<any | null>(null)\n const seededOpened = useRef<string | null>(null)\n useEffect(() => {\n if (!seededOrderId || seededOpened.current === seededOrderId) return\n if (listStatus === 'loading') return\n seededOpened.current = seededOrderId\n setSelectedId(seededOrderId)\n if (loadedOrders.some((order: any) => order.$id === seededOrderId)) return\n void getDoc(doc(firestore, 'hosts', hostId, 'orders', seededOrderId))\n .then((snapshot) => {\n if (seededOpened.current !== snapshot.id || !snapshot.exists()) return\n setSeededOrder({ ...snapshot.data(), $id: snapshot.id })\n })\n .catch(() => undefined)\n }, [seededOrderId, loadedOrders, listStatus, firestore, hostId])\n const [draft, setDraft] = useState<{\n productId: string\n variantId: string\n quantity: string\n email: string\n /** Destination the merchant declared, once they have been asked. */\n shipTo?: string\n /**\n * Revealed only when the server refuses for want of a destination\n * (AGL-1792). A merchant whose rates are the same everywhere — or who\n * configured none — is never asked and never sees this field.\n */\n shipCountries?: string[]\n busy?: boolean\n } | null>(null)\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n /**\n * Each order on the page with the values the grid DRAWS. What it filters\n * by is on the query; this is presentation only. `liftLegacyOrder` names\n * a status and channel on a row that predates them, and the item name\n * reads the line items first (AGL-1747): the flat `productId` is written\n * only by the two buy-now Stripe paths.\n */\n const orderRows = useMemo(\n () =>\n orders.map((order: any) => {\n const lifted = CommerceModel.liftLegacyOrder(order)\n return {\n ...order,\n orderLabel: CommerceModel.formatOrderNumber(lifted, order.$id),\n itemName:\n lifted.lineItems?.[0]?.name ??\n productNames[order.productId] ??\n order.productId ??\n '',\n statusKey: lifted.status,\n channelKey: lifted.channel ?? 'online',\n netCents: CommerceModel.orderNetCents(lifted),\n disputeBadge: CommerceModel.describeOrderDispute(lifted),\n }\n }),\n [orders, productNames],\n )\n const productOptions = useMemo(\n () =>\n productWindow.rows.map((product: any) => ({\n value: String(product.$id),\n label: String(product.name ?? product.$id),\n })),\n [productWindow],\n )\n const filterOptions = useMemo(\n () => ({\n statusKey: ORDER_STATUS_OPTIONS,\n channelKey: ORDER_CHANNEL_OPTIONS,\n productIds: productOptions,\n disputeKey: ORDER_DISPUTE_OPTIONS,\n }),\n [productOptions],\n )\n const refused = useMemo(\n () =>\n listQueryRefusals(plan.refused, {\n fields: ORDER_LIST_FIELDS,\n headers: ORDER_LIST_HEADERS,\n options: filterOptions,\n }),\n [plan, filterOptions],\n )\n const showingOpenDisputes = clauses.some(\n (clause) =>\n clause.field === OPEN_DISPUTE_CLAUSE.field &&\n clause.op === OPEN_DISPUTE_CLAUSE.op &&\n clause.value === OPEN_DISPUTE_CLAUSE.value,\n )\n\n const openDisputes = useMemo(\n () =>\n CommerceModel.summariseOpenDisputes(\n disputedRows.map((order: any) => CommerceModel.liftLegacyOrder(order)),\n ),\n [disputedRows],\n )\n\n /*\n * 30-day money summary with the prior 30 days behind it (AGL-2136).\n * ANALYTICS, NOT A FILTER: the tiles describe a period, not the list, so\n * they keep their own bounded read — sixty days by `createdAtMs`, the\n * newest `STATS_ORDER_CEILING` of them, read only when the tiles show.\n * Range and order are the same field, so it needs no composite.\n */\n const statsSince = useMemo(() => Date.now() - STATS_WINDOW_MS, [])\n const { data: statsDocs } = useFirestoreCollection<any>(\n () =>\n showStats\n ? query(\n ordersRef,\n where('createdAtMs', '>=', statsSince),\n orderBy('createdAtMs', 'desc'),\n limit(STATS_ORDER_CEILING + 1),\n )\n : null,\n [firestore, hostId, showStats, statsSince],\n { idField: '$id' },\n )\n const statsWindow = useMemo(\n () => ceilingedWindow<any>(statsDocs ?? undefined, STATS_ORDER_CEILING),\n [statsDocs],\n )\n const summary = useMemo(\n () =>\n CommerceModel.summarizeOrderWindow(statsWindow.rows, { nowMs: Date.now() }),\n [statsWindow],\n )\n\n /**\n * Export CSV writes what the QUERY matches — every clause and the search\n * the grid is showing — not the page on screen. It walks the list's own\n * plan in pages of `ORDERS_EXPORT_PAGE` up to `ORDERS_EXPORT_CEILING`, and\n * says so when the matches run past it.\n */\n const [exporting, setExporting] = useState(false)\n const handleExportCsv = useCallback(async () => {\n setExporting(true)\n try {\n const matched: any[] = []\n let cursor: QueryDocumentSnapshot | null = null\n let more = true\n while (more && matched.length < ORDERS_EXPORT_CEILING) {\n const take = Math.min(\n ORDERS_EXPORT_PAGE,\n ORDERS_EXPORT_CEILING - matched.length,\n )\n // One past the page: whether more match is a fact, not a guess.\n const snapshot = await getDocs(\n query(\n ordersRef,\n ...listQueryConstraints(plan),\n ...(cursor ? [startAfter(cursor)] : []),\n limit(take + 1),\n ),\n )\n const docs = snapshot.docs.slice(0, take)\n for (const entry of docs) matched.push({ ...entry.data(), $id: entry.id })\n more = snapshot.docs.length > take\n cursor = docs[docs.length - 1] ?? null\n if (!cursor) more = false\n }\n // The rows themselves are built by the pure model helper (AGL-1747) so\n // the column-by-column arithmetic is unit-testable without Firestore.\n const csv = CommerceModel.buildOrdersCsv(matched, productNames)\n const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv' }))\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = 'orders.csv'\n anchor.click()\n URL.revokeObjectURL(url)\n if (more) {\n enqueueSnackbar(\n `Exported the ${ORDERS_EXPORT_CEILING.toLocaleString('en-US')} newest matching orders. ` +\n 'Narrow the filters to export the rest.',\n { variant: 'warning', allowDuplicate: true },\n )\n }\n } catch (error) {\n console.error('Orders export failed', error)\n enqueueSnackbar('Export failed — try again', {\n variant: 'error',\n allowDuplicate: true,\n })\n } finally {\n setExporting(false)\n }\n }, [ordersRef, plan, productNames, enqueueSnackbar])\n\n /**\n * Idempotency key for ONE draft attempt (AGL-1697).\n *\n * Minted lazily on the first create click, NOT per call — two clicks (or a\n * click retried after a lost response) must present ONE key so the server\n * replays the original payment link instead of minting a second order.\n * Retired whenever a content field changes: an edited draft is a different\n * order, and replaying the old one against it would hand the merchant a\n * link priced for fields they no longer see. Answering the shipping-country\n * ask also retires it, which is safe — that refusal is served before the\n * server takes the claim, so the first key was never spent.\n */\n const draftAttemptKey = useRef('')\n useEffect(() => {\n draftAttemptKey.current = ''\n }, [\n draft?.productId,\n draft?.variantId,\n draft?.quantity,\n draft?.email,\n draft?.shipTo,\n ])\n\n const handleDraftCreate = useCallback(async () => {\n if (!draft?.productId) return\n setDraft((prev) => (prev ? { ...prev, busy: true } : prev))\n try {\n if (!draftAttemptKey.current) {\n draftAttemptKey.current =\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now()}-${Math.random().toString(36).slice(2)}`\n }\n const response = await authorizedFetch(\n user,\n '/api/commerce/draft-order',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n // Stable across a retry of THIS attempt (AGL-1697), so a\n // double-click cannot mint two live payment links.\n 'Idempotency-Key': draftAttemptKey.current,\n },\n body: JSON.stringify({\n hostId,\n productId: draft.productId,\n variantId: draft.variantId || undefined,\n quantity: Number(draft.quantity) || 1,\n email: draft.email || undefined,\n // A request, never an instruction: the server resolves the\n // rates for this country AND restricts the payment link's\n // collectable addresses to it, so declaring one cannot buy a\n // cheaper zone's rate than the address the buyer then enters\n // (AGL-1721).\n ...(draft.shipTo ? { shippingCountry: draft.shipTo } : {}),\n }),\n },\n )\n const payload = await response.json()\n if (!response.ok) {\n // The merchant's rates differ by destination, so the server will not\n // price this order until it knows one (AGL-1792). Reveal the field and\n // let them answer; a store whose rates are the same everywhere, or\n // which configured none, never sends this and never shows it.\n if (payload?.needsShippingCountry) {\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n shipCountries: (\n payload.shippingCountries as string[] | undefined\n )?.length\n ? (payload.shippingCountries as string[])\n : [...CommerceModel.CHECKOUT_SHIPPING_COUNTRIES],\n }\n : prev,\n )\n }\n return void enqueueSnackbar(payload?.error ?? 'Draft order failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n await navigator.clipboard.writeText(payload.url).catch(() => undefined)\n enqueueSnackbar('Draft created — payment link copied', {\n variant: 'success',\n persist: false,\n })\n setDraft(null)\n } finally {\n setDraft((prev) => (prev ? { ...prev, busy: false } : prev))\n }\n }, [draft, user, hostId, enqueueSnackbar])\n\n const selectedOrder =\n loadedOrders.find((order: any) => order.$id === selectedId) ??\n (seededOrder && seededOrder.$id === selectedId ? seededOrder : null)\n\n /**\n * Shared by the two triggers (AGL-1805) so the empty state and the toolbar\n * cannot drift into opening the dialog with different starting fields.\n */\n const openDraft = useCallback(\n () => setDraft({ productId: '', variantId: '', quantity: '1', email: '' }),\n [],\n )\n\n /** What the draft dialog can actually offer. */\n const selectableProducts = useMemo(\n () => productWindow.rows.filter((product: any) => !product.deletedAt),\n [productWindow],\n )\n\n /*\n * The six columns `/product/commerce` advertises, in the order the mockup\n * shows them — every fact scannable on its own, Channel included.\n */\n const orderColumns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'orderLabel',\n headerName: 'Order',\n flex: 1,\n minWidth: 160,\n renderCell: ({ row }: any) => {\n const extraItems = Math.max(0, (row.lineItems?.length ?? 0) - 1)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.orderLabel}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {extraItems ? `${row.itemName} +${extraItems} more` : row.itemName}\n </Typography>\n </Stack>\n )\n },\n },\n {\n field: 'customerEmail',\n headerName: 'Customer',\n flex: 1,\n minWidth: 180,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\" noWrap>\n {row.customerEmail || '—'}\n </Typography>\n ),\n },\n {\n field: 'channelKey',\n headerName: 'Channel',\n width: 120,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\">\n {CommerceModel.orderChannelLabel(row.channelKey)}\n </Typography>\n ),\n },\n {\n field: 'netCents',\n headerName: 'Total',\n type: 'number',\n width: 150,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: any) => (\n <Stack sx={{ alignItems: 'flex-end', minWidth: 0, width: 1 }}>\n <Typography variant=\"body2\">\n {`$${(row.netCents / 100).toFixed(2)}`}\n </Typography>\n {row.refundedCents ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`$${((row.totals?.totalCents ?? row.amountCents ?? 0) / 100).toFixed(2)} less refunds`}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'statusKey',\n headerName: 'Status',\n width: 230,\n renderCell: ({ row }: any) => {\n // A lost chargeback leaves `status: 'refunded'` (AGL-1787), so the\n // status pill cannot tell the two apart either — which is why the\n // dispute chip sits beside it rather than being folded into it.\n const dispute: ReturnType<typeof CommerceModel.describeOrderDispute> =\n row.disputeBadge\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <Chip\n label={\n CommerceModel.ORDER_STATUS_LABELS[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? row.statusKey\n }\n size=\"small\"\n color={\n CommerceModel.ORDER_STATUS_COLOR[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? 'default'\n }\n variant=\"outlined\"\n />\n {dispute ? (\n <Tooltip title={dispute.detail}>\n <Chip\n label={dispute.label}\n size=\"small\"\n color={DISPUTE_COLOR[dispute.tone]}\n variant=\"filled\"\n />\n </Tooltip>\n ) : null}\n </Stack>\n {/*\n The deadline on the row itself, while the case is open — the\n one fact on this screen that expires.\n */}\n {dispute?.evidenceDaysLeft !== undefined ? (\n <Typography\n variant=\"caption\"\n component=\"div\"\n color={dispute.evidenceDaysLeft < 0 ? 'error' : 'warning.main'}\n >\n {dispute.evidenceDaysLeft < 0\n ? 'Evidence deadline passed'\n : `Evidence due in ${dispute.evidenceDaysLeft} day${\n dispute.evidenceDaysLeft === 1 ? '' : 's'\n }`}\n </Typography>\n ) : null}\n </Stack>\n )\n },\n },\n {\n field: 'createdAtMs',\n headerName: 'Date',\n width: 130,\n renderCell: ({ row }: any) => {\n const createdAt =\n row.createdAt?.toDate?.() ??\n (row.createdAtMs ? new Date(row.createdAtMs) : null)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {createdAt ? createdAt.toLocaleDateString() : '—'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {createdAt ? createdAt.toLocaleTimeString() : ''}\n </Typography>\n </Stack>\n )\n },\n },\n ],\n [],\n )\n\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n orderColumns,\n ORDER_LIST_FIELDS,\n filterOptions,\n ORDER_LIST_HEADERS,\n ),\n [orderColumns, filterOptions],\n )\n\n /*\n * The store is empty only when an UNFILTERED first page came back empty; a\n * filter or search that matches nothing keeps the grid, its chips and its\n * panel, so the reader can take the filter back off.\n */\n const empty =\n listStatus === 'success' && !filtering && page === 0 && orders.length === 0\n\n return (\n <CardDisplay\n header={'Orders'}\n help={pluginDocsHelp('commerce', {\n anchor: '#orders-screen',\n excerpt:\n 'Every order with its channel, status and net total. A refunded ' +\n 'order shows what is left, with the gross beneath it.',\n })}\n contentGutterX\n contentGutterY\n >\n {empty ? (\n /*\n * An invitation, not a report (AGL-1805). The \"Draft order\" button\n * used to live in the other arm of this ternary, so the one state a\n * draft order exists for — no sales yet, invoice the customer you\n * already have — was the only state that could not reach it, and\n * every other route into the dialog needs an order to exist first.\n *\n * The filters stay behind deliberately: they belong to a list with\n * rows in it. Export CSV likewise — a header-only file is not\n * something a merchant on day one is looking for.\n */\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'No orders yet. Storefront sales, POS sales and draft orders ' +\n 'all appear here as they come in.'\n }\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Selling to someone directly? Draft their order and send them ' +\n 'a payment link.'\n }\n </Typography>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n ) : (\n <Stack spacing={1}>\n {showStats ? (\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n >\n <CommerceStatTile\n label=\"Revenue · 30d\"\n value={`$${(summary.revenueCents / 100).toFixed(2)}`}\n deltaPct={summary.revenueDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Orders · 30d\"\n value={String(summary.orders)}\n deltaPct={summary.ordersDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Avg order value\"\n value={`$${(summary.aovCents / 100).toFixed(2)}`}\n deltaPct={summary.aovDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n </Stack>\n ) : null}\n {showStats && statsWindow.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Figures count the ${STATS_ORDER_CEILING} most recent orders of the last 60 days.`}\n </Typography>\n ) : null}\n {openDisputes.count > 0 ? (\n <Alert\n severity={openDisputes.overdue ? 'error' : 'warning'}\n action={\n showingOpenDisputes ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() =>\n setClauses(\n upsertListFilterClause(\n clauses,\n OPEN_DISPUTE_CLAUSE.field,\n OPEN_DISPUTE_CLAUSE,\n ),\n )\n }\n >\n {'Show them'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {moreDisputes\n ? `More than ${OPEN_DISPUTE_CEILING} charges are disputed with the shopper’s bank`\n : openDisputes.count === 1\n ? 'A shopper has disputed a charge with their bank'\n : `${openDisputes.count} charges are disputed with the shopper’s bank`}\n </AlertTitle>\n {openDisputes.soonestDaysLeft === undefined\n ? 'Answer in the Stripe dashboard while the case is open — an unanswered dispute is decided for the shopper.'\n : openDisputes.soonestDaysLeft < 0\n ? 'The evidence deadline has passed on at least one of these. Stripe decides an unanswered dispute for the shopper.'\n : `Evidence is due to Stripe in ${openDisputes.soonestDaysLeft} day${\n openDisputes.soonestDaysLeft === 1 ? '' : 's'\n } on the tightest of these. An unanswered dispute is decided for the shopper.`}\n </Alert>\n ) : null}\n {/*\n The buttons stay beside the grid: Export CSV writes the model's\n own order columns (`buildOrdersCsv`) for every order the query\n matches, which the grid's own export — one page — cannot.\n */}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', justifyContent: 'flex-end' }}\n >\n <Button\n size=\"small\"\n onClick={() => void handleExportCsv()}\n disabled={exporting}\n >\n {'Export CSV'}\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n <ListFilterChips\n fields={ORDER_LIST_FIELDS}\n headers={ORDER_LIST_HEADERS}\n clauses={clauses}\n onChange={setClauses}\n options={filterOptions}\n />\n <ListQueryNotices refused={refused} notices={plan.notices} />\n <ListTable\n aria-label=\"Orders\"\n rows={orderRows}\n columns={columns}\n onOpen={(id) => setSelectedId(id)}\n loading={listStatus === 'loading'}\n /*\n * The grid must NOT also filter, search or sort: the query\n * answers all three, and the list has one order — newest first —\n * which the query already holds.\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n sortingMode=\"server\"\n disableColumnSorting\n // `ListPagination` below pages the query.\n hideFooter\n initialState={{\n columns: { columnVisibilityModel: ORDER_HIDDEN_COLUMNS },\n }}\n noRowsLabel=\"No orders match these filters\"\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={orderRows.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n )}\n {selectedOrder ? (\n <OrderDetailDialog\n hostId={hostId}\n order={selectedOrder}\n onClose={() => setSelectedId(null)}\n />\n ) : null}\n <Dialog\n open={Boolean(draft)}\n onClose={() => setDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Draft order'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Product\"\n value={draft?.productId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? { ...prev, productId: event.target.value, variantId: '' }\n : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n /*\n * The second dead end behind the first (AGL-1805): a store with\n * no orders may have no products either, and a draft order is\n * composed from one — so \"Create & copy link\" stays disabled. Say\n * why, rather than opening an empty menu onto nothing.\n */\n helperText={\n selectableProducts.length === 0\n ? 'Add a product to this store first — a draft order is built from one.'\n : undefined\n }\n >\n {selectableProducts.map((product: any) => (\n <MenuItem key={product.$id} value={product.$id}>\n {product.name ?? product.$id}\n </MenuItem>\n ))}\n </TextField>\n {(() => {\n const product = productWindow.rows.find(\n (item: any) => item.$id === draft?.productId,\n )\n const variants = product\n ? CommerceModel.liftLegacyProduct(product).variants\n : []\n return variants.length > 1 ? (\n <TextField\n label=\"Variant\"\n value={draft?.variantId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, variantId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n >\n {variants.map((variant) => (\n <MenuItem key={variant.id} value={variant.id}>\n {`${Object.values(variant.options ?? {}).join(' / ') || 'Default'} — $${variant.priceUsd}`}\n </MenuItem>\n ))}\n </TextField>\n ) : null\n })()}\n <TextField\n label=\"Quantity\"\n value={draft?.quantity ?? '1'}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n quantity: event.target.value.replace(/[^0-9]/g, ''),\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Buyer email (optional)\"\n value={draft?.email ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, email: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n {draft?.shipCountries?.length ? (\n <TextField\n label=\"Ships to\"\n value={draft?.shipTo ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, shipTo: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n helperText={\n 'This store’s shipping rates differ by destination, so the ' +\n 'payment link has to be priced for one.'\n }\n >\n {draft.shipCountries.map((code) => (\n <MenuItem key={code} value={code}>\n {CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !draft?.productId ||\n draft?.busy ||\n // Once asked, the answer is required: retrying without one is\n // refused again, so the button would only look broken.\n Boolean(draft?.shipCountries?.length && !draft?.shipTo)\n }\n onClick={handleDraftCreate}\n >\n {'Create & copy link'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostOrdersCard.displayName = 'HostOrdersCard'\n\nexport default HostOrdersCard\n"],"names":["CommerceModel","CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","hiddenFilterVisibility","listFilterGridColumns","upsertListFilterClause","useListGridFilter","useSnackbar","checkEntitlement","pluginDocsHelp","ORDERS_CUSTOMER_PARAM","ORDERS_ORDER_PARAM","Alert","AlertTitle","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Tooltip","Typography","collection","doc","getDoc","getDocs","limit","orderBy","query","startAfter","where","useSearchParams","useCallback","useEffect","useMemo","useRef","useState","ceilingedWindow","collectionCeiling","useFirestore","useOrgPlan","useUser","useFirestoreCollection","listQueryConstraints","useListQuery","authorizedFetch","OPEN_DISPUTE_CLAUSE","ORDER_CHANNEL_OPTIONS","ORDER_DISPUTE_OPTIONS","ORDER_LIST_FIELDS","ORDER_LIST_HEADERS","ORDER_LIST_QUERY","ORDER_LIST_SELECT_FIELDS","ORDER_STATUS_OPTIONS","ordersCustomerClause","CommerceStatTile","OrderDetailDialog","DISPUTE_COLOR","PRODUCT_NAME_WINDOW","OPEN_DISPUTE_CEILING","STATS_WINDOW_MS","STATS_ORDER_CEILING","ORDERS_EXPORT_CEILING","ORDERS_EXPORT_PAGE","ORDER_VISIBLE_COLUMNS","ORDER_HIDDEN_COLUMNS","HostOrdersCard","props","loadedOrders","draft","hostId","firestore","ordersRef","data","productDocs","idField","productWindow","undefined","productNames","map","product","rows","$id","name","org","ready","orgReady","showStats","searchParams","clauses","setClauses","customer","get","gridFilter","onChange","selectFields","filtering","length","searchWords","some","word","trim","disputedRows","hasMore","moreDisputes","declaration","request","deps","pageSize","confirmDisappearances","orders","page","setPage","setPageSize","plan","status","listStatus","search","selectedId","setSelectedId","seededOrderId","seededOrder","setSeededOrder","seededOpened","current","order","then","snapshot","id","exists","catch","setDraft","user","enqueueSnackbar","orderRows","lifted","liftLegacyOrder","orderLabel","formatOrderNumber","itemName","lineItems","productId","statusKey","channelKey","channel","netCents","orderNetCents","disputeBadge","describeOrderDispute","productOptions","value","String","label","filterOptions","productIds","disputeKey","refused","fields","headers","options","showingOpenDisputes","clause","field","op","openDisputes","summariseOpenDisputes","statsSince","Date","now","statsDocs","statsWindow","summary","summarizeOrderWindow","nowMs","exporting","setExporting","handleExportCsv","matched","cursor","more","docs","take","Math","min","slice","entry","push","csv","buildOrdersCsv","url","URL","createObjectURL","Blob","type","anchor","document","createElement","href","download","click","revokeObjectURL","toLocaleString","variant","allowDuplicate","error","console","draftAttemptKey","variantId","quantity","email","shipTo","handleDraftCreate","prev","busy","globalThis","crypto","randomUUID","random","toString","response","method","body","JSON","stringify","Number","shippingCountry","payload","json","ok","needsShippingCountry","shipCountries","shippingCountries","CHECKOUT_SHIPPING_COUNTRIES","navigator","clipboard","writeText","persist","selectedOrder","find","openDraft","selectableProducts","filter","deletedAt","orderColumns","headerName","flex","minWidth","renderCell","row","extraItems","max","sx","noWrap","color","customerEmail","width","orderChannelLabel","align","headerAlign","alignItems","toFixed","refundedCents","totals","totalCents","amountCents","dispute","direction","spacing","ORDER_STATUS_LABELS","size","ORDER_STATUS_COLOR","title","detail","tone","evidenceDaysLeft","component","createdAt","toDate","createdAtMs","toLocaleDateString","toLocaleTimeString","columns","empty","header","help","excerpt","contentGutterX","contentGutterY","onClick","flexWrap","rowGap","revenueCents","deltaPct","revenueDeltaPct","deltaCaption","ordersDeltaPct","aovCents","aovDeltaPct","truncated","count","severity","overdue","action","soonestDaysLeft","justifyContent","disabled","notices","aria-label","onOpen","loading","filterMode","filterModel","onFilterModelChange","quickFilter","sortingMode","disableColumnSorting","hideFooter","initialState","columnVisibilityModel","noRowsLabel","rowCount","onPageChange","onPageSizeChange","onClose","open","Boolean","maxWidth","fullWidth","display","flexDirection","gap","event","target","select","mt","helperText","item","variants","liftLegacyProduct","Object","values","join","priceUsd","replace","slotProps","htmlInput","inputMode","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/host-orders-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\n'use client'\n\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListQueryNotices,\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { hiddenFilterVisibility } from '@aglyn/shared-ui-jsx/const/list-filter'\nimport {\n type ListFilterClause,\n listFilterGridColumns,\n upsertListFilterClause,\n} from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { checkEntitlement, pluginDocsHelp } from '@aglyn/aglyn'\nimport { ORDERS_CUSTOMER_PARAM, ORDERS_ORDER_PARAM } from '../../model/commerce-record-routes'\nimport {\n Alert,\n AlertTitle,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n MenuItem,\n Stack,\n TextField,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n doc,\n getDoc,\n limit,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useSearchParams } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport {\n ceilingedWindow,\n collectionCeiling,\n useFirestore,\n useOrgPlan,\n useUser,\n} from '@aglyn/tenant-feature-instance'\nimport { useFirestoreCollection } from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { useTransferLauncher } from '@aglyn/aglyn/app-utils/transfer-launcher-context'\nimport { commerceListFilter } from '../../transfer/list-filter'\nimport { COMMERCE_ORDERS_TRANSFER } from '../../transfer/transfer-keys'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\n\nimport {\n OPEN_DISPUTE_CLAUSE,\n ORDER_CHANNEL_OPTIONS,\n ORDER_DISPUTE_OPTIONS,\n ORDER_LIST_FIELDS,\n ORDER_LIST_HEADERS,\n ORDER_LIST_QUERY,\n ORDER_LIST_SELECT_FIELDS,\n ORDER_STATUS_OPTIONS,\n ordersCustomerClause,\n} from '../../constants/orders-list-query'\nimport CommerceStatTile from './commerce-stat-tile.component'\nimport OrderDetailDialog, {\n DISPUTE_COLOR,\n} from './order-detail-dialog.component'\n\n/**\n * The products the card reads to NAME a legacy row and the CSV, to offer the\n * Product filter's choices and to fill the draft dialog's picker. A picker's\n * reach, not the list's: the orders themselves are paged by their query and\n * the Product filter asks it for any product id, named here or not.\n */\nconst PRODUCT_NAME_WINDOW = 100\n\n/**\n * How many open disputes the banner reads. Its own query, `disputeKey ==\n * 'open'`, so a deadline is raised whatever the list is filtered to and\n * however deep in the store the order sits; a store past this many open\n * cases is told \"more than\".\n */\nconst OPEN_DISPUTE_CEILING = 50\n\n/**\n * The money tiles read the last sixty days (thirty, and the thirty before it)\n * on their own bounded query. They are analytics over a period, not a filter\n * over the list, and share the analytics card's bound.\n */\nconst STATS_WINDOW_MS = 60 * 24 * 60 * 60 * 1000\nconst STATS_ORDER_CEILING = 500\n\n/** The columns the grid draws; every other declared field is a hidden filter column. */\nconst ORDER_VISIBLE_COLUMNS = [\n 'orderLabel',\n 'customerEmail',\n 'channelKey',\n 'statusKey',\n 'createdAtMs',\n]\nconst ORDER_HIDDEN_COLUMNS = hiddenFilterVisibility(\n ORDER_LIST_FIELDS,\n ORDER_VISIBLE_COLUMNS,\n)\n\nexport interface HostOrdersCardProps {\n hostId: string\n}\n\n/**\n * Orders console (AGL-287): filterable list over webhook-written order\n * docs with a detail dialog (timeline, fulfill, refund, cancel, notes,\n * packing slip) and draft orders that send the buyer a payment link.\n *\n * ## Every filter and the search are on the query (AGL-3321)\n *\n * The list used to read the newest two hundred orders and match its Filters\n * panel and search over them, so a Status filter on a busy store answered\n * \"none\" about every order past the two hundredth. Now each clause and the\n * search word are predicates on ONE Firestore query (`ORDER_LIST_QUERY`),\n * over the fields every writer stamps (`orderListFields`), and the footer\n * pages that query's answer. A combination one query cannot hold is refused\n * by name above the grid and never applied to some rows only.\n */\nexport function HostOrdersCard(props: HostOrdersCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const ordersRef = useMemo(\n () => collection(firestore, 'hosts', hostId, 'orders'),\n [firestore, hostId],\n )\n const { data: productDocs } = useFirestoreCollection<any>(\n () =>\n collectionCeiling(\n collection(firestore, 'hosts', hostId, 'products'),\n PRODUCT_NAME_WINDOW,\n ),\n [firestore, hostId],\n { idField: '$id' },\n )\n const productWindow = useMemo(\n () => ceilingedWindow<any>(productDocs ?? undefined, PRODUCT_NAME_WINDOW),\n [productDocs],\n )\n const productNames = useMemo(() => {\n const map: Record<string, string> = {}\n for (const product of productWindow.rows) {\n map[product.$id] = product.name ?? product.$id\n }\n return map\n }, [productWindow])\n\n /**\n * The money tiles are the `commerceAnalytics` surface (AGL-1938,\n * AGL-2056), and this is the third place they appear. Rendering them\n * ungated here would undo both of those passes and hand a Starter org\n * the Pro figures one tab away from the upgrade prompt that refuses\n * them. The TABLE below is not gated — a list of your own orders is not\n * a paid feature.\n */\n const { org, ready: orgReady } = useOrgPlan(hostId)\n const showStats =\n orgReady && checkEntitlement(org as never, 'commerceAnalytics')\n\n /*\n * The grid's clauses (AGL-96, AGL-3317), held here so two things outside\n * the grid can set one: the CRM's contact page links with the buyer's\n * address to answer \"what has this customer ordered\" (AGL-2622) — a whole\n * address is that buyer, a domain every buyer at a company\n * (`ordersCustomerClause`) — and the dispute banner's \"Show them\".\n */\n const searchParams = useSearchParams()\n const [clauses, setClauses] = useState<ListFilterClause[]>(() => {\n const customer = ordersCustomerClause(\n searchParams?.get(ORDERS_CUSTOMER_PARAM) ?? '',\n )\n return customer ? [customer] : []\n })\n const gridFilter = useListGridFilter({\n clauses,\n onChange: setClauses,\n selectFields: ORDER_LIST_SELECT_FIELDS,\n })\n const filtering =\n clauses.length > 0 || gridFilter.searchWords.some((word) => word.trim())\n\n /*\n * The open disputes, on their OWN query (AGL-1796). Raised whatever the\n * list is filtered to — a merchant filtered to \"delivered\" still has a\n * deadline running on a `paid` order, and the evidence window is days —\n * and however far back the order is. Asked before the list so the list's\n * plan is the one a spec reads last.\n */\n const {\n rows: disputedRows,\n hasMore: moreDisputes,\n } = useListQuery<any>({\n collection: ordersRef,\n declaration: ORDER_LIST_QUERY,\n request: { clauses: [OPEN_DISPUTE_CLAUSE] },\n deps: [firestore, hostId],\n idField: '$id',\n pageSize: OPEN_DISPUTE_CEILING,\n // A dispute that closes leaves this query; confirm it before believing it.\n confirmDisappearances: true,\n })\n\n /*\n * THE LIST: every clause and the search word on one query, newest first,\n * paged by the footer. A status, a dispute or a paid draft's address can\n * move an order out of the filtered query mid-session, so a disappearance\n * is confirmed against the server before it is believed (AGL-1196).\n */\n const {\n rows: orders,\n data: loadedOrders,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n status: listStatus,\n } = useListQuery<any>({\n collection: ordersRef,\n declaration: ORDER_LIST_QUERY,\n request: { clauses, search: gridFilter.searchWords },\n deps: [firestore, hostId],\n idField: '$id',\n confirmDisappearances: true,\n })\n\n const [selectedId, setSelectedId] = useState<string | null>(null)\n /*\n * An order named in the URL opens in its dialog on arrival (AGL-2622). A\n * contact's timeline names the order that made the person a customer,\n * and the address it links to is this list with `?order={id}`. The list\n * shows one page, and an order from last year is not on it, so the\n * document is read once by id and held beside the page for the dialog; an\n * order that IS loaded is found there first and the read is skipped. Once\n * per id — the ref keeps a re-render from reopening a dialog the merchant\n * has since closed — and the landing read is judged against the ref\n * rather than an effect cleanup, because the `setSelectedId` above\n * re-renders before the read lands and a cleanup keyed on any dep would\n * cancel the answer to the question just asked.\n */\n const seededOrderId = searchParams?.get(ORDERS_ORDER_PARAM) ?? null\n const [seededOrder, setSeededOrder] = useState<any | null>(null)\n const seededOpened = useRef<string | null>(null)\n useEffect(() => {\n if (!seededOrderId || seededOpened.current === seededOrderId) return\n if (listStatus === 'loading') return\n seededOpened.current = seededOrderId\n setSelectedId(seededOrderId)\n if (loadedOrders.some((order: any) => order.$id === seededOrderId)) return\n void getDoc(doc(firestore, 'hosts', hostId, 'orders', seededOrderId))\n .then((snapshot) => {\n if (seededOpened.current !== snapshot.id || !snapshot.exists()) return\n setSeededOrder({ ...snapshot.data(), $id: snapshot.id })\n })\n .catch(() => undefined)\n }, [seededOrderId, loadedOrders, listStatus, firestore, hostId])\n const [draft, setDraft] = useState<{\n productId: string\n variantId: string\n quantity: string\n email: string\n /** Destination the merchant declared, once they have been asked. */\n shipTo?: string\n /**\n * Revealed only when the server refuses for want of a destination\n * (AGL-1792). A merchant whose rates are the same everywhere — or who\n * configured none — is never asked and never sees this field.\n */\n shipCountries?: string[]\n busy?: boolean\n } | null>(null)\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n /**\n * Each order on the page with the values the grid DRAWS. What it filters\n * by is on the query; this is presentation only. `liftLegacyOrder` names\n * a status and channel on a row that predates them, and the item name\n * reads the line items first (AGL-1747): the flat `productId` is written\n * only by the two buy-now Stripe paths.\n */\n const orderRows = useMemo(\n () =>\n orders.map((order: any) => {\n const lifted = CommerceModel.liftLegacyOrder(order)\n return {\n ...order,\n orderLabel: CommerceModel.formatOrderNumber(lifted, order.$id),\n itemName:\n lifted.lineItems?.[0]?.name ??\n productNames[order.productId] ??\n order.productId ??\n '',\n statusKey: lifted.status,\n channelKey: lifted.channel ?? 'online',\n netCents: CommerceModel.orderNetCents(lifted),\n disputeBadge: CommerceModel.describeOrderDispute(lifted),\n }\n }),\n [orders, productNames],\n )\n const productOptions = useMemo(\n () =>\n productWindow.rows.map((product: any) => ({\n value: String(product.$id),\n label: String(product.name ?? product.$id),\n })),\n [productWindow],\n )\n const filterOptions = useMemo(\n () => ({\n statusKey: ORDER_STATUS_OPTIONS,\n channelKey: ORDER_CHANNEL_OPTIONS,\n productIds: productOptions,\n disputeKey: ORDER_DISPUTE_OPTIONS,\n }),\n [productOptions],\n )\n const refused = useMemo(\n () =>\n listQueryRefusals(plan.refused, {\n fields: ORDER_LIST_FIELDS,\n headers: ORDER_LIST_HEADERS,\n options: filterOptions,\n }),\n [plan, filterOptions],\n )\n const showingOpenDisputes = clauses.some(\n (clause) =>\n clause.field === OPEN_DISPUTE_CLAUSE.field &&\n clause.op === OPEN_DISPUTE_CLAUSE.op &&\n clause.value === OPEN_DISPUTE_CLAUSE.value,\n )\n\n const openDisputes = useMemo(\n () =>\n CommerceModel.summariseOpenDisputes(\n disputedRows.map((order: any) => CommerceModel.liftLegacyOrder(order)),\n ),\n [disputedRows],\n )\n\n /*\n * 30-day money summary with the prior 30 days behind it (AGL-2136).\n * ANALYTICS, NOT A FILTER: the tiles describe a period, not the list, so\n * they keep their own bounded read — sixty days by `createdAtMs`, the\n * newest `STATS_ORDER_CEILING` of them, read only when the tiles show.\n * Range and order are the same field, so it needs no composite.\n */\n const statsSince = useMemo(() => Date.now() - STATS_WINDOW_MS, [])\n const { data: statsDocs } = useFirestoreCollection<any>(\n () =>\n showStats\n ? query(\n ordersRef,\n where('createdAtMs', '>=', statsSince),\n orderBy('createdAtMs', 'desc'),\n limit(STATS_ORDER_CEILING + 1),\n )\n : null,\n [firestore, hostId, showStats, statsSince],\n { idField: '$id' },\n )\n const statsWindow = useMemo(\n () => ceilingedWindow<any>(statsDocs ?? undefined, STATS_ORDER_CEILING),\n [statsDocs],\n )\n const summary = useMemo(\n () =>\n CommerceModel.summarizeOrderWindow(statsWindow.rows, { nowMs: Date.now() }),\n [statsWindow],\n )\n\n /**\n * Export (AGL-3531) opens the console's export dialog on the\n * `commerce.orders` resource: the fields the person picks, for what the\n * QUERY matches — every clause and the search the grid is showing, not the\n * page on screen — read and streamed on the server however many orders\n * match. Orders are exported only; there is no Import.\n */\n const transfer = useTransferLauncher()\n const openExport = useCallback(() => {\n transfer?.openExport({\n resource: COMMERCE_ORDERS_TRANSFER,\n scope: 'host',\n hostId,\n ...(filtering\n ? {\n filter: {\n label: 'the orders the list’s filters and search find',\n value: commerceListFilter(plan),\n },\n }\n : {}),\n })\n }, [transfer, hostId, filtering, plan])\n\n /**\n * Idempotency key for ONE draft attempt (AGL-1697).\n *\n * Minted lazily on the first create click, NOT per call — two clicks (or a\n * click retried after a lost response) must present ONE key so the server\n * replays the original payment link instead of minting a second order.\n * Retired whenever a content field changes: an edited draft is a different\n * order, and replaying the old one against it would hand the merchant a\n * link priced for fields they no longer see. Answering the shipping-country\n * ask also retires it, which is safe — that refusal is served before the\n * server takes the claim, so the first key was never spent.\n */\n const draftAttemptKey = useRef('')\n useEffect(() => {\n draftAttemptKey.current = ''\n }, [\n draft?.productId,\n draft?.variantId,\n draft?.quantity,\n draft?.email,\n draft?.shipTo,\n ])\n\n const handleDraftCreate = useCallback(async () => {\n if (!draft?.productId) return\n setDraft((prev) => (prev ? { ...prev, busy: true } : prev))\n try {\n if (!draftAttemptKey.current) {\n draftAttemptKey.current =\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now()}-${Math.random().toString(36).slice(2)}`\n }\n const response = await authorizedFetch(\n user,\n '/api/commerce/draft-order',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n // Stable across a retry of THIS attempt (AGL-1697), so a\n // double-click cannot mint two live payment links.\n 'Idempotency-Key': draftAttemptKey.current,\n },\n body: JSON.stringify({\n hostId,\n productId: draft.productId,\n variantId: draft.variantId || undefined,\n quantity: Number(draft.quantity) || 1,\n email: draft.email || undefined,\n // A request, never an instruction: the server resolves the\n // rates for this country AND restricts the payment link's\n // collectable addresses to it, so declaring one cannot buy a\n // cheaper zone's rate than the address the buyer then enters\n // (AGL-1721).\n ...(draft.shipTo ? { shippingCountry: draft.shipTo } : {}),\n }),\n },\n )\n const payload = await response.json()\n if (!response.ok) {\n // The merchant's rates differ by destination, so the server will not\n // price this order until it knows one (AGL-1792). Reveal the field and\n // let them answer; a store whose rates are the same everywhere, or\n // which configured none, never sends this and never shows it.\n if (payload?.needsShippingCountry) {\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n shipCountries: (\n payload.shippingCountries as string[] | undefined\n )?.length\n ? (payload.shippingCountries as string[])\n : [...CommerceModel.CHECKOUT_SHIPPING_COUNTRIES],\n }\n : prev,\n )\n }\n return void enqueueSnackbar(payload?.error ?? 'Draft order failed', {\n variant: 'error',\n allowDuplicate: true,\n })\n }\n await navigator.clipboard.writeText(payload.url).catch(() => undefined)\n enqueueSnackbar('Draft created — payment link copied', {\n variant: 'success',\n persist: false,\n })\n setDraft(null)\n } finally {\n setDraft((prev) => (prev ? { ...prev, busy: false } : prev))\n }\n }, [draft, user, hostId, enqueueSnackbar])\n\n const selectedOrder =\n loadedOrders.find((order: any) => order.$id === selectedId) ??\n (seededOrder && seededOrder.$id === selectedId ? seededOrder : null)\n\n /**\n * Shared by the two triggers (AGL-1805) so the empty state and the toolbar\n * cannot drift into opening the dialog with different starting fields.\n */\n const openDraft = useCallback(\n () => setDraft({ productId: '', variantId: '', quantity: '1', email: '' }),\n [],\n )\n\n /** What the draft dialog can actually offer. */\n const selectableProducts = useMemo(\n () => productWindow.rows.filter((product: any) => !product.deletedAt),\n [productWindow],\n )\n\n /*\n * The six columns `/product/commerce` advertises, in the order the mockup\n * shows them — every fact scannable on its own, Channel included.\n */\n const orderColumns = useMemo<GridColDef[]>(\n () => [\n {\n field: 'orderLabel',\n headerName: 'Order',\n flex: 1,\n minWidth: 160,\n renderCell: ({ row }: any) => {\n const extraItems = Math.max(0, (row.lineItems?.length ?? 0) - 1)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {row.orderLabel}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {extraItems ? `${row.itemName} +${extraItems} more` : row.itemName}\n </Typography>\n </Stack>\n )\n },\n },\n {\n field: 'customerEmail',\n headerName: 'Customer',\n flex: 1,\n minWidth: 180,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\" noWrap>\n {row.customerEmail || '—'}\n </Typography>\n ),\n },\n {\n field: 'channelKey',\n headerName: 'Channel',\n width: 120,\n renderCell: ({ row }: any) => (\n <Typography variant=\"body2\">\n {CommerceModel.orderChannelLabel(row.channelKey)}\n </Typography>\n ),\n },\n {\n field: 'netCents',\n headerName: 'Total',\n type: 'number',\n width: 150,\n align: 'right',\n headerAlign: 'right',\n renderCell: ({ row }: any) => (\n <Stack sx={{ alignItems: 'flex-end', minWidth: 0, width: 1 }}>\n <Typography variant=\"body2\">\n {`$${(row.netCents / 100).toFixed(2)}`}\n </Typography>\n {row.refundedCents ? (\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`$${((row.totals?.totalCents ?? row.amountCents ?? 0) / 100).toFixed(2)} less refunds`}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'statusKey',\n headerName: 'Status',\n width: 230,\n renderCell: ({ row }: any) => {\n // A lost chargeback leaves `status: 'refunded'` (AGL-1787), so the\n // status pill cannot tell the two apart either — which is why the\n // dispute chip sits beside it rather than being folded into it.\n const dispute: ReturnType<typeof CommerceModel.describeOrderDispute> =\n row.disputeBadge\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Stack direction=\"row\" spacing={0.5} sx={{ alignItems: 'center' }}>\n <Chip\n label={\n CommerceModel.ORDER_STATUS_LABELS[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? row.statusKey\n }\n size=\"small\"\n color={\n CommerceModel.ORDER_STATUS_COLOR[\n row.statusKey as CommerceModel.OrderStatus\n ] ?? 'default'\n }\n variant=\"outlined\"\n />\n {dispute ? (\n <Tooltip title={dispute.detail}>\n <Chip\n label={dispute.label}\n size=\"small\"\n color={DISPUTE_COLOR[dispute.tone]}\n variant=\"filled\"\n />\n </Tooltip>\n ) : null}\n </Stack>\n {/*\n The deadline on the row itself, while the case is open — the\n one fact on this screen that expires.\n */}\n {dispute?.evidenceDaysLeft !== undefined ? (\n <Typography\n variant=\"caption\"\n component=\"div\"\n color={dispute.evidenceDaysLeft < 0 ? 'error' : 'warning.main'}\n >\n {dispute.evidenceDaysLeft < 0\n ? 'Evidence deadline passed'\n : `Evidence due in ${dispute.evidenceDaysLeft} day${\n dispute.evidenceDaysLeft === 1 ? '' : 's'\n }`}\n </Typography>\n ) : null}\n </Stack>\n )\n },\n },\n {\n field: 'createdAtMs',\n headerName: 'Date',\n width: 130,\n renderCell: ({ row }: any) => {\n const createdAt =\n row.createdAt?.toDate?.() ??\n (row.createdAtMs ? new Date(row.createdAtMs) : null)\n return (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap>\n {createdAt ? createdAt.toLocaleDateString() : '—'}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {createdAt ? createdAt.toLocaleTimeString() : ''}\n </Typography>\n </Stack>\n )\n },\n },\n ],\n [],\n )\n\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n orderColumns,\n ORDER_LIST_FIELDS,\n filterOptions,\n ORDER_LIST_HEADERS,\n ),\n [orderColumns, filterOptions],\n )\n\n /*\n * The store is empty only when an UNFILTERED first page came back empty; a\n * filter or search that matches nothing keeps the grid, its chips and its\n * panel, so the reader can take the filter back off.\n */\n const empty =\n listStatus === 'success' && !filtering && page === 0 && orders.length === 0\n\n return (\n <CardDisplay\n header={'Orders'}\n help={pluginDocsHelp('commerce', {\n anchor: '#orders-screen',\n excerpt:\n 'Every order with its channel, status and net total. A refunded ' +\n 'order shows what is left, with the gross beneath it.',\n })}\n contentGutterX\n contentGutterY\n >\n {empty ? (\n /*\n * An invitation, not a report (AGL-1805). The \"Draft order\" button\n * used to live in the other arm of this ternary, so the one state a\n * draft order exists for — no sales yet, invoice the customer you\n * already have — was the only state that could not reach it, and\n * every other route into the dialog needs an order to exist first.\n *\n * The filters stay behind deliberately: they belong to a list with\n * rows in it. Export orders likewise — a header-only file is not\n * something a merchant on day one is looking for.\n */\n <Stack spacing={1} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'No orders yet. Storefront sales, POS sales and draft orders ' +\n 'all appear here as they come in.'\n }\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Selling to someone directly? Draft their order and send them ' +\n 'a payment link.'\n }\n </Typography>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n ) : (\n <Stack spacing={1}>\n {showStats ? (\n <Stack\n direction=\"row\"\n spacing={3}\n sx={{ flexWrap: 'wrap', rowGap: 1 }}\n >\n <CommerceStatTile\n label=\"Revenue · 30d\"\n value={`$${(summary.revenueCents / 100).toFixed(2)}`}\n deltaPct={summary.revenueDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Orders · 30d\"\n value={String(summary.orders)}\n deltaPct={summary.ordersDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n <CommerceStatTile\n label=\"Avg order value\"\n value={`$${(summary.aovCents / 100).toFixed(2)}`}\n deltaPct={summary.aovDeltaPct}\n deltaCaption=\"vs the previous 30 days\"\n />\n </Stack>\n ) : null}\n {showStats && statsWindow.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Figures count the ${STATS_ORDER_CEILING} most recent orders of the last 60 days.`}\n </Typography>\n ) : null}\n {openDisputes.count > 0 ? (\n <Alert\n severity={openDisputes.overdue ? 'error' : 'warning'}\n action={\n showingOpenDisputes ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() =>\n setClauses(\n upsertListFilterClause(\n clauses,\n OPEN_DISPUTE_CLAUSE.field,\n OPEN_DISPUTE_CLAUSE,\n ),\n )\n }\n >\n {'Show them'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {moreDisputes\n ? `More than ${OPEN_DISPUTE_CEILING} charges are disputed with the shopper’s bank`\n : openDisputes.count === 1\n ? 'A shopper has disputed a charge with their bank'\n : `${openDisputes.count} charges are disputed with the shopper’s bank`}\n </AlertTitle>\n {openDisputes.soonestDaysLeft === undefined\n ? 'Answer in the Stripe dashboard while the case is open — an unanswered dispute is decided for the shopper.'\n : openDisputes.soonestDaysLeft < 0\n ? 'The evidence deadline has passed on at least one of these. Stripe decides an unanswered dispute for the shopper.'\n : `Evidence is due to Stripe in ${openDisputes.soonestDaysLeft} day${\n openDisputes.soonestDaysLeft === 1 ? '' : 's'\n } on the tightest of these. An unanswered dispute is decided for the shopper.`}\n </Alert>\n ) : null}\n {/*\n The buttons stay beside the grid: Export orders writes every order\n the query matches, with the fields the person picks, which the grid's\n own export — one page — cannot.\n */}\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center', justifyContent: 'flex-end' }}\n >\n {/* Only for whom the route takes it (AGL-3554). */}\n {transfer?.can('export', { resource: COMMERCE_ORDERS_TRANSFER, scope: 'host', hostId }) ? (\n <Button size=\"small\" onClick={openExport}>\n {'Export orders'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={openDraft}\n >\n {'Draft order'}\n </Button>\n </Stack>\n <ListFilterChips\n fields={ORDER_LIST_FIELDS}\n headers={ORDER_LIST_HEADERS}\n clauses={clauses}\n onChange={setClauses}\n options={filterOptions}\n />\n <ListQueryNotices refused={refused} notices={plan.notices} />\n <ListTable\n aria-label=\"Orders\"\n rows={orderRows}\n columns={columns}\n onOpen={(id) => setSelectedId(id)}\n loading={listStatus === 'loading'}\n /*\n * The grid must NOT also filter, search or sort: the query\n * answers all three, and the list has one order — newest first —\n * which the query already holds.\n */\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n sortingMode=\"server\"\n disableColumnSorting\n // `ListPagination` below pages the query.\n hideFooter\n initialState={{\n columns: { columnVisibilityModel: ORDER_HIDDEN_COLUMNS },\n }}\n noRowsLabel=\"No orders match these filters\"\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={orderRows.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n )}\n {selectedOrder ? (\n <OrderDetailDialog\n hostId={hostId}\n order={selectedOrder}\n onClose={() => setSelectedId(null)}\n />\n ) : null}\n <Dialog\n open={Boolean(draft)}\n onClose={() => setDraft(null)}\n maxWidth=\"xs\"\n fullWidth\n >\n <DialogTitle>{'Draft order'}</DialogTitle>\n <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <TextField\n label=\"Product\"\n value={draft?.productId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? { ...prev, productId: event.target.value, variantId: '' }\n : prev,\n )\n }\n size=\"small\"\n select\n sx={{ mt: 1 }}\n /*\n * The second dead end behind the first (AGL-1805): a store with\n * no orders may have no products either, and a draft order is\n * composed from one — so \"Create & copy link\" stays disabled. Say\n * why, rather than opening an empty menu onto nothing.\n */\n helperText={\n selectableProducts.length === 0\n ? 'Add a product to this store first — a draft order is built from one.'\n : undefined\n }\n >\n {selectableProducts.map((product: any) => (\n <MenuItem key={product.$id} value={product.$id}>\n {product.name ?? product.$id}\n </MenuItem>\n ))}\n </TextField>\n {(() => {\n const product = productWindow.rows.find(\n (item: any) => item.$id === draft?.productId,\n )\n const variants = product\n ? CommerceModel.liftLegacyProduct(product).variants\n : []\n return variants.length > 1 ? (\n <TextField\n label=\"Variant\"\n value={draft?.variantId ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, variantId: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n >\n {variants.map((variant) => (\n <MenuItem key={variant.id} value={variant.id}>\n {`${Object.values(variant.options ?? {}).join(' / ') || 'Default'} — $${variant.priceUsd}`}\n </MenuItem>\n ))}\n </TextField>\n ) : null\n })()}\n <TextField\n label=\"Quantity\"\n value={draft?.quantity ?? '1'}\n onChange={(event) =>\n setDraft((prev) =>\n prev\n ? {\n ...prev,\n quantity: event.target.value.replace(/[^0-9]/g, ''),\n }\n : prev,\n )\n }\n size=\"small\"\n slotProps={{ htmlInput: { inputMode: 'numeric' } }}\n />\n <TextField\n label=\"Buyer email (optional)\"\n value={draft?.email ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, email: event.target.value } : prev,\n )\n }\n size=\"small\"\n />\n {draft?.shipCountries?.length ? (\n <TextField\n label=\"Ships to\"\n value={draft?.shipTo ?? ''}\n onChange={(event) =>\n setDraft((prev) =>\n prev ? { ...prev, shipTo: event.target.value } : prev,\n )\n }\n size=\"small\"\n select\n helperText={\n 'This store’s shipping rates differ by destination, so the ' +\n 'payment link has to be priced for one.'\n }\n >\n {draft.shipCountries.map((code) => (\n <MenuItem key={code} value={code}>\n {CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code}\n </MenuItem>\n ))}\n </TextField>\n ) : null}\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setDraft(null)}>{'Cancel'}</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={\n !draft?.productId ||\n draft?.busy ||\n // Once asked, the answer is required: retrying without one is\n // refused again, so the button would only look broken.\n Boolean(draft?.shipCountries?.length && !draft?.shipTo)\n }\n onClick={handleDraftCreate}\n >\n {'Create & copy link'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nHostOrdersCard.displayName = 'HostOrdersCard'\n\nexport default HostOrdersCard\n"],"names":["CommerceModel","CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","hiddenFilterVisibility","listFilterGridColumns","upsertListFilterClause","useListGridFilter","useSnackbar","checkEntitlement","pluginDocsHelp","ORDERS_CUSTOMER_PARAM","ORDERS_ORDER_PARAM","Alert","AlertTitle","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","MenuItem","Stack","TextField","Tooltip","Typography","collection","doc","getDoc","limit","orderBy","query","where","useSearchParams","useCallback","useEffect","useMemo","useRef","useState","ceilingedWindow","collectionCeiling","useFirestore","useOrgPlan","useUser","useFirestoreCollection","useListQuery","useTransferLauncher","commerceListFilter","COMMERCE_ORDERS_TRANSFER","authorizedFetch","OPEN_DISPUTE_CLAUSE","ORDER_CHANNEL_OPTIONS","ORDER_DISPUTE_OPTIONS","ORDER_LIST_FIELDS","ORDER_LIST_HEADERS","ORDER_LIST_QUERY","ORDER_LIST_SELECT_FIELDS","ORDER_STATUS_OPTIONS","ordersCustomerClause","CommerceStatTile","OrderDetailDialog","DISPUTE_COLOR","PRODUCT_NAME_WINDOW","OPEN_DISPUTE_CEILING","STATS_WINDOW_MS","STATS_ORDER_CEILING","ORDER_VISIBLE_COLUMNS","ORDER_HIDDEN_COLUMNS","HostOrdersCard","props","loadedOrders","draft","hostId","firestore","ordersRef","data","productDocs","idField","productWindow","undefined","productNames","map","product","rows","$id","name","org","ready","orgReady","showStats","searchParams","clauses","setClauses","customer","get","gridFilter","onChange","selectFields","filtering","length","searchWords","some","word","trim","disputedRows","hasMore","moreDisputes","declaration","request","deps","pageSize","confirmDisappearances","orders","page","setPage","setPageSize","plan","status","listStatus","search","selectedId","setSelectedId","seededOrderId","seededOrder","setSeededOrder","seededOpened","current","order","then","snapshot","id","exists","catch","setDraft","user","enqueueSnackbar","orderRows","lifted","liftLegacyOrder","orderLabel","formatOrderNumber","itemName","lineItems","productId","statusKey","channelKey","channel","netCents","orderNetCents","disputeBadge","describeOrderDispute","productOptions","value","String","label","filterOptions","productIds","disputeKey","refused","fields","headers","options","showingOpenDisputes","clause","field","op","openDisputes","summariseOpenDisputes","statsSince","Date","now","statsDocs","statsWindow","summary","summarizeOrderWindow","nowMs","transfer","openExport","resource","scope","filter","draftAttemptKey","variantId","quantity","email","shipTo","handleDraftCreate","prev","busy","globalThis","crypto","randomUUID","Math","random","toString","slice","response","method","body","JSON","stringify","Number","shippingCountry","payload","json","ok","needsShippingCountry","shipCountries","shippingCountries","CHECKOUT_SHIPPING_COUNTRIES","error","variant","allowDuplicate","navigator","clipboard","writeText","url","persist","selectedOrder","find","openDraft","selectableProducts","deletedAt","orderColumns","headerName","flex","minWidth","renderCell","row","extraItems","max","sx","noWrap","color","customerEmail","width","orderChannelLabel","type","align","headerAlign","alignItems","toFixed","refundedCents","totals","totalCents","amountCents","dispute","direction","spacing","ORDER_STATUS_LABELS","size","ORDER_STATUS_COLOR","title","detail","tone","evidenceDaysLeft","component","createdAt","toDate","createdAtMs","toLocaleDateString","toLocaleTimeString","columns","empty","header","help","anchor","excerpt","contentGutterX","contentGutterY","onClick","flexWrap","rowGap","revenueCents","deltaPct","revenueDeltaPct","deltaCaption","ordersDeltaPct","aovCents","aovDeltaPct","truncated","count","severity","overdue","action","soonestDaysLeft","justifyContent","can","notices","aria-label","onOpen","loading","filterMode","filterModel","onFilterModelChange","quickFilter","sortingMode","disableColumnSorting","hideFooter","initialState","columnVisibilityModel","noRowsLabel","rowCount","onPageChange","onPageSizeChange","onClose","open","Boolean","maxWidth","fullWidth","display","flexDirection","gap","event","target","select","mt","helperText","item","variants","liftLegacyProduct","Object","values","join","priceUsd","replace","slotProps","htmlInput","inputMode","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","disabled","displayName"],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
18
|
* The zones this plugin hosts on its product surfaces (AGL-2916): the product
|
|
19
|
-
* editor, the products hub and the
|
|
19
|
+
* editor, the products hub and the import wizard's After import step.
|
|
20
20
|
*
|
|
21
21
|
* Each is drawn through the shell's renderer (`useConsoleWidgetSlot`), so a
|
|
22
22
|
* widget there passes the same enablement, entitlement and permission gates as
|
|
@@ -135,7 +135,7 @@ export interface ConsoleProductsHubZoneProps {
|
|
|
135
135
|
/** The catalog rows the hub holds, in its order. */
|
|
136
136
|
products: readonly ConsoleProductSummary[];
|
|
137
137
|
/**
|
|
138
|
-
* The products the hub's latest
|
|
138
|
+
* The products the hub's latest import created in this visit, and the
|
|
139
139
|
* options the `productImport` zone set for them; `null` before any import.
|
|
140
140
|
* `key` is new for each import.
|
|
141
141
|
*/
|
|
@@ -154,8 +154,8 @@ export interface ConsoleProductsHubZoneProps {
|
|
|
154
154
|
createDiscountDrafts: (discounts: readonly ConsoleProposedDiscount[]) => Promise<number>;
|
|
155
155
|
}
|
|
156
156
|
/**
|
|
157
|
-
* What the `productImport` zone hands each widget,
|
|
158
|
-
*
|
|
157
|
+
* What the `productImport` zone hands each widget, in the import wizard's
|
|
158
|
+
* After import step: how many products the import creates, and options a widget sets
|
|
159
159
|
* for what happens to them once they land. The hub hands the options, with
|
|
160
160
|
* the ids of the products the import created, to `productsHub` as its
|
|
161
161
|
* `lastImport`.
|
|
@@ -178,7 +178,8 @@ export declare const PRODUCT_IMPORT_ZONE: import("@aglyn/aglyn/plugin-manager/pl
|
|
|
178
178
|
* Declares the three zones, from the console registrar. The ids are the ones
|
|
179
179
|
* widgets have always registered under, so a widget written for them keeps
|
|
180
180
|
* working. Each is `bare`: the section is one item of a layout this plugin
|
|
181
|
-
* draws — among the editor's fields, above the catalog table,
|
|
182
|
-
* import
|
|
181
|
+
* draws — among the editor's fields, above the catalog table, in the import
|
|
182
|
+
* wizard's After import step — and a wrapper would add a gap the page
|
|
183
|
+
* already spaces.
|
|
183
184
|
*/
|
|
184
185
|
export declare function registerCommerceZones(): void;
|
|
@@ -22,8 +22,9 @@ export const PRODUCT_IMPORT_ZONE = definePluginZone('productImport');
|
|
|
22
22
|
* Declares the three zones, from the console registrar. The ids are the ones
|
|
23
23
|
* widgets have always registered under, so a widget written for them keeps
|
|
24
24
|
* working. Each is `bare`: the section is one item of a layout this plugin
|
|
25
|
-
* draws — among the editor's fields, above the catalog table,
|
|
26
|
-
* import
|
|
25
|
+
* draws — among the editor's fields, above the catalog table, in the import
|
|
26
|
+
* wizard's After import step — and a wrapper would add a gap the page
|
|
27
|
+
* already spaces.
|
|
27
28
|
*/ export function registerCommerceZones() {
|
|
28
29
|
const owner = {
|
|
29
30
|
pluginId: BUNDLE_ID
|
|
@@ -44,10 +45,10 @@ export const PRODUCT_IMPORT_ZONE = definePluginZone('productImport');
|
|
|
44
45
|
}, owner);
|
|
45
46
|
registerPluginZone({
|
|
46
47
|
zone: PRODUCT_IMPORT_ZONE,
|
|
47
|
-
label: 'Product
|
|
48
|
+
label: 'Product import',
|
|
48
49
|
surface: 'console',
|
|
49
50
|
layout: 'bare',
|
|
50
|
-
description: '
|
|
51
|
+
description: 'In the product import wizard’s After import step. A widget here sets options for what happens to the imported products once they land; the hub hands them to `productsHub` with the ids the import created.'
|
|
51
52
|
}, owner);
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/product-zones.ts"],"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\nimport {\n definePluginZone,\n registerPluginZone,\n} from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport { BUNDLE_ID } from '../../constants/bundle-common'\n\n/**\n * The zones this plugin hosts on its product surfaces (AGL-2916): the product\n * editor, the products hub and the CSV import dialog.\n *\n * Each is drawn through the shell's renderer (`useConsoleWidgetSlot`), so a\n * widget there passes the same enablement, entitlement and permission gates as\n * one on a console page. The props are this plugin's words for its catalog,\n * declared on the zone's token; a widget from another plugin restates the\n * shape it reads rather than importing this package. A widget proposes and\n * never writes: every write is this plugin's, with the guards it carries.\n */\n\n/** A product type, as the commerce catalog names it. */\nexport type ConsoleProductType = 'physical' | 'digital' | 'service'\n\n/** A product option axis: its name and the values a shopper picks from. */\nexport interface ConsoleProductOption {\n name: string\n values: string[]\n}\n\n/** A product as the commerce editor holds it: saved, or staged and unsaved. */\nexport interface ConsoleProductDraft {\n /** `null` for a product nobody has saved yet. */\n id: string | null\n name: string\n type: ConsoleProductType\n description: string\n tags: string[]\n categoryIds: string[]\n options: ConsoleProductOption[]\n /** The product's media, in order; the first is the photo the storefront leads with. */\n mediaUrls: string[]\n seoTitle: string\n seoDescription: string\n}\n\n/**\n * Copy a widget proposes for one product. A field left out is left alone;\n * `optionNames` renames the product's options in order, keeping every\n * variant, and is ignored unless it names each option once.\n */\nexport interface ConsoleProductCopyValues {\n description?: string\n tags?: string[]\n categoryIds?: string[]\n optionNames?: string[]\n seoTitle?: string\n seoDescription?: string\n}\n\n/**\n * What the `productEditor` zone hands each widget: the product as the editor\n * holds it, and `proposeValues`, which stages copy in the editor as unsaved\n * edits. The zone sits under the product's description, tags and categories,\n * as the editor's search listing hosts `seoFields`. Save product is the\n * write, with the guards it carries.\n */\nexport interface ConsoleProductEditorZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n product: ConsoleProductDraft\n /** The site's categories, as the editor's picker lists them. */\n categories: ReadonlyArray<{ id: string; name: string }>\n /**\n * Stages `values` in the editor as unsaved edits under `key`. Save product\n * is what stores them; a widget never writes the product itself.\n */\n proposeValues: (values: ConsoleProductCopyValues, key: string) => void\n}\n\n/** A catalog row as the products hub holds it. */\nexport interface ConsoleProductSummary {\n id: string\n name: string\n status: 'draft' | 'active' | 'archived'\n /** Whether a variant of the product has no price yet. */\n priceMissing: boolean\n /** Whether the product has a photo. */\n hasPhoto: boolean\n}\n\n/** A new product a widget asks the hub to create, always as a draft with no price. */\nexport interface ConsoleProposedProduct {\n name: string\n type: ConsoleProductType\n description: string\n tags: string[]\n options: ConsoleProductOption[]\n seoTitle: string\n seoDescription: string\n}\n\n/** A discount a widget asks the hub to create, always switched off. */\nexport interface ConsoleProposedDiscount {\n name: string\n /** The code a shopper types, or `null` for a discount that applies on its own. */\n code: string | null\n kind: 'percent' | 'fixed' | 'free_shipping'\n valuePct: number | null\n valueCents: number | null\n minSubtotalCents: number | null\n}\n\n/**\n * What the `productsHub` zone hands each widget: the catalog rows the hub\n * holds, the products its latest import created, and the hub's own writes a\n * widget may ask it to make — copy onto saved products, new draft products,\n * categories and switched-off discounts. The zone sits above the catalog\n * table. Each write is the hub's, with its seed, allowance and slug checks; a\n * widget writes nothing itself. Every write resolves once it is stored and\n * throws with a message a person can act on when the hub refuses it: an\n * allowance the plan has no room for, a product that is gone.\n *\n * The creates are safe to ask twice: a product or a category named like one\n * the site has, and a discount with the code (or, with no code, the name) of\n * one the site has, are passed over, so a proposal applied again, by anyone,\n * creates nothing it already created.\n */\nexport interface ConsoleProductsHubZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n /** The catalog rows the hub holds, in its order. */\n products: readonly ConsoleProductSummary[]\n /**\n * The products the hub's latest CSV import created in this visit, and the\n * options the `productImport` zone set for them; `null` before any import.\n * `key` is new for each import.\n */\n lastImport: {\n key: string\n productIds: readonly string[]\n options: Readonly<Record<string, boolean>>\n } | null\n /** Writes copy onto a saved product, read fresh from the store. */\n applyProductCopy: (productId: string, values: ConsoleProductCopyValues) => Promise<void>\n /** Creates each product as a draft with no price, and resolves with the ids it created. */\n createProductDrafts: (products: readonly ConsoleProposedProduct[]) => Promise<string[]>\n /** Creates categories by name, and resolves with how many it created. */\n createCategories: (names: readonly string[]) => Promise<number>\n /** Creates each discount switched off, and resolves with how many it created. */\n createDiscountDrafts: (discounts: readonly ConsoleProposedDiscount[]) => Promise<number>\n}\n\n/**\n * What the `productImport` zone hands each widget, inside the CSV import\n * dialog: how many products the import creates, and options a widget sets\n * for what happens to them once they land. The hub hands the options, with\n * the ids of the products the import created, to `productsHub` as its\n * `lastImport`.\n */\nexport interface ConsoleProductImportZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n /** How many products the import creates. */\n count: number\n /** The options set for this import, by key. */\n options: Readonly<Record<string, boolean>>\n /** Sets one option; the hub carries it to `productsHub` with what the import created. */\n setOption: (key: string, on: boolean) => void\n}\n\nexport const PRODUCT_EDITOR_ZONE =\n definePluginZone<ConsoleProductEditorZoneProps>('productEditor')\n\nexport const PRODUCTS_HUB_ZONE =\n definePluginZone<ConsoleProductsHubZoneProps>('productsHub')\n\nexport const PRODUCT_IMPORT_ZONE =\n definePluginZone<ConsoleProductImportZoneProps>('productImport')\n\n/**\n * Declares the three zones, from the console registrar. The ids are the ones\n * widgets have always registered under, so a widget written for them keeps\n * working. Each is `bare`: the section is one item of a layout this plugin\n * draws — among the editor's fields, above the catalog table, inside the\n * import dialog — and a wrapper would add a gap the page already spaces.\n */\nexport function registerCommerceZones(): void {\n const owner = { pluginId: BUNDLE_ID }\n registerPluginZone(\n {\n zone: PRODUCT_EDITOR_ZONE,\n label: 'Product editor',\n surface: 'console',\n layout: 'bare',\n description:\n 'Under a product’s description, tags and categories in the product editor. A widget here proposes copy for the fields through `proposeValues`, staged as unsaved edits; Save product is the write.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: PRODUCTS_HUB_ZONE,\n label: 'Products hub',\n surface: 'console',\n layout: 'bare',\n description:\n 'Above the catalog table on the Products page. A widget here proposes copy, draft products, categories and switched-off discounts, and asks the hub to write them; every write is the hub’s, with its allowance and slug checks.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: PRODUCT_IMPORT_ZONE,\n label: 'Product CSV import',\n surface: 'console',\n layout: 'bare',\n description:\n 'Inside the CSV import dialog. A widget here sets options for what happens to the imported products once they land; the hub hands them to `productsHub` with the ids the import created.',\n },\n owner,\n )\n}\n"],"names":["definePluginZone","registerPluginZone","BUNDLE_ID","PRODUCT_EDITOR_ZONE","PRODUCTS_HUB_ZONE","PRODUCT_IMPORT_ZONE","registerCommerceZones","owner","pluginId","zone","label","surface","layout","description"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,gBAAgB,EAChBC,kBAAkB,QACb,2CAA0C;AACjD,SAASC,SAAS,QAAQ,mCAA+B;AAuKzD,OAAO,MAAMC,sBACXH,iBAAgD,iBAAgB;AAElE,OAAO,MAAMI,oBACXJ,iBAA8C,eAAc;AAE9D,OAAO,MAAMK,sBACXL,iBAAgD,iBAAgB;AAElE;;;;;;CAMC,GACD,OAAO,SAASM;IACd,MAAMC,QAAQ;QAAEC,UAAUN;IAAU;IACpCD,mBACE;QACEQ,MAAMN;QACNO,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;IAEFN,mBACE;QACEQ,MAAML;QACNM,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;IAEFN,mBACE;QACEQ,MAAMJ;QACNK,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;AAEJ"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/product-zones.ts"],"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\nimport {\n definePluginZone,\n registerPluginZone,\n} from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport { BUNDLE_ID } from '../../constants/bundle-common'\n\n/**\n * The zones this plugin hosts on its product surfaces (AGL-2916): the product\n * editor, the products hub and the import wizard's After import step.\n *\n * Each is drawn through the shell's renderer (`useConsoleWidgetSlot`), so a\n * widget there passes the same enablement, entitlement and permission gates as\n * one on a console page. The props are this plugin's words for its catalog,\n * declared on the zone's token; a widget from another plugin restates the\n * shape it reads rather than importing this package. A widget proposes and\n * never writes: every write is this plugin's, with the guards it carries.\n */\n\n/** A product type, as the commerce catalog names it. */\nexport type ConsoleProductType = 'physical' | 'digital' | 'service'\n\n/** A product option axis: its name and the values a shopper picks from. */\nexport interface ConsoleProductOption {\n name: string\n values: string[]\n}\n\n/** A product as the commerce editor holds it: saved, or staged and unsaved. */\nexport interface ConsoleProductDraft {\n /** `null` for a product nobody has saved yet. */\n id: string | null\n name: string\n type: ConsoleProductType\n description: string\n tags: string[]\n categoryIds: string[]\n options: ConsoleProductOption[]\n /** The product's media, in order; the first is the photo the storefront leads with. */\n mediaUrls: string[]\n seoTitle: string\n seoDescription: string\n}\n\n/**\n * Copy a widget proposes for one product. A field left out is left alone;\n * `optionNames` renames the product's options in order, keeping every\n * variant, and is ignored unless it names each option once.\n */\nexport interface ConsoleProductCopyValues {\n description?: string\n tags?: string[]\n categoryIds?: string[]\n optionNames?: string[]\n seoTitle?: string\n seoDescription?: string\n}\n\n/**\n * What the `productEditor` zone hands each widget: the product as the editor\n * holds it, and `proposeValues`, which stages copy in the editor as unsaved\n * edits. The zone sits under the product's description, tags and categories,\n * as the editor's search listing hosts `seoFields`. Save product is the\n * write, with the guards it carries.\n */\nexport interface ConsoleProductEditorZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n product: ConsoleProductDraft\n /** The site's categories, as the editor's picker lists them. */\n categories: ReadonlyArray<{ id: string; name: string }>\n /**\n * Stages `values` in the editor as unsaved edits under `key`. Save product\n * is what stores them; a widget never writes the product itself.\n */\n proposeValues: (values: ConsoleProductCopyValues, key: string) => void\n}\n\n/** A catalog row as the products hub holds it. */\nexport interface ConsoleProductSummary {\n id: string\n name: string\n status: 'draft' | 'active' | 'archived'\n /** Whether a variant of the product has no price yet. */\n priceMissing: boolean\n /** Whether the product has a photo. */\n hasPhoto: boolean\n}\n\n/** A new product a widget asks the hub to create, always as a draft with no price. */\nexport interface ConsoleProposedProduct {\n name: string\n type: ConsoleProductType\n description: string\n tags: string[]\n options: ConsoleProductOption[]\n seoTitle: string\n seoDescription: string\n}\n\n/** A discount a widget asks the hub to create, always switched off. */\nexport interface ConsoleProposedDiscount {\n name: string\n /** The code a shopper types, or `null` for a discount that applies on its own. */\n code: string | null\n kind: 'percent' | 'fixed' | 'free_shipping'\n valuePct: number | null\n valueCents: number | null\n minSubtotalCents: number | null\n}\n\n/**\n * What the `productsHub` zone hands each widget: the catalog rows the hub\n * holds, the products its latest import created, and the hub's own writes a\n * widget may ask it to make — copy onto saved products, new draft products,\n * categories and switched-off discounts. The zone sits above the catalog\n * table. Each write is the hub's, with its seed, allowance and slug checks; a\n * widget writes nothing itself. Every write resolves once it is stored and\n * throws with a message a person can act on when the hub refuses it: an\n * allowance the plan has no room for, a product that is gone.\n *\n * The creates are safe to ask twice: a product or a category named like one\n * the site has, and a discount with the code (or, with no code, the name) of\n * one the site has, are passed over, so a proposal applied again, by anyone,\n * creates nothing it already created.\n */\nexport interface ConsoleProductsHubZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n /** The catalog rows the hub holds, in its order. */\n products: readonly ConsoleProductSummary[]\n /**\n * The products the hub's latest import created in this visit, and the\n * options the `productImport` zone set for them; `null` before any import.\n * `key` is new for each import.\n */\n lastImport: {\n key: string\n productIds: readonly string[]\n options: Readonly<Record<string, boolean>>\n } | null\n /** Writes copy onto a saved product, read fresh from the store. */\n applyProductCopy: (productId: string, values: ConsoleProductCopyValues) => Promise<void>\n /** Creates each product as a draft with no price, and resolves with the ids it created. */\n createProductDrafts: (products: readonly ConsoleProposedProduct[]) => Promise<string[]>\n /** Creates categories by name, and resolves with how many it created. */\n createCategories: (names: readonly string[]) => Promise<number>\n /** Creates each discount switched off, and resolves with how many it created. */\n createDiscountDrafts: (discounts: readonly ConsoleProposedDiscount[]) => Promise<number>\n}\n\n/**\n * What the `productImport` zone hands each widget, in the import wizard's\n * After import step: how many products the import creates, and options a widget sets\n * for what happens to them once they land. The hub hands the options, with\n * the ids of the products the import created, to `productsHub` as its\n * `lastImport`.\n */\nexport interface ConsoleProductImportZoneProps {\n hostId: string\n /** The org the page names; `undefined` where the host does not know it. */\n orgId: string | undefined\n /** How many products the import creates. */\n count: number\n /** The options set for this import, by key. */\n options: Readonly<Record<string, boolean>>\n /** Sets one option; the hub carries it to `productsHub` with what the import created. */\n setOption: (key: string, on: boolean) => void\n}\n\nexport const PRODUCT_EDITOR_ZONE =\n definePluginZone<ConsoleProductEditorZoneProps>('productEditor')\n\nexport const PRODUCTS_HUB_ZONE =\n definePluginZone<ConsoleProductsHubZoneProps>('productsHub')\n\nexport const PRODUCT_IMPORT_ZONE =\n definePluginZone<ConsoleProductImportZoneProps>('productImport')\n\n/**\n * Declares the three zones, from the console registrar. The ids are the ones\n * widgets have always registered under, so a widget written for them keeps\n * working. Each is `bare`: the section is one item of a layout this plugin\n * draws — among the editor's fields, above the catalog table, in the import\n * wizard's After import step — and a wrapper would add a gap the page\n * already spaces.\n */\nexport function registerCommerceZones(): void {\n const owner = { pluginId: BUNDLE_ID }\n registerPluginZone(\n {\n zone: PRODUCT_EDITOR_ZONE,\n label: 'Product editor',\n surface: 'console',\n layout: 'bare',\n description:\n 'Under a product’s description, tags and categories in the product editor. A widget here proposes copy for the fields through `proposeValues`, staged as unsaved edits; Save product is the write.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: PRODUCTS_HUB_ZONE,\n label: 'Products hub',\n surface: 'console',\n layout: 'bare',\n description:\n 'Above the catalog table on the Products page. A widget here proposes copy, draft products, categories and switched-off discounts, and asks the hub to write them; every write is the hub’s, with its allowance and slug checks.',\n },\n owner,\n )\n registerPluginZone(\n {\n zone: PRODUCT_IMPORT_ZONE,\n label: 'Product import',\n surface: 'console',\n layout: 'bare',\n description:\n 'In the product import wizard’s After import step. A widget here sets options for what happens to the imported products once they land; the hub hands them to `productsHub` with the ids the import created.',\n },\n owner,\n )\n}\n"],"names":["definePluginZone","registerPluginZone","BUNDLE_ID","PRODUCT_EDITOR_ZONE","PRODUCTS_HUB_ZONE","PRODUCT_IMPORT_ZONE","registerCommerceZones","owner","pluginId","zone","label","surface","layout","description"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,gBAAgB,EAChBC,kBAAkB,QACb,2CAA0C;AACjD,SAASC,SAAS,QAAQ,mCAA+B;AAuKzD,OAAO,MAAMC,sBACXH,iBAAgD,iBAAgB;AAElE,OAAO,MAAMI,oBACXJ,iBAA8C,eAAc;AAE9D,OAAO,MAAMK,sBACXL,iBAAgD,iBAAgB;AAElE;;;;;;;CAOC,GACD,OAAO,SAASM;IACd,MAAMC,QAAQ;QAAEC,UAAUN;IAAU;IACpCD,mBACE;QACEQ,MAAMN;QACNO,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;IAEFN,mBACE;QACEQ,MAAML;QACNM,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;IAEFN,mBACE;QACEQ,MAAMJ;QACNK,OAAO;QACPC,SAAS;QACTC,QAAQ;QACRC,aACE;IACJ,GACAN;AAEJ"}
|