@aglyn/plugins-commerce 1.0.0-beta.234 → 1.0.0-beta.236
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/lib/components/cart.js +3 -2
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/console/commerce-analytics-card.component.js +3 -6
- package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
- package/src/lib/components/console/commerce-glance-card.component.js +9 -3
- package/src/lib/components/console/commerce-glance-card.component.js.map +1 -1
- package/src/lib/components/console/customer-notifications-card.component.js +3 -1
- package/src/lib/components/console/customer-notifications-card.component.js.map +1 -1
- package/src/lib/components/console/discounts-card.component.js +2 -3
- package/src/lib/components/console/discounts-card.component.js.map +1 -1
- package/src/lib/components/console/host-coupons-card.component.js +3 -4
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
- package/src/lib/components/console/local-delivery-settings-card.component.js +2 -2
- package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +1 -1
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/member-posts-card.component.js +2 -2
- package/src/lib/components/console/member-posts-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +35 -30
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.d.ts +11 -1
- package/src/lib/components/console/order-fulfillment-panel.component.js +75 -4
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -1
- package/src/lib/components/console/order-zones.d.ts +45 -1
- package/src/lib/components/console/order-zones.js +8 -0
- package/src/lib/components/console/order-zones.js.map +1 -1
- package/src/lib/components/console/pickup-delivery-queue-card.component.js +15 -1
- package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.js +3 -2
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.d.ts +2 -0
- package/src/lib/components/console/product-editor-dialog.component.js +30 -12
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +13 -4
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/products-hub-zone.component.d.ts +2 -1
- package/src/lib/components/console/products-hub-zone.component.js +1 -1
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
- package/src/lib/components/console/reviews-moderation-card.component.js +1 -2
- package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -1
- package/src/lib/components/console/shipping-settings-card.component.js +2 -2
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
- package/src/lib/components/console/shipstation-card.component.js +1 -2
- package/src/lib/components/console/shipstation-card.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +3 -1
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/tax-settings-card.component.js +4 -1
- package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
- package/src/lib/components/order-status.js +24 -1
- package/src/lib/components/order-status.js.map +1 -1
- package/src/lib/components/product-detail.js +30 -16
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-grid.d.ts +7 -0
- package/src/lib/components/product-grid.js +50 -7
- package/src/lib/components/product-grid.js.map +1 -1
- package/src/lib/components/related-products.js +1 -1
- package/src/lib/components/related-products.js.map +1 -1
- package/src/lib/components/wishlist.js +1 -1
- package/src/lib/components/wishlist.js.map +1 -1
- package/src/lib/declarations.server.js +11 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-orders.d.ts +8 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce.d.ts +21 -0
- package/src/lib/model/commerce.js +39 -1
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/index.js +1 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-annotations.d.ts +54 -0
- package/src/lib/model/order-annotations.js +76 -0
- package/src/lib/model/order-annotations.js.map +1 -0
- package/src/lib/model/order-events.d.ts +2 -0
- package/src/lib/model/order-events.js.map +1 -1
- package/src/lib/model/order-fulfillment.d.ts +29 -0
- package/src/lib/model/order-fulfillment.js +63 -0
- package/src/lib/model/order-fulfillment.js.map +1 -1
- package/src/lib/model/order-local-fulfillment.d.ts +36 -0
- package/src/lib/model/order-local-fulfillment.js +19 -1
- package/src/lib/model/order-local-fulfillment.js.map +1 -1
- package/src/lib/model/order-status-view.d.ts +11 -0
- package/src/lib/model/order-status-view.js +20 -3
- package/src/lib/model/order-status-view.js.map +1 -1
- package/src/lib/model/product-write.js +8 -4
- package/src/lib/model/product-write.js.map +1 -1
- package/src/lib/server/api-v1/openapi.js +1 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +1 -0
- package/src/lib/server/api-v1/order-view.js +10 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -1
- package/src/lib/server/cart-checkout.js +9 -1
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/catalog.d.ts +6 -0
- package/src/lib/server/catalog.js +3 -1
- package/src/lib/server/catalog.js.map +1 -1
- package/src/lib/server/checkout.js +11 -1
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +12 -0
- package/src/lib/server/fulfill-order.js +13 -0
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/local-delivery-records.d.ts +30 -0
- package/src/lib/server/local-delivery-records.js +284 -0
- package/src/lib/server/local-delivery-records.js.map +1 -0
- package/src/lib/server/order-annotations.d.ts +28 -0
- package/src/lib/server/order-annotations.js +143 -0
- package/src/lib/server/order-annotations.js.map +1 -0
- package/src/lib/server/order-events.js +2 -1
- package/src/lib/server/order-events.js.map +1 -1
- package/src/lib/server/order-notifications.js +39 -8
- package/src/lib/server/order-notifications.js.map +1 -1
- package/src/lib/server/pos-payment.js +9 -3
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/product-catalog.js +6 -2
- package/src/lib/server/product-catalog.js.map +1 -1
- package/src/lib/server/product-drafts.d.ts +36 -9
- package/src/lib/server/product-drafts.js +24 -5
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product.d.ts +6 -1
- package/src/lib/server/product.js +1 -1
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.d.ts +29 -0
- package/src/lib/server/products-write.js +7 -7
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server/shipment-records.js +2 -0
- package/src/lib/server/shipment-records.js.map +1 -1
- package/src/lib/server/site-page-resolver.d.ts +13 -0
- package/src/lib/server/site-page-resolver.js +138 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +5 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/utils/use-storefront-purchase-event.js +8 -2
- package/src/lib/utils/use-storefront-purchase-event.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/reviews-moderation-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n MenuItem,\n Rating,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, doc, limit, orderBy, query, where } from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { usePagedCollection } from '@aglyn/tenant-feature-instance'\nimport { EntitlementGatedCard } from './entitlement-gate.component'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ReviewsModerationCardProps {\n hostId: string\n}\n\n/**\n * Review moderation (AGL-324): pending queue with approve/reject and\n * inline seller replies; approved reviews render on the product-reviews\n * block with the aggregate in JSON-LD.\n *\n * Gated on `productReviews` since AGL-2080. `server/reviews.ts:108` enforces\n * it, and this card rendered identically on Free — an operator could work a\n * moderation queue whose approvals the renderer would never honour.\n */\n\nconst reviewsHelp = pluginDocsHelp('catalog', {\n anchor: '#products-options-and-variants',\n excerpt:\n 'Customer reviews left on your products, waiting for you to ' +\n 'publish or reject them.',\n})\n\nexport function ReviewsModerationCard(props: ReviewsModerationCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [filter, setFilter] = useState('pending')\n /*\n * The status filter is the QUERY, and the order is the server's.\n *\n * This read `limit(200)` with no `orderBy` and then filtered and sorted in\n * the browser. A limited Firestore query with no ordering returns\n * DOCUMENT-ID order, and reviews are written with `.add()`, so those two\n * hundred were a pseudo-random sample — \"Pending\" showed whichever pending\n * reviews happened to fall in it, which on a busy shop is not the queue and\n * cannot be worked through.\n *\n * Filtering here rather than in the browser is what lets the window shrink:\n * a page of 25 mixed reviews might hold three pending ones, and a card that\n * rendered those three would call it a page while the queue sat behind it.\n * `reviews(status ASC, createdAtMs DESC)` is in\n * cloud/firebase-firestore.indexes.json for exactly this.\n */\n const {\n rows: reviews,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n } = usePagedCollection<any>(\n (pageLimit) =>\n query(\n collection(firestore, 'hosts', hostId, 'reviews'),\n ...(filter === 'all' ? [] : [where('status', '==', filter)]),\n orderBy('createdAtMs', 'desc'),\n limit(pageLimit),\n ),\n [firestore, hostId, filter],\n { idField: '$id' },\n )\n const [replyFor, setReplyFor] = useState<string | null>(null)\n const [reply, setReply] = useState('')\n\n // An approved review is on the product page — its star aggregate and its\n // reply — so moderating one carries the site's cache drop (AGL-3386).\n const setStatus = (review: any, status: string) =>\n writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId, 'reviews', review.$id), {\n status,\n }),\n })\n\n return (\n <EntitlementGatedCard\n help={reviewsHelp}\n hostId={hostId}\n feature=\"productReviews\"\n header={'Review moderation'}\n upsell={\n 'Verified-buyer reviews let customers rate what they bought and ' +\n 'show the star aggregate on your product pages and in search ' +\n 'results.'\n }\n >\n <CardDisplay\n header={'Review moderation'}\n help={reviewsHelp}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <TextField\n label=\"Show\"\n value={filter}\n onChange={(event) => setFilter(event.target.value)}\n size=\"small\"\n select\n sx={{ maxWidth: 180 }}\n >\n <MenuItem value=\"pending\">{'Pending'}</MenuItem>\n <MenuItem value=\"approved\">{'Approved'}</MenuItem>\n <MenuItem value=\"rejected\">{'Rejected'}</MenuItem>\n <MenuItem value=\"all\">{'All'}</MenuItem>\n </TextField>\n {reviews.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Nothing to moderate.'}\n </Typography>\n ) : (\n reviews.map((review: any) => (\n <Stack key={review.$id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Rating value={review.rating} size=\"small\" readOnly />\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {review.authorName}\n </Typography>\n {review.verified ? (\n <Chip label=\"Verified\" size=\"small\" color=\"success\" variant=\"outlined\" />\n ) : null}\n <Chip label={review.status} size=\"small\" variant=\"outlined\" />\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {review.body}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n {review.status !== 'approved' ? (\n <Button size=\"small\" onClick={() => setStatus(review, 'approved')}>\n {'Approve'}\n </Button>\n ) : null}\n {review.status !== 'rejected' ? (\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => setStatus(review, 'rejected')}\n >\n {'Reject'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n onClick={() => {\n setReplyFor(review.$id)\n setReply(review.reply ?? '')\n }}\n >\n {review.reply ? 'Edit reply' : 'Reply'}\n </Button>\n </Stack>\n {replyFor === review.$id ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n value={reply}\n onChange={(event) => setReply(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Public seller reply\"\n />\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(\n doc(firestore, 'hosts', hostId, 'reviews', review.$id),\n { reply: reply.trim().slice(0, 500) || null },\n ),\n })\n setReplyFor(null)\n enqueueSnackbar('Reply saved', {\n variant: 'success',\n persist: false,\n })\n }}\n >\n {'Save'}\n </Button>\n </Stack>\n ) : null}\n </Stack>\n ))\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={reviews.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n </CardDisplay>\n </EntitlementGatedCard>\n )\n}\nReviewsModerationCard.displayName = 'ReviewsModerationCard'\n\nexport default ReviewsModerationCard\n"],"names":["CardDisplay","ListPagination","useSnackbar","Button","Chip","MenuItem","Rating","Stack","TextField","Typography","collection","doc","limit","orderBy","query","where","useState","useFirestore","useUser","writeSiteWideChange","usePagedCollection","EntitlementGatedCard","pluginDocsHelp","reviewsHelp","anchor","excerpt","ReviewsModerationCard","props","hostId","firestore","data","user","enqueueSnackbar","filter","setFilter","rows","reviews","hasMore","page","setPage","pageSize","setPageSize","pageLimit","idField","replyFor","setReplyFor","reply","setReply","setStatus","review","status","write","batch","update","$id","help","feature","header","upsell","contentGutterX","contentGutterY","spacing","label","value","onChange","event","target","size","select","sx","maxWidth","length","variant","color","map","direction","alignItems","rating","readOnly","fontWeight","authorName","verified","body","onClick","flex","placeholder","trim","slice","persist","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/reviews-moderation-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Button,\n Chip,\n MenuItem,\n Rating,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { collection, doc, limit, orderBy, query, where } from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport { writeSiteWideChange } from '@aglyn/tenant-feature-instance/hooks/helpers/site-wide-change'\nimport { usePagedCollection } from '@aglyn/tenant-feature-instance'\nimport { EntitlementGatedCard } from './entitlement-gate.component'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ReviewsModerationCardProps {\n hostId: string\n}\n\n/**\n * Review moderation (AGL-324): pending queue with approve/reject and\n * inline seller replies; approved reviews render on the product-reviews\n * block with the aggregate in JSON-LD.\n *\n * Gated on `productReviews` since AGL-2080. `server/reviews.ts:108` enforces\n * it, and this card rendered identically on Free — an operator could work a\n * moderation queue whose approvals the renderer would never honour.\n */\n\nconst reviewsHelp = pluginDocsHelp('catalog', { anchor: '#product-reviews' })\n\nexport function ReviewsModerationCard(props: ReviewsModerationCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [filter, setFilter] = useState('pending')\n /*\n * The status filter is the QUERY, and the order is the server's.\n *\n * This read `limit(200)` with no `orderBy` and then filtered and sorted in\n * the browser. A limited Firestore query with no ordering returns\n * DOCUMENT-ID order, and reviews are written with `.add()`, so those two\n * hundred were a pseudo-random sample — \"Pending\" showed whichever pending\n * reviews happened to fall in it, which on a busy shop is not the queue and\n * cannot be worked through.\n *\n * Filtering here rather than in the browser is what lets the window shrink:\n * a page of 25 mixed reviews might hold three pending ones, and a card that\n * rendered those three would call it a page while the queue sat behind it.\n * `reviews(status ASC, createdAtMs DESC)` is in\n * cloud/firebase-firestore.indexes.json for exactly this.\n */\n const {\n rows: reviews,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n } = usePagedCollection<any>(\n (pageLimit) =>\n query(\n collection(firestore, 'hosts', hostId, 'reviews'),\n ...(filter === 'all' ? [] : [where('status', '==', filter)]),\n orderBy('createdAtMs', 'desc'),\n limit(pageLimit),\n ),\n [firestore, hostId, filter],\n { idField: '$id' },\n )\n const [replyFor, setReplyFor] = useState<string | null>(null)\n const [reply, setReply] = useState('')\n\n // An approved review is on the product page — its star aggregate and its\n // reply — so moderating one carries the site's cache drop (AGL-3386).\n const setStatus = (review: any, status: string) =>\n writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(doc(firestore, 'hosts', hostId, 'reviews', review.$id), {\n status,\n }),\n })\n\n return (\n <EntitlementGatedCard\n help={reviewsHelp}\n hostId={hostId}\n feature=\"productReviews\"\n header={'Review moderation'}\n upsell={\n 'Verified-buyer reviews let customers rate what they bought and ' +\n 'show the star aggregate on your product pages and in search ' +\n 'results.'\n }\n >\n <CardDisplay\n header={'Review moderation'}\n help={reviewsHelp}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <TextField\n label=\"Show\"\n value={filter}\n onChange={(event) => setFilter(event.target.value)}\n size=\"small\"\n select\n sx={{ maxWidth: 180 }}\n >\n <MenuItem value=\"pending\">{'Pending'}</MenuItem>\n <MenuItem value=\"approved\">{'Approved'}</MenuItem>\n <MenuItem value=\"rejected\">{'Rejected'}</MenuItem>\n <MenuItem value=\"all\">{'All'}</MenuItem>\n </TextField>\n {reviews.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Nothing to moderate.'}\n </Typography>\n ) : (\n reviews.map((review: any) => (\n <Stack key={review.$id} spacing={0.5}>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center' }}>\n <Rating value={review.rating} size=\"small\" readOnly />\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {review.authorName}\n </Typography>\n {review.verified ? (\n <Chip label=\"Verified\" size=\"small\" color=\"success\" variant=\"outlined\" />\n ) : null}\n <Chip label={review.status} size=\"small\" variant=\"outlined\" />\n </Stack>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {review.body}\n </Typography>\n <Stack direction=\"row\" spacing={1}>\n {review.status !== 'approved' ? (\n <Button size=\"small\" onClick={() => setStatus(review, 'approved')}>\n {'Approve'}\n </Button>\n ) : null}\n {review.status !== 'rejected' ? (\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => setStatus(review, 'rejected')}\n >\n {'Reject'}\n </Button>\n ) : null}\n <Button\n size=\"small\"\n onClick={() => {\n setReplyFor(review.$id)\n setReply(review.reply ?? '')\n }}\n >\n {review.reply ? 'Edit reply' : 'Reply'}\n </Button>\n </Stack>\n {replyFor === review.$id ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n value={reply}\n onChange={(event) => setReply(event.target.value)}\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Public seller reply\"\n />\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"primary\"\n onClick={async () => {\n await writeSiteWideChange({\n firestore,\n user,\n hostId,\n write: (batch) =>\n batch.update(\n doc(firestore, 'hosts', hostId, 'reviews', review.$id),\n { reply: reply.trim().slice(0, 500) || null },\n ),\n })\n setReplyFor(null)\n enqueueSnackbar('Reply saved', {\n variant: 'success',\n persist: false,\n })\n }}\n >\n {'Save'}\n </Button>\n </Stack>\n ) : null}\n </Stack>\n ))\n )}\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={reviews.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n </CardDisplay>\n </EntitlementGatedCard>\n )\n}\nReviewsModerationCard.displayName = 'ReviewsModerationCard'\n\nexport default ReviewsModerationCard\n"],"names":["CardDisplay","ListPagination","useSnackbar","Button","Chip","MenuItem","Rating","Stack","TextField","Typography","collection","doc","limit","orderBy","query","where","useState","useFirestore","useUser","writeSiteWideChange","usePagedCollection","EntitlementGatedCard","pluginDocsHelp","reviewsHelp","anchor","ReviewsModerationCard","props","hostId","firestore","data","user","enqueueSnackbar","filter","setFilter","rows","reviews","hasMore","page","setPage","pageSize","setPageSize","pageLimit","idField","replyFor","setReplyFor","reply","setReply","setStatus","review","status","write","batch","update","$id","help","feature","header","upsell","contentGutterX","contentGutterY","spacing","label","value","onChange","event","target","size","select","sx","maxWidth","length","variant","color","map","direction","alignItems","rating","readOnly","fontWeight","authorName","verified","body","onClick","flex","placeholder","trim","slice","persist","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
|
|
@@ -217,8 +217,8 @@ import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
|
217
217
|
const cents = (raw)=>raw.trim() === '' ? undefined : Math.round(Number(raw) * 100);
|
|
218
218
|
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
219
219
|
header: 'Shipping',
|
|
220
|
-
help: pluginDocsHelp('
|
|
221
|
-
anchor: '#
|
|
220
|
+
help: pluginDocsHelp('shipping', {
|
|
221
|
+
anchor: '#zones-and-rates'
|
|
222
222
|
}),
|
|
223
223
|
contentGutterX: true,
|
|
224
224
|
contentGutterY: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shipping-settings-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n AlertTitle,\n Button,\n Divider,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { doc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n useFirestoreDoc,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ShippingSettingsCardProps {\n hostId: string\n}\n\n/**\n * Shipping settings (AGL-288): zones (countries, '*' = rest of world)\n * and rates (flat / free-over / subtotal or weight tiers), stored on\n * `hosts/{hostId}/settings/store` under `shipping`. Cart checkout (AGL-1707)\n * and buy-now (AGL-1720) both read this document and declare the rates as\n * Stripe `shipping_options` (`planCheckoutShipping`) — until those landed\n * nothing read it, and everything saved here was charged to nobody. Buy-now\n * resolves only for physical one-time sales; draft orders and POS still\n * resolve no shipping at all.\n *\n * WHAT A ZONE LAYOUT COSTS THE SHOPPER (AGL-1721). A session's rates are\n * charged as picked and never re-checked against the address, so the checkout\n * can only offer rates that hold for every country it will ship to. Saving\n * zones whose rates DIFFER therefore makes the storefront ask the shopper\n * where the parcel is going before it can price one, and a destination no\n * zone covers is refused outright rather than shipped for free. One zone, or\n * one '*' zone, needs neither: the shopper is asked nothing.\n *\n * AND THE MERCHANT HAS TO BE ABLE TO SEE IT (AGL-1791). Both consequences\n * were invisible here: a merchant saved zones and the checkouts they refused\n * were refused with nothing on this page saying so, which is a support ticket\n * from someone who did nothing wrong. The coverage block reads\n * `summarizeShippingCoverage` off the same `current` object the save writes,\n * so it moves as the form is typed rather than after a round trip.\n *\n * It escalates to a warning ONLY for a merchant who priced some destinations\n * and not others. A merchant whose rates reach nowhere is not misconfigured —\n * that store charges no shipping and refuses nobody — and warning them would\n * nag every store selling downloads.\n */\nexport function ShippingSettingsCard(props: ShippingSettingsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const {\n data: store,\n status: storeStatus,\n /**\n * The settings doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). `current` is `draft ?? store?.shipping ?? {}` and the save\n * writes `{shipping: current}`, so `merge: true` protects the doc's other\n * maps and nothing inside this one — `zones` and `rates` are arrays, and\n * a merge replaces an array wholesale. Against a cached read, adding one\n * rate restores that snapshot's whole price table, and a zone that\n * disappears with it means `resolveShippingRates` matches nothing, which\n * is a checkout offering no shipping option at all.\n */\n fromCache: storeFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const [draft, setDraft] = useState<CommerceModel.ShippingSettings | null>(null)\n const current: CommerceModel.ShippingSettings = draft ?? store?.shipping ?? {}\n /*\n * Live carrier rates (AGL-3612) are offered only where a plugin quotes\n * carriers for this site — the server asks core's quoter seam. A rate\n * already saved as `carrier` stays editable either way, so a merchant can\n * still see and change what checkout falls back to.\n */\n const { data: user } = useUser()\n const [carrier, setCarrier] = useState<{\n available: boolean\n services: Array<{ serviceKey: string; label: string }>\n }>({ available: false, services: [] })\n useEffect(() => {\n let live = true\n authorizedFetch(user, `/api/commerce/shipping/carrier-rates?hostId=${encodeURIComponent(hostId)}`)\n .then((response) => (response.ok ? response.json() : null))\n .then((answer) => {\n if (live && answer) setCarrier({ available: Boolean(answer.available), services: answer.services ?? [] })\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n const update = (patch: Partial<CommerceModel.ShippingSettings>) =>\n setDraft({ ...current, ...patch })\n\n const updateZone = (\n index: number,\n patch: Partial<CommerceModel.ShippingZone> | null,\n ) => {\n const zones = [...(current.zones ?? [])]\n if (patch === null) {\n const removed = zones.splice(index, 1)[0]\n update({\n zones,\n rates: (current.rates ?? []).filter(\n (rate) => rate.zoneId !== removed?.id,\n ),\n })\n return\n }\n zones[index] = {\n id: Aglyn.createResourceUid(),\n name: '',\n countries: [],\n ...zones[index],\n ...patch,\n }\n update({ zones })\n }\n\n const updateRate = (\n index: number,\n patch: Partial<CommerceModel.ShippingRate> | null,\n ) => {\n const rates = [...(current.rates ?? [])]\n if (patch === null) rates.splice(index, 1)\n else\n rates[index] = {\n id: Aglyn.createResourceUid(),\n zoneId: current.zones?.[0]?.id ?? '',\n name: '',\n kind: 'flat',\n ...rates[index],\n ...patch,\n }\n update({ rates })\n }\n\n const handleSave = useCallback(async () => {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * No create path to exempt: this is a FIXED document path, so the empty\n * `{}` that `store?.shipping ?? {}` yields when the cache has never seen\n * the map is not the harmless blank a fresh uid would be — it replaces\n * the real zones and rates with nothing.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'shipping settings',\n unreadable: storeStatus === 'error',\n fromCache: storeFromCache,\n },\n async () => {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'settings', 'store'),\n { shipping: current },\n { merge: true },\n )\n },\n )\n // Before `setDraft(null)`, so a refusal keeps every zone and rate that\n // was typed on screen.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setDraft(null)\n enqueueSnackbar('Shipping settings saved', {\n variant: 'success',\n persist: false,\n })\n }, [current, firestore, hostId, enqueueSnackbar, storeStatus, storeFromCache])\n\n /**\n * Recomputed from `current` on every render, so the block answers for what\n * is typed rather than for what was last saved — a merchant deleting the\n * zone that covered France should see France go uncovered before they\n * commit it, not after.\n */\n const coverage = CommerceModel.summarizeShippingCoverage(current)\n const countryNames = (codes: readonly string[]) =>\n codes\n .map((code) => CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code)\n .join(', ')\n /**\n * The one-click half of the fix: a zone claiming everywhere the merchant\n * has not named. It adds the ZONE only — a rate on it prices the parcel,\n * and choosing that price is the merchant's call, not ours. A rate row\n * defaulted here would post at whatever we picked, and a blank flat rate\n * resolves to free.\n */\n const addRestOfWorldZone = () =>\n updateZone(current.zones?.length ?? 0, {\n name: 'Rest of world',\n countries: ['*'],\n })\n\n const usd = (cents: number | undefined) =>\n cents == null ? '' : String(cents / 100)\n const cents = (raw: string) =>\n raw.trim() === '' ? undefined : Math.round(Number(raw) * 100)\n\n return (\n <CardDisplay\n header={'Shipping'}\n help={pluginDocsHelp('commerce', { anchor: '#shipping--taxes' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">{'Zones'}</Typography>\n {(current.zones ?? []).map((zone, index) => (\n <Stack key={zone.id} direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone name\"\n value={zone.name}\n onChange={(event) =>\n updateZone(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ width: 180 }}\n />\n <TextField\n label=\"Countries\"\n value={zone.countries.join(', ')}\n onChange={(event) =>\n updateZone(index, {\n countries: event.target.value\n .split(',')\n .map((code) => code.trim().toUpperCase())\n .filter(Boolean),\n })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"US, CA — or * for everywhere else\"\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateZone(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateZone(current.zones?.length ?? 0, {})}\n >\n {'Add zone'}\n </Button>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Rates'}</Typography>\n {(current.rates ?? []).map((rate, index) => (\n <Stack key={rate.id} spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone\"\n value={rate.zoneId}\n onChange={(event) =>\n updateRate(index, { zoneId: event.target.value })\n }\n size=\"small\"\n select\n sx={{ width: 150 }}\n >\n {(current.zones ?? []).map((zone) => (\n <MenuItem key={zone.id} value={zone.id}>\n {zone.name || zone.countries.join(',')}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Name\"\n value={rate.name}\n onChange={(event) =>\n updateRate(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Standard\"\n /**\n * A rate with no name is DROPPED at checkout, silently\n * (AGL-1791): Stripe requires a `display_name`, so\n * `resolveCheckoutShippingOptions` skips the row rather than\n * emit a session Stripe would reject. Saved blank it looks\n * like a priced rate on this page and is not one anywhere\n * else, which is the same invisibility the coverage block\n * below exists to end — said on the row that causes it,\n * because the block can only report the destination it costs.\n */\n error={!rate.name.trim()}\n helperText={\n rate.name.trim() ? undefined : 'Needed — checkout drops it'\n }\n />\n <TextField\n label=\"Type\"\n value={rate.kind}\n onChange={(event) =>\n updateRate(index, {\n kind: event.target.value as CommerceModel.ShippingRate['kind'],\n })\n }\n size=\"small\"\n select\n sx={{ width: 170 }}\n >\n <MenuItem value=\"flat\">{'Flat'}</MenuItem>\n <MenuItem value=\"free_over\">{'Free over subtotal'}</MenuItem>\n <MenuItem value=\"price_tiers\">{'Subtotal tiers'}</MenuItem>\n <MenuItem value=\"weight_tiers\">{'Weight tiers'}</MenuItem>\n {carrier.available || rate.kind === 'carrier' ? (\n <MenuItem value=\"carrier\">{'Carrier rates'}</MenuItem>\n ) : null}\n </TextField>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateRate(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n {rate.kind === 'carrier' ? (\n <CarrierRateFields\n rate={rate}\n services={carrier.services}\n available={carrier.available}\n fallbacks={(current.rates ?? []).filter(\n (other) =>\n other.id !== rate.id &&\n other.zoneId === rate.zoneId &&\n other.kind !== 'carrier',\n )}\n onChange={(patch) => updateRate(index, patch)}\n />\n ) : rate.kind === 'flat' || rate.kind === 'free_over' ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Price ($)\"\n value={usd(rate.amountCents)}\n onChange={(event) =>\n updateRate(index, { amountCents: cents(event.target.value) })\n }\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n {rate.kind === 'free_over' ? (\n <TextField\n label=\"Free at subtotal ($)\"\n value={usd(rate.freeOverCents)}\n onChange={(event) =>\n updateRate(index, {\n freeOverCents: cents(event.target.value),\n })\n }\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n ) : null}\n </Stack>\n ) : (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {(rate.tiers ?? []).map((tier, tierIndex) => (\n <Stack key={tierIndex} direction=\"row\" spacing={1}>\n <TextField\n label={\n rate.kind === 'weight_tiers'\n ? 'Up to (g)'\n : 'Up to subtotal ($)'\n }\n value={\n rate.kind === 'weight_tiers'\n ? tier.upTo\n : tier.upTo / 100\n }\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n const value = Number(event.target.value) || 0\n tiers[tierIndex] = {\n ...tier,\n upTo:\n rate.kind === 'weight_tiers'\n ? Math.round(value)\n : Math.round(value * 100),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Price ($)\"\n value={tier.amountCents / 100}\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n tiers[tierIndex] = {\n ...tier,\n amountCents: Math.round(\n (Number(event.target.value) || 0) * 100,\n ),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n updateRate(index, {\n tiers: (rate.tiers ?? []).filter(\n (_item, itemIndex) => itemIndex !== tierIndex,\n ),\n })\n }\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n updateRate(index, {\n tiers: [\n ...(rate.tiers ?? []),\n { upTo: 0, amountCents: 0 },\n ],\n })\n }\n >\n {'Add tier'}\n </Button>\n </Stack>\n )}\n </Stack>\n ))}\n <Button\n size=\"small\"\n disabled={(current.zones?.length ?? 0) === 0}\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateRate(current.rates?.length ?? 0, {})}\n >\n {'Add rate'}\n </Button>\n\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(current.localPickup)}\n onChange={(event) =>\n update({ localPickup: event.target.checked })\n }\n />\n }\n label=\"Offer free local pickup\"\n />\n {/*\n What the toggle does NOT do, said on the toggle (AGL-2325). It\n used to silently switch off every \"we don't ship there\" refusal,\n because pickup resolved for every destination and checkout read a\n non-empty list as coverage. It no longer does — but a merchant\n reading one line of label still has to be told that turning\n collection on is not the same as adding a destination.\n */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'A free collection choice at checkout. It does not widen where ' +\n 'you ship — a destination none of your rates reaches is still ' +\n 'turned away. To let buyers choose WHERE to collect, with hours ' +\n 'and a ready-for-pickup email, turn on pickup for a location ' +\n 'under Inventory locations; this choice then gives way to it.'\n }\n </Typography>\n </Stack>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Coverage'}</Typography>\n {coverage.unserved.length > 0 ? (\n <Alert\n severity=\"warning\"\n action={\n coverage.hasRestOfWorldZone ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={addRestOfWorldZone}\n >\n {'Add rest-of-world zone'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {`Checkout turns away ${coverage.unserved.length} of the ${CommerceModel.CHECKOUT_SHIPPING_COUNTRIES.length} destinations it collects addresses for`}\n </AlertTitle>\n {`No rate reaches ${countryNames(coverage.unserved)}, so an order shipping there is refused rather than posted for free. `}\n {coverage.offersLocalPickup\n ? 'Local pickup does not cover them: it is a collection choice, not a rate, so it is offered where checkout completes and closes no gap here. '\n : ''}\n {coverage.hasRestOfWorldZone\n ? 'A rest-of-world zone is already saved, so what those destinations are missing is a rate on the zone that covers them.'\n : 'Give them a zone with a rate, or add a rest-of-world zone and price one on it.'}\n </Alert>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {coverage.shipsNowhere\n ? coverage.offersLocalPickup\n ? 'No rate reaches any destination, so local pickup is the only delivery choice checkout offers. Nobody is turned away and no shipping is charged.'\n : 'No rate reaches any destination, so checkout charges no shipping and turns nobody away.'\n : `Checkout can price shipping to every destination it collects an address for: ${countryNames(coverage.served)}.`}\n </Typography>\n )}\n {coverage.asksDestination ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'Rates differ by destination, so checkout asks the shopper where the parcel is going before it can price one.'\n }\n </Typography>\n ) : null}\n\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!draft}\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save shipping settings'}\n </Button>\n </Stack>\n </CardDisplay>\n )\n}\nShippingSettingsCard.displayName = 'ShippingSettingsCard'\n\nexport default ShippingSettingsCard\n\ninterface CarrierRateFieldsProps {\n rate: CommerceModel.ShippingRate\n services: Array<{ serviceKey: string; label: string }>\n available: boolean\n /** The zone's other rates, any of which checkout may fall back to. */\n fallbacks: CommerceModel.ShippingRate[]\n onChange: (patch: Partial<CommerceModel.ShippingRate>) => void\n}\n\n/**\n * A carrier rate's options (AGL-3612): which services checkout offers, what\n * is added to each quote, and the zone's rate offered when no live quote is.\n */\nfunction CarrierRateFields(props: CarrierRateFieldsProps) {\n const { rate, services, available, fallbacks, onChange } = props\n const options = rate.carrier ?? {}\n const set = (patch: Partial<CommerceModel.CarrierRateSettings>) =>\n onChange({ carrier: { ...options, ...patch } })\n const picked = new Set(options.services ?? [])\n return (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {!available ? (\n <Alert severity=\"info\">\n {'Live carrier rates are not available on this site right now, so checkout offers the fallback rate.'}\n </Alert>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Shoppers see each carrier’s price for their address. Leave every service unticked to offer all of them.'}\n </Typography>\n <Stack direction=\"row\" sx={{ flexWrap: 'wrap', columnGap: 2 }}>\n {services.map((service) => (\n <FormControlLabel\n key={service.serviceKey}\n control={\n <Switch\n size=\"small\"\n checked={picked.has(service.serviceKey)}\n onChange={(event) =>\n set({\n services: event.target.checked\n ? [...picked, service.serviceKey]\n : [...picked].filter((key) => key !== service.serviceKey),\n })\n }\n />\n }\n label={service.label}\n />\n ))}\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Markup (%)\"\n value={options.markupPct ?? ''}\n onChange={(event) => {\n const pct = Number(event.target.value)\n set({ markupPct: Number.isFinite(pct) && pct > 0 ? Math.min(pct, 500) : 0 })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Handling fee ($)\"\n value={options.handlingCents ? options.handlingCents / 100 : ''}\n onChange={(event) => {\n const dollars = Number(event.target.value)\n set({ handlingCents: Number.isFinite(dollars) && dollars > 0 ? Math.round(dollars * 100) : 0 })\n }}\n size=\"small\"\n sx={{ width: 150 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n select\n label=\"If no live rate\"\n value={options.fallbackRateId ?? ''}\n onChange={(event) => set({ fallbackRateId: event.target.value })}\n size=\"small\"\n sx={{ minWidth: 200 }}\n error={!options.fallbackRateId}\n helperText={\n options.fallbackRateId ? undefined : 'Pick a rate of this zone, or checkout can’t price without a quote'\n }\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {fallbacks.map((fallback) => (\n <MenuItem key={fallback.id} value={fallback.id}>\n {fallback.name || 'Unnamed rate'}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n </Stack>\n )\n}\n"],"names":["Aglyn","CommerceModel","CardDisplay","useSnackbar","Alert","AlertTitle","Button","Divider","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","authorizedFetch","doc","setDoc","useCallback","useEffect","useState","useFirestore","useUser","useFirestoreDoc","writeGuardedBySeed","pluginDocsHelp","ShippingSettingsCard","props","draft","current","hostId","firestore","enqueueSnackbar","data","store","status","storeStatus","fromCache","storeFromCache","setDraft","shipping","user","carrier","setCarrier","available","services","live","encodeURIComponent","then","response","ok","json","answer","Boolean","catch","undefined","update","patch","updateZone","index","zones","removed","splice","rates","filter","rate","zoneId","id","createResourceUid","name","countries","updateRate","kind","handleSave","verdict","subject","unreadable","merge","message","variant","persist","coverage","summarizeShippingCoverage","countryNames","codes","map","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","join","addRestOfWorldZone","length","usd","cents","String","raw","trim","Math","round","Number","header","help","anchor","contentGutterX","contentGutterY","spacing","zone","direction","label","value","onChange","event","target","size","sx","width","split","toUpperCase","flex","placeholder","color","onClick","alignSelf","select","error","helperText","CarrierRateFields","fallbacks","other","amountCents","slotProps","htmlInput","inputMode","freeOverCents","pl","tiers","tier","tierIndex","upTo","_item","itemIndex","disabled","control","checked","localPickup","unserved","severity","action","hasRestOfWorldZone","CHECKOUT_SHIPPING_COUNTRIES","offersLocalPickup","shipsNowhere","served","asksDestination","displayName","options","set","picked","Set","flexWrap","columnGap","service","has","serviceKey","key","markupPct","pct","isFinite","min","handlingCents","dollars","fallbackRateId","minWidth","fallback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shipping-settings-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport * as CommerceModel from '../../model'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n AlertTitle,\n Button,\n Divider,\n FormControlLabel,\n MenuItem,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { doc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore, useUser } from '@aglyn/tenant-feature-instance'\nimport {\n useFirestoreDoc,\n writeGuardedBySeed,\n} from '@aglyn/tenant-feature-instance'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\n\nexport interface ShippingSettingsCardProps {\n hostId: string\n}\n\n/**\n * Shipping settings (AGL-288): zones (countries, '*' = rest of world)\n * and rates (flat / free-over / subtotal or weight tiers), stored on\n * `hosts/{hostId}/settings/store` under `shipping`. Cart checkout (AGL-1707)\n * and buy-now (AGL-1720) both read this document and declare the rates as\n * Stripe `shipping_options` (`planCheckoutShipping`) — until those landed\n * nothing read it, and everything saved here was charged to nobody. Buy-now\n * resolves only for physical one-time sales; draft orders and POS still\n * resolve no shipping at all.\n *\n * WHAT A ZONE LAYOUT COSTS THE SHOPPER (AGL-1721). A session's rates are\n * charged as picked and never re-checked against the address, so the checkout\n * can only offer rates that hold for every country it will ship to. Saving\n * zones whose rates DIFFER therefore makes the storefront ask the shopper\n * where the parcel is going before it can price one, and a destination no\n * zone covers is refused outright rather than shipped for free. One zone, or\n * one '*' zone, needs neither: the shopper is asked nothing.\n *\n * AND THE MERCHANT HAS TO BE ABLE TO SEE IT (AGL-1791). Both consequences\n * were invisible here: a merchant saved zones and the checkouts they refused\n * were refused with nothing on this page saying so, which is a support ticket\n * from someone who did nothing wrong. The coverage block reads\n * `summarizeShippingCoverage` off the same `current` object the save writes,\n * so it moves as the form is typed rather than after a round trip.\n *\n * It escalates to a warning ONLY for a merchant who priced some destinations\n * and not others. A merchant whose rates reach nowhere is not misconfigured —\n * that store charges no shipping and refuses nobody — and warning them would\n * nag every store selling downloads.\n */\nexport function ShippingSettingsCard(props: ShippingSettingsCardProps) {\n const { hostId } = props\n const firestore = useFirestore()\n const { enqueueSnackbar } = useSnackbar()\n const {\n data: store,\n status: storeStatus,\n /**\n * The settings doc this form is seeded from is unconfirmed by the server\n * (AGL-1358). `current` is `draft ?? store?.shipping ?? {}` and the save\n * writes `{shipping: current}`, so `merge: true` protects the doc's other\n * maps and nothing inside this one — `zones` and `rates` are arrays, and\n * a merge replaces an array wholesale. Against a cached read, adding one\n * rate restores that snapshot's whole price table, and a zone that\n * disappears with it means `resolveShippingRates` matches nothing, which\n * is a checkout offering no shipping option at all.\n */\n fromCache: storeFromCache,\n } = useFirestoreDoc<any>(\n () => doc(firestore, 'hosts', hostId, 'settings', 'store'),\n [firestore, hostId],\n )\n const [draft, setDraft] = useState<CommerceModel.ShippingSettings | null>(null)\n const current: CommerceModel.ShippingSettings = draft ?? store?.shipping ?? {}\n /*\n * Live carrier rates (AGL-3612) are offered only where a plugin quotes\n * carriers for this site — the server asks core's quoter seam. A rate\n * already saved as `carrier` stays editable either way, so a merchant can\n * still see and change what checkout falls back to.\n */\n const { data: user } = useUser()\n const [carrier, setCarrier] = useState<{\n available: boolean\n services: Array<{ serviceKey: string; label: string }>\n }>({ available: false, services: [] })\n useEffect(() => {\n let live = true\n authorizedFetch(user, `/api/commerce/shipping/carrier-rates?hostId=${encodeURIComponent(hostId)}`)\n .then((response) => (response.ok ? response.json() : null))\n .then((answer) => {\n if (live && answer) setCarrier({ available: Boolean(answer.available), services: answer.services ?? [] })\n })\n .catch(() => undefined)\n return () => {\n live = false\n }\n }, [hostId, user])\n const update = (patch: Partial<CommerceModel.ShippingSettings>) =>\n setDraft({ ...current, ...patch })\n\n const updateZone = (\n index: number,\n patch: Partial<CommerceModel.ShippingZone> | null,\n ) => {\n const zones = [...(current.zones ?? [])]\n if (patch === null) {\n const removed = zones.splice(index, 1)[0]\n update({\n zones,\n rates: (current.rates ?? []).filter(\n (rate) => rate.zoneId !== removed?.id,\n ),\n })\n return\n }\n zones[index] = {\n id: Aglyn.createResourceUid(),\n name: '',\n countries: [],\n ...zones[index],\n ...patch,\n }\n update({ zones })\n }\n\n const updateRate = (\n index: number,\n patch: Partial<CommerceModel.ShippingRate> | null,\n ) => {\n const rates = [...(current.rates ?? [])]\n if (patch === null) rates.splice(index, 1)\n else\n rates[index] = {\n id: Aglyn.createResourceUid(),\n zoneId: current.zones?.[0]?.id ?? '',\n name: '',\n kind: 'flat',\n ...rates[index],\n ...patch,\n }\n update({ rates })\n }\n\n const handleSave = useCallback(async () => {\n /**\n * Refuse a save whose seed the server never confirmed (AGL-1358).\n *\n * No create path to exempt: this is a FIXED document path, so the empty\n * `{}` that `store?.shipping ?? {}` yields when the cache has never seen\n * the map is not the harmless blank a fresh uid would be — it replaces\n * the real zones and rates with nothing.\n *\n * The guard WRAPS the write — an early return is a shape you can keep\n * while losing the protection.\n */\n const verdict = await writeGuardedBySeed(\n {\n subject: 'shipping settings',\n unreadable: storeStatus === 'error',\n fromCache: storeFromCache,\n },\n async () => {\n await setDoc(\n doc(firestore, 'hosts', hostId, 'settings', 'store'),\n { shipping: current },\n { merge: true },\n )\n },\n )\n // Before `setDraft(null)`, so a refusal keeps every zone and rate that\n // was typed on screen.\n if (!verdict.ok) {\n return void enqueueSnackbar(verdict.message, {\n variant: 'warning',\n persist: false,\n })\n }\n setDraft(null)\n enqueueSnackbar('Shipping settings saved', {\n variant: 'success',\n persist: false,\n })\n }, [current, firestore, hostId, enqueueSnackbar, storeStatus, storeFromCache])\n\n /**\n * Recomputed from `current` on every render, so the block answers for what\n * is typed rather than for what was last saved — a merchant deleting the\n * zone that covered France should see France go uncovered before they\n * commit it, not after.\n */\n const coverage = CommerceModel.summarizeShippingCoverage(current)\n const countryNames = (codes: readonly string[]) =>\n codes\n .map((code) => CommerceModel.CHECKOUT_SHIPPING_COUNTRY_NAMES[code] ?? code)\n .join(', ')\n /**\n * The one-click half of the fix: a zone claiming everywhere the merchant\n * has not named. It adds the ZONE only — a rate on it prices the parcel,\n * and choosing that price is the merchant's call, not ours. A rate row\n * defaulted here would post at whatever we picked, and a blank flat rate\n * resolves to free.\n */\n const addRestOfWorldZone = () =>\n updateZone(current.zones?.length ?? 0, {\n name: 'Rest of world',\n countries: ['*'],\n })\n\n const usd = (cents: number | undefined) =>\n cents == null ? '' : String(cents / 100)\n const cents = (raw: string) =>\n raw.trim() === '' ? undefined : Math.round(Number(raw) * 100)\n\n return (\n <CardDisplay\n header={'Shipping'}\n help={pluginDocsHelp('shipping', { anchor: '#zones-and-rates' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">{'Zones'}</Typography>\n {(current.zones ?? []).map((zone, index) => (\n <Stack key={zone.id} direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone name\"\n value={zone.name}\n onChange={(event) =>\n updateZone(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ width: 180 }}\n />\n <TextField\n label=\"Countries\"\n value={zone.countries.join(', ')}\n onChange={(event) =>\n updateZone(index, {\n countries: event.target.value\n .split(',')\n .map((code) => code.trim().toUpperCase())\n .filter(Boolean),\n })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"US, CA — or * for everywhere else\"\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateZone(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateZone(current.zones?.length ?? 0, {})}\n >\n {'Add zone'}\n </Button>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Rates'}</Typography>\n {(current.rates ?? []).map((rate, index) => (\n <Stack key={rate.id} spacing={1}>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Zone\"\n value={rate.zoneId}\n onChange={(event) =>\n updateRate(index, { zoneId: event.target.value })\n }\n size=\"small\"\n select\n sx={{ width: 150 }}\n >\n {(current.zones ?? []).map((zone) => (\n <MenuItem key={zone.id} value={zone.id}>\n {zone.name || zone.countries.join(',')}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n label=\"Name\"\n value={rate.name}\n onChange={(event) =>\n updateRate(index, { name: event.target.value })\n }\n size=\"small\"\n sx={{ flex: 1 }}\n placeholder=\"Standard\"\n /**\n * A rate with no name is DROPPED at checkout, silently\n * (AGL-1791): Stripe requires a `display_name`, so\n * `resolveCheckoutShippingOptions` skips the row rather than\n * emit a session Stripe would reject. Saved blank it looks\n * like a priced rate on this page and is not one anywhere\n * else, which is the same invisibility the coverage block\n * below exists to end — said on the row that causes it,\n * because the block can only report the destination it costs.\n */\n error={!rate.name.trim()}\n helperText={\n rate.name.trim() ? undefined : 'Needed — checkout drops it'\n }\n />\n <TextField\n label=\"Type\"\n value={rate.kind}\n onChange={(event) =>\n updateRate(index, {\n kind: event.target.value as CommerceModel.ShippingRate['kind'],\n })\n }\n size=\"small\"\n select\n sx={{ width: 170 }}\n >\n <MenuItem value=\"flat\">{'Flat'}</MenuItem>\n <MenuItem value=\"free_over\">{'Free over subtotal'}</MenuItem>\n <MenuItem value=\"price_tiers\">{'Subtotal tiers'}</MenuItem>\n <MenuItem value=\"weight_tiers\">{'Weight tiers'}</MenuItem>\n {carrier.available || rate.kind === 'carrier' ? (\n <MenuItem value=\"carrier\">{'Carrier rates'}</MenuItem>\n ) : null}\n </TextField>\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() => updateRate(index, null)}\n >\n {'✕'}\n </Button>\n </Stack>\n {rate.kind === 'carrier' ? (\n <CarrierRateFields\n rate={rate}\n services={carrier.services}\n available={carrier.available}\n fallbacks={(current.rates ?? []).filter(\n (other) =>\n other.id !== rate.id &&\n other.zoneId === rate.zoneId &&\n other.kind !== 'carrier',\n )}\n onChange={(patch) => updateRate(index, patch)}\n />\n ) : rate.kind === 'flat' || rate.kind === 'free_over' ? (\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Price ($)\"\n value={usd(rate.amountCents)}\n onChange={(event) =>\n updateRate(index, { amountCents: cents(event.target.value) })\n }\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n {rate.kind === 'free_over' ? (\n <TextField\n label=\"Free at subtotal ($)\"\n value={usd(rate.freeOverCents)}\n onChange={(event) =>\n updateRate(index, {\n freeOverCents: cents(event.target.value),\n })\n }\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n ) : null}\n </Stack>\n ) : (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {(rate.tiers ?? []).map((tier, tierIndex) => (\n <Stack key={tierIndex} direction=\"row\" spacing={1}>\n <TextField\n label={\n rate.kind === 'weight_tiers'\n ? 'Up to (g)'\n : 'Up to subtotal ($)'\n }\n value={\n rate.kind === 'weight_tiers'\n ? tier.upTo\n : tier.upTo / 100\n }\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n const value = Number(event.target.value) || 0\n tiers[tierIndex] = {\n ...tier,\n upTo:\n rate.kind === 'weight_tiers'\n ? Math.round(value)\n : Math.round(value * 100),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 160 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Price ($)\"\n value={tier.amountCents / 100}\n onChange={(event) => {\n const tiers = [...(rate.tiers ?? [])]\n tiers[tierIndex] = {\n ...tier,\n amountCents: Math.round(\n (Number(event.target.value) || 0) * 100,\n ),\n }\n updateRate(index, { tiers })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <Button\n size=\"small\"\n color=\"error\"\n onClick={() =>\n updateRate(index, {\n tiers: (rate.tiers ?? []).filter(\n (_item, itemIndex) => itemIndex !== tierIndex,\n ),\n })\n }\n >\n {'✕'}\n </Button>\n </Stack>\n ))}\n <Button\n size=\"small\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={() =>\n updateRate(index, {\n tiers: [\n ...(rate.tiers ?? []),\n { upTo: 0, amountCents: 0 },\n ],\n })\n }\n >\n {'Add tier'}\n </Button>\n </Stack>\n )}\n </Stack>\n ))}\n <Button\n size=\"small\"\n disabled={(current.zones?.length ?? 0) === 0}\n sx={{ alignSelf: 'flex-start' }}\n onClick={() => updateRate(current.rates?.length ?? 0, {})}\n >\n {'Add rate'}\n </Button>\n\n <Stack spacing={0}>\n <FormControlLabel\n control={\n <Switch\n size=\"small\"\n checked={Boolean(current.localPickup)}\n onChange={(event) =>\n update({ localPickup: event.target.checked })\n }\n />\n }\n label=\"Offer free local pickup\"\n />\n {/*\n What the toggle does NOT do, said on the toggle (AGL-2325). It\n used to silently switch off every \"we don't ship there\" refusal,\n because pickup resolved for every destination and checkout read a\n non-empty list as coverage. It no longer does — but a merchant\n reading one line of label still has to be told that turning\n collection on is not the same as adding a destination.\n */}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'A free collection choice at checkout. It does not widen where ' +\n 'you ship — a destination none of your rates reaches is still ' +\n 'turned away. To let buyers choose WHERE to collect, with hours ' +\n 'and a ready-for-pickup email, turn on pickup for a location ' +\n 'under Inventory locations; this choice then gives way to it.'\n }\n </Typography>\n </Stack>\n\n <Divider />\n <Typography variant=\"subtitle2\">{'Coverage'}</Typography>\n {coverage.unserved.length > 0 ? (\n <Alert\n severity=\"warning\"\n action={\n coverage.hasRestOfWorldZone ? undefined : (\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={addRestOfWorldZone}\n >\n {'Add rest-of-world zone'}\n </Button>\n )\n }\n >\n <AlertTitle>\n {`Checkout turns away ${coverage.unserved.length} of the ${CommerceModel.CHECKOUT_SHIPPING_COUNTRIES.length} destinations it collects addresses for`}\n </AlertTitle>\n {`No rate reaches ${countryNames(coverage.unserved)}, so an order shipping there is refused rather than posted for free. `}\n {coverage.offersLocalPickup\n ? 'Local pickup does not cover them: it is a collection choice, not a rate, so it is offered where checkout completes and closes no gap here. '\n : ''}\n {coverage.hasRestOfWorldZone\n ? 'A rest-of-world zone is already saved, so what those destinations are missing is a rate on the zone that covers them.'\n : 'Give them a zone with a rate, or add a rest-of-world zone and price one on it.'}\n </Alert>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {coverage.shipsNowhere\n ? coverage.offersLocalPickup\n ? 'No rate reaches any destination, so local pickup is the only delivery choice checkout offers. Nobody is turned away and no shipping is charged.'\n : 'No rate reaches any destination, so checkout charges no shipping and turns nobody away.'\n : `Checkout can price shipping to every destination it collects an address for: ${countryNames(coverage.served)}.`}\n </Typography>\n )}\n {coverage.asksDestination ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {\n 'Rates differ by destination, so checkout asks the shopper where the parcel is going before it can price one.'\n }\n </Typography>\n ) : null}\n\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n disabled={!draft}\n onClick={handleSave}\n sx={{ alignSelf: 'flex-start' }}\n >\n {'Save shipping settings'}\n </Button>\n </Stack>\n </CardDisplay>\n )\n}\nShippingSettingsCard.displayName = 'ShippingSettingsCard'\n\nexport default ShippingSettingsCard\n\ninterface CarrierRateFieldsProps {\n rate: CommerceModel.ShippingRate\n services: Array<{ serviceKey: string; label: string }>\n available: boolean\n /** The zone's other rates, any of which checkout may fall back to. */\n fallbacks: CommerceModel.ShippingRate[]\n onChange: (patch: Partial<CommerceModel.ShippingRate>) => void\n}\n\n/**\n * A carrier rate's options (AGL-3612): which services checkout offers, what\n * is added to each quote, and the zone's rate offered when no live quote is.\n */\nfunction CarrierRateFields(props: CarrierRateFieldsProps) {\n const { rate, services, available, fallbacks, onChange } = props\n const options = rate.carrier ?? {}\n const set = (patch: Partial<CommerceModel.CarrierRateSettings>) =>\n onChange({ carrier: { ...options, ...patch } })\n const picked = new Set(options.services ?? [])\n return (\n <Stack spacing={1} sx={{ pl: 2 }}>\n {!available ? (\n <Alert severity=\"info\">\n {'Live carrier rates are not available on this site right now, so checkout offers the fallback rate.'}\n </Alert>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Shoppers see each carrier’s price for their address. Leave every service unticked to offer all of them.'}\n </Typography>\n <Stack direction=\"row\" sx={{ flexWrap: 'wrap', columnGap: 2 }}>\n {services.map((service) => (\n <FormControlLabel\n key={service.serviceKey}\n control={\n <Switch\n size=\"small\"\n checked={picked.has(service.serviceKey)}\n onChange={(event) =>\n set({\n services: event.target.checked\n ? [...picked, service.serviceKey]\n : [...picked].filter((key) => key !== service.serviceKey),\n })\n }\n />\n }\n label={service.label}\n />\n ))}\n </Stack>\n <Stack direction=\"row\" spacing={1}>\n <TextField\n label=\"Markup (%)\"\n value={options.markupPct ?? ''}\n onChange={(event) => {\n const pct = Number(event.target.value)\n set({ markupPct: Number.isFinite(pct) && pct > 0 ? Math.min(pct, 500) : 0 })\n }}\n size=\"small\"\n sx={{ width: 120 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n label=\"Handling fee ($)\"\n value={options.handlingCents ? options.handlingCents / 100 : ''}\n onChange={(event) => {\n const dollars = Number(event.target.value)\n set({ handlingCents: Number.isFinite(dollars) && dollars > 0 ? Math.round(dollars * 100) : 0 })\n }}\n size=\"small\"\n sx={{ width: 150 }}\n slotProps={{ htmlInput: { inputMode: 'decimal' } }}\n />\n <TextField\n select\n label=\"If no live rate\"\n value={options.fallbackRateId ?? ''}\n onChange={(event) => set({ fallbackRateId: event.target.value })}\n size=\"small\"\n sx={{ minWidth: 200 }}\n error={!options.fallbackRateId}\n helperText={\n options.fallbackRateId ? undefined : 'Pick a rate of this zone, or checkout can’t price without a quote'\n }\n >\n <MenuItem value=\"\">{'None'}</MenuItem>\n {fallbacks.map((fallback) => (\n <MenuItem key={fallback.id} value={fallback.id}>\n {fallback.name || 'Unnamed rate'}\n </MenuItem>\n ))}\n </TextField>\n </Stack>\n </Stack>\n )\n}\n"],"names":["Aglyn","CommerceModel","CardDisplay","useSnackbar","Alert","AlertTitle","Button","Divider","FormControlLabel","MenuItem","Stack","Switch","TextField","Typography","authorizedFetch","doc","setDoc","useCallback","useEffect","useState","useFirestore","useUser","useFirestoreDoc","writeGuardedBySeed","pluginDocsHelp","ShippingSettingsCard","props","draft","current","hostId","firestore","enqueueSnackbar","data","store","status","storeStatus","fromCache","storeFromCache","setDraft","shipping","user","carrier","setCarrier","available","services","live","encodeURIComponent","then","response","ok","json","answer","Boolean","catch","undefined","update","patch","updateZone","index","zones","removed","splice","rates","filter","rate","zoneId","id","createResourceUid","name","countries","updateRate","kind","handleSave","verdict","subject","unreadable","merge","message","variant","persist","coverage","summarizeShippingCoverage","countryNames","codes","map","code","CHECKOUT_SHIPPING_COUNTRY_NAMES","join","addRestOfWorldZone","length","usd","cents","String","raw","trim","Math","round","Number","header","help","anchor","contentGutterX","contentGutterY","spacing","zone","direction","label","value","onChange","event","target","size","sx","width","split","toUpperCase","flex","placeholder","color","onClick","alignSelf","select","error","helperText","CarrierRateFields","fallbacks","other","amountCents","slotProps","htmlInput","inputMode","freeOverCents","pl","tiers","tier","tierIndex","upTo","_item","itemIndex","disabled","control","checked","localPickup","unserved","severity","action","hasRestOfWorldZone","CHECKOUT_SHIPPING_COUNTRIES","offersLocalPickup","shipsNowhere","served","asksDestination","displayName","options","set","picked","Set","flexWrap","columnGap","service","has","serviceKey","key","markupPct","pct","isFinite","min","handlingCents","dollars","fallbackRateId","minWidth","fallback"],"mappings":"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"}
|
|
@@ -192,7 +192,7 @@ const EMPTY_KEYS = {
|
|
|
192
192
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
193
193
|
header: 'ShippingEasy',
|
|
194
194
|
help: pluginDocsHelp('shippingEasy', {
|
|
195
|
-
|
|
195
|
+
anchor: '#connect'
|
|
196
196
|
}),
|
|
197
197
|
HeaderProps: {
|
|
198
198
|
action: actions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shippingeasy-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { CopyField } from '@aglyn/shared-ui-jsx/components/copy-field.component'\nimport ScrollTable from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { StatusChip } from '@aglyn/shared-ui-jsx/components/status-chip.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Stack,\n TableBody,\n TableCell,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useState } from 'react'\n\nexport interface ShippingEasyCardProps {\n hostId: string\n}\n\n/** What the connectors route answers about a ShippingEasy connection. */\ninterface ConnectionStatus {\n /** Whether this deployment can keep an API secret at all. */\n available?: boolean\n connected: boolean\n apiKeyEnding?: string\n storeApiKeyEnding?: string\n createdAtMs?: number\n updatedAtMs?: number\n lastPushAtMs?: number\n lastCallbackAtMs?: number\n lastError?: { message: string; atMs: number; orderNumber?: string } | null\n sync?: { sent: number; already: number; failed: number; more: boolean }\n}\n\n/** The address ShippingEasy posts shipments to: this console's own origin, never a guess. */\nexport function shippingEasyCallbackUrl(hostId: string, origin?: string): string {\n const base = origin ?? (typeof window === 'undefined' ? '' : window.location.origin)\n return `${base}/api/commerce/shippingeasy/${encodeURIComponent(hostId)}`\n}\n\n/** What one \"Send open orders\" did, in a sentence. */\nexport function describeShippingEasySync(sync: NonNullable<ConnectionStatus['sync']>): string {\n const parts: string[] = []\n parts.push(sync.sent === 1 ? '1 order sent to ShippingEasy' : `${sync.sent} orders sent to ShippingEasy`)\n if (sync.already) parts.push(`${sync.already} already there`)\n if (sync.failed) parts.push(`${sync.failed} not sent`)\n return `${parts.join(', ')}.${sync.more ? ' More open orders are waiting: select Send open orders again.' : ''}`\n}\n\nconst when = (ms: number | undefined): string =>\n ms ? new Date(ms).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' }) : 'Not yet'\n\n/** One fixed fact of the connection: a label and its value. */\nfunction FactRow(props: { label: string; value: string }) {\n return (\n <TableRow>\n <TableCell component=\"th\" scope=\"row\">\n {props.label}\n </TableCell>\n <TableCell>{props.value}</TableCell>\n </TableRow>\n )\n}\n\nconst EMPTY_KEYS = { apiKey: '', apiSecret: '', storeApiKey: '' }\n\n/**\n * Connect ShippingEasy (AGL-3633): the merchant's API key, API secret and\n * store API key, from their own ShippingEasy account; the callback URL to\n * paste into the API store; send open orders; and disconnect. Paid orders go\n * to ShippingEasy as they are paid, and each label bought there marks the\n * order shipped here. The secret is sealed on the server and never shown\n * again; the card renders nothing on a deployment that cannot seal one.\n */\nexport function ShippingEasyCard(props: ShippingEasyCardProps) {\n const { hostId } = props\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [status, setStatus] = useState<ConnectionStatus | null>(null)\n const [loadError, setLoadError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [editing, setEditing] = useState(false)\n const [keys, setKeys] = useState(EMPTY_KEYS)\n const [confirmDisconnect, setConfirmDisconnect] = useState(false)\n\n const uid = user?.uid\n useEffect(() => {\n if (!uid || !user) return\n let live = true\n authorizedFetch(\n user as never,\n `/api/commerce/shipping-connectors?connector=shippingeasy&hostId=${encodeURIComponent(hostId)}`,\n )\n .then(async (response) => {\n const body = await response.json().catch(() => ({}))\n if (!live) return\n if (!response.ok) return setLoadError(body?.error ?? 'The connection could not be read.')\n setLoadError(null)\n setStatus(body as ConnectionStatus)\n })\n .catch(() => live && setLoadError('The connection could not be read.'))\n return () => {\n live = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `user` changes identity every render; `uid` is what matters.\n }, [uid, hostId])\n\n const act = useCallback(\n async (action: 'connect' | 'sync' | 'disconnect', extra: Record<string, string> = {}): Promise<boolean> => {\n setBusy(true)\n try {\n const response = await authorizedFetch(user as never, '/api/commerce/shipping-connectors', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, connector: 'shippingeasy', action, ...extra }),\n })\n const body = (await response.json().catch(() => ({}))) as ConnectionStatus & { error?: string }\n if (!response.ok) {\n enqueueSnackbar(body?.error ?? 'That did not work. Try again.', { variant: 'error', allowDuplicate: true })\n return false\n }\n setStatus(body)\n if (action === 'disconnect') {\n enqueueSnackbar('ShippingEasy is disconnected. New orders are no longer sent to it.', { variant: 'success' })\n } else if (action === 'sync' && body.sync) {\n enqueueSnackbar(describeShippingEasySync(body.sync), { variant: body.sync.failed ? 'warning' : 'success' })\n } else if (action === 'connect') {\n enqueueSnackbar('ShippingEasy is connected. Paste the callback URL into your ShippingEasy API store.', {\n variant: 'success',\n })\n }\n return true\n } finally {\n setBusy(false)\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n // A deployment with no secret keyring cannot keep a secret: nothing to offer.\n if (status && status.available === false && !status.connected) return null\n\n const connected = Boolean(status?.connected)\n const openKeys = () => {\n setKeys(EMPTY_KEYS)\n setEditing(true)\n }\n const actions = !status ? null : connected ? (\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" disabled={busy} onClick={() => void act('sync')}>\n {'Send open orders'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={openKeys}>\n {'Change keys'}\n </Button>\n <Button size=\"small\" color=\"error\" disabled={busy} onClick={() => setConfirmDisconnect(true)}>\n {'Disconnect'}\n </Button>\n </Stack>\n ) : (\n <Button size=\"small\" variant=\"contained\" disabled={busy} onClick={openKeys}>\n {'Connect ShippingEasy'}\n </Button>\n )\n\n const complete = Boolean(keys.apiKey.trim() && keys.apiSecret.trim() && keys.storeApiKey.trim())\n const save = async () => {\n if (await act('connect', keys)) {\n setEditing(false)\n setKeys(EMPTY_KEYS)\n }\n }\n\n return (\n <CardDisplay\n header={'ShippingEasy'}\n help={pluginDocsHelp('shippingEasy', {\n excerpt:\n `${PLATFORM_BRAND_NAME} sends your paid orders to your ShippingEasy account, and each label you buy there ` +\n 'marks the order shipped and emails your customer the tracking link.',\n })}\n HeaderProps={{ action: actions }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {loadError ? <Alert severity=\"error\">{loadError}</Alert> : null}\n {!connected ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Labeling in ShippingEasy? Connect it with the API key, API secret and store API key from your ' +\n 'ShippingEasy account: each order is sent there as it is paid, and each label you buy there ' +\n 'marks the order shipped here and emails your customer the tracking link. Only a site admin ' +\n 'can connect it.'\n }\n </Typography>\n ) : (\n <>\n <div>\n {status?.lastError ? (\n <StatusChip label=\"Needs attention\" tone=\"warning\" data-testid=\"shippingeasy-status\" />\n ) : (\n <StatusChip label=\"Connected\" tone=\"success\" data-testid=\"shippingeasy-status\" />\n )}\n </div>\n {status?.lastError ? (\n <Alert severity=\"warning\">\n {`${status.lastError.message} (${when(status.lastError.atMs)})`}\n </Alert>\n ) : null}\n <CopyField\n label=\"Callback URL\"\n value={shippingEasyCallbackUrl(hostId)}\n helperText=\"Paste this into your API store's settings in ShippingEasy, so each label marks the order shipped here.\"\n onCopied={() => enqueueSnackbar('Callback URL copied', { variant: 'success', persist: false })}\n onCopyFailed={() => enqueueSnackbar('Select the callback URL and copy it.', { variant: 'info' })}\n />\n <ScrollTable size=\"small\" aria-label=\"ShippingEasy connection\">\n <TableBody>\n <FactRow label=\"API key\" value={status?.apiKeyEnding ? `Ends in ${status.apiKeyEnding}` : ''} />\n <FactRow label=\"Store API key\" value={status?.storeApiKeyEnding ? `Ends in ${status.storeApiKeyEnding}` : ''} />\n <FactRow label=\"Last order sent\" value={when(status?.lastPushAtMs)} />\n <FactRow label=\"Last shipment received\" value={when(status?.lastCallbackAtMs)} />\n </TableBody>\n </ScrollTable>\n </>\n )}\n </Stack>\n <Dialog open={editing} onClose={() => !busy && setEditing(false)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{connected ? 'Change ShippingEasy keys' : 'Connect ShippingEasy'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <DialogContentText>\n {\n 'In ShippingEasy, the API key and API secret are under Settings → API Credentials, and the ' +\n 'store API key is on your API store under Settings → Stores & Orders. The secret is kept ' +\n 'encrypted and is never shown again.'\n }\n </DialogContentText>\n <TextField\n label=\"API key\"\n value={keys.apiKey}\n onChange={(event) => setKeys((current) => ({ ...current, apiKey: event.target.value }))}\n autoComplete=\"off\"\n fullWidth\n required\n />\n <TextField\n label=\"API secret\"\n type=\"password\"\n value={keys.apiSecret}\n onChange={(event) => setKeys((current) => ({ ...current, apiSecret: event.target.value }))}\n autoComplete=\"new-password\"\n fullWidth\n required\n />\n <TextField\n label=\"Store API key\"\n value={keys.storeApiKey}\n onChange={(event) => setKeys((current) => ({ ...current, storeApiKey: event.target.value }))}\n autoComplete=\"off\"\n fullWidth\n required\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button disabled={busy} onClick={() => setEditing(false)}>\n {'Cancel'}\n </Button>\n <Button variant=\"contained\" disabled={busy || !complete} onClick={() => void save()}>\n {connected ? 'Save keys' : 'Connect'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={confirmDisconnect} onClose={() => setConfirmDisconnect(false)}>\n <DialogTitle>{'Disconnect ShippingEasy?'}</DialogTitle>\n <DialogContent>\n <DialogContentText>\n {\n 'New orders stop going to ShippingEasy, and shipments it sends back are refused. Orders ' +\n 'already there stay there, and orders already shipped stay shipped. The keys are deleted ' +\n `from ${PLATFORM_BRAND_NAME}.`\n }\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setConfirmDisconnect(false)}>{'Cancel'}</Button>\n <Button\n color=\"error\"\n disabled={busy}\n onClick={() => {\n setConfirmDisconnect(false)\n void act('disconnect')\n }}\n >\n {'Disconnect'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nShippingEasyCard.displayName = 'ShippingEasyCard'\n\nexport default ShippingEasyCard\n"],"names":["pluginDocsHelp","PLATFORM_BRAND_NAME","CardDisplay","CopyField","ScrollTable","StatusChip","useSnackbar","authorizedFetch","useUser","Alert","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Stack","TableBody","TableCell","TableRow","TextField","Typography","useCallback","useEffect","useState","shippingEasyCallbackUrl","hostId","origin","base","window","location","encodeURIComponent","describeShippingEasySync","sync","parts","push","sent","already","failed","join","more","when","ms","Date","toLocaleString","undefined","dateStyle","timeStyle","FactRow","props","component","scope","label","value","EMPTY_KEYS","apiKey","apiSecret","storeApiKey","ShippingEasyCard","data","user","enqueueSnackbar","status","setStatus","loadError","setLoadError","busy","setBusy","editing","setEditing","keys","setKeys","confirmDisconnect","setConfirmDisconnect","uid","live","then","response","body","json","catch","ok","error","act","action","extra","method","headers","JSON","stringify","connector","variant","allowDuplicate","available","connected","Boolean","openKeys","actions","direction","spacing","size","disabled","onClick","color","complete","trim","save","header","help","excerpt","HeaderProps","contentGutterX","contentGutterY","severity","div","lastError","tone","data-testid","message","atMs","helperText","onCopied","persist","onCopyFailed","aria-label","apiKeyEnding","storeApiKeyEnding","lastPushAtMs","lastCallbackAtMs","open","onClose","fullWidth","maxWidth","sx","pt","onChange","event","current","target","autoComplete","required","type","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shippingeasy-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { CopyField } from '@aglyn/shared-ui-jsx/components/copy-field.component'\nimport ScrollTable from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { StatusChip } from '@aglyn/shared-ui-jsx/components/status-chip.component'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Stack,\n TableBody,\n TableCell,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useState } from 'react'\n\nexport interface ShippingEasyCardProps {\n hostId: string\n}\n\n/** What the connectors route answers about a ShippingEasy connection. */\ninterface ConnectionStatus {\n /** Whether this deployment can keep an API secret at all. */\n available?: boolean\n connected: boolean\n apiKeyEnding?: string\n storeApiKeyEnding?: string\n createdAtMs?: number\n updatedAtMs?: number\n lastPushAtMs?: number\n lastCallbackAtMs?: number\n lastError?: { message: string; atMs: number; orderNumber?: string } | null\n sync?: { sent: number; already: number; failed: number; more: boolean }\n}\n\n/** The address ShippingEasy posts shipments to: this console's own origin, never a guess. */\nexport function shippingEasyCallbackUrl(hostId: string, origin?: string): string {\n const base = origin ?? (typeof window === 'undefined' ? '' : window.location.origin)\n return `${base}/api/commerce/shippingeasy/${encodeURIComponent(hostId)}`\n}\n\n/** What one \"Send open orders\" did, in a sentence. */\nexport function describeShippingEasySync(sync: NonNullable<ConnectionStatus['sync']>): string {\n const parts: string[] = []\n parts.push(sync.sent === 1 ? '1 order sent to ShippingEasy' : `${sync.sent} orders sent to ShippingEasy`)\n if (sync.already) parts.push(`${sync.already} already there`)\n if (sync.failed) parts.push(`${sync.failed} not sent`)\n return `${parts.join(', ')}.${sync.more ? ' More open orders are waiting: select Send open orders again.' : ''}`\n}\n\nconst when = (ms: number | undefined): string =>\n ms ? new Date(ms).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' }) : 'Not yet'\n\n/** One fixed fact of the connection: a label and its value. */\nfunction FactRow(props: { label: string; value: string }) {\n return (\n <TableRow>\n <TableCell component=\"th\" scope=\"row\">\n {props.label}\n </TableCell>\n <TableCell>{props.value}</TableCell>\n </TableRow>\n )\n}\n\nconst EMPTY_KEYS = { apiKey: '', apiSecret: '', storeApiKey: '' }\n\n/**\n * Connect ShippingEasy (AGL-3633): the merchant's API key, API secret and\n * store API key, from their own ShippingEasy account; the callback URL to\n * paste into the API store; send open orders; and disconnect. Paid orders go\n * to ShippingEasy as they are paid, and each label bought there marks the\n * order shipped here. The secret is sealed on the server and never shown\n * again; the card renders nothing on a deployment that cannot seal one.\n */\nexport function ShippingEasyCard(props: ShippingEasyCardProps) {\n const { hostId } = props\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [status, setStatus] = useState<ConnectionStatus | null>(null)\n const [loadError, setLoadError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [editing, setEditing] = useState(false)\n const [keys, setKeys] = useState(EMPTY_KEYS)\n const [confirmDisconnect, setConfirmDisconnect] = useState(false)\n\n const uid = user?.uid\n useEffect(() => {\n if (!uid || !user) return\n let live = true\n authorizedFetch(\n user as never,\n `/api/commerce/shipping-connectors?connector=shippingeasy&hostId=${encodeURIComponent(hostId)}`,\n )\n .then(async (response) => {\n const body = await response.json().catch(() => ({}))\n if (!live) return\n if (!response.ok) return setLoadError(body?.error ?? 'The connection could not be read.')\n setLoadError(null)\n setStatus(body as ConnectionStatus)\n })\n .catch(() => live && setLoadError('The connection could not be read.'))\n return () => {\n live = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `user` changes identity every render; `uid` is what matters.\n }, [uid, hostId])\n\n const act = useCallback(\n async (action: 'connect' | 'sync' | 'disconnect', extra: Record<string, string> = {}): Promise<boolean> => {\n setBusy(true)\n try {\n const response = await authorizedFetch(user as never, '/api/commerce/shipping-connectors', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, connector: 'shippingeasy', action, ...extra }),\n })\n const body = (await response.json().catch(() => ({}))) as ConnectionStatus & { error?: string }\n if (!response.ok) {\n enqueueSnackbar(body?.error ?? 'That did not work. Try again.', { variant: 'error', allowDuplicate: true })\n return false\n }\n setStatus(body)\n if (action === 'disconnect') {\n enqueueSnackbar('ShippingEasy is disconnected. New orders are no longer sent to it.', { variant: 'success' })\n } else if (action === 'sync' && body.sync) {\n enqueueSnackbar(describeShippingEasySync(body.sync), { variant: body.sync.failed ? 'warning' : 'success' })\n } else if (action === 'connect') {\n enqueueSnackbar('ShippingEasy is connected. Paste the callback URL into your ShippingEasy API store.', {\n variant: 'success',\n })\n }\n return true\n } finally {\n setBusy(false)\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n // A deployment with no secret keyring cannot keep a secret: nothing to offer.\n if (status && status.available === false && !status.connected) return null\n\n const connected = Boolean(status?.connected)\n const openKeys = () => {\n setKeys(EMPTY_KEYS)\n setEditing(true)\n }\n const actions = !status ? null : connected ? (\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" disabled={busy} onClick={() => void act('sync')}>\n {'Send open orders'}\n </Button>\n <Button size=\"small\" disabled={busy} onClick={openKeys}>\n {'Change keys'}\n </Button>\n <Button size=\"small\" color=\"error\" disabled={busy} onClick={() => setConfirmDisconnect(true)}>\n {'Disconnect'}\n </Button>\n </Stack>\n ) : (\n <Button size=\"small\" variant=\"contained\" disabled={busy} onClick={openKeys}>\n {'Connect ShippingEasy'}\n </Button>\n )\n\n const complete = Boolean(keys.apiKey.trim() && keys.apiSecret.trim() && keys.storeApiKey.trim())\n const save = async () => {\n if (await act('connect', keys)) {\n setEditing(false)\n setKeys(EMPTY_KEYS)\n }\n }\n\n return (\n <CardDisplay\n header={'ShippingEasy'}\n help={pluginDocsHelp('shippingEasy', { anchor: '#connect' })}\n HeaderProps={{ action: actions }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {loadError ? <Alert severity=\"error\">{loadError}</Alert> : null}\n {!connected ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Labeling in ShippingEasy? Connect it with the API key, API secret and store API key from your ' +\n 'ShippingEasy account: each order is sent there as it is paid, and each label you buy there ' +\n 'marks the order shipped here and emails your customer the tracking link. Only a site admin ' +\n 'can connect it.'\n }\n </Typography>\n ) : (\n <>\n <div>\n {status?.lastError ? (\n <StatusChip label=\"Needs attention\" tone=\"warning\" data-testid=\"shippingeasy-status\" />\n ) : (\n <StatusChip label=\"Connected\" tone=\"success\" data-testid=\"shippingeasy-status\" />\n )}\n </div>\n {status?.lastError ? (\n <Alert severity=\"warning\">\n {`${status.lastError.message} (${when(status.lastError.atMs)})`}\n </Alert>\n ) : null}\n <CopyField\n label=\"Callback URL\"\n value={shippingEasyCallbackUrl(hostId)}\n helperText=\"Paste this into your API store's settings in ShippingEasy, so each label marks the order shipped here.\"\n onCopied={() => enqueueSnackbar('Callback URL copied', { variant: 'success', persist: false })}\n onCopyFailed={() => enqueueSnackbar('Select the callback URL and copy it.', { variant: 'info' })}\n />\n <ScrollTable size=\"small\" aria-label=\"ShippingEasy connection\">\n <TableBody>\n <FactRow label=\"API key\" value={status?.apiKeyEnding ? `Ends in ${status.apiKeyEnding}` : ''} />\n <FactRow label=\"Store API key\" value={status?.storeApiKeyEnding ? `Ends in ${status.storeApiKeyEnding}` : ''} />\n <FactRow label=\"Last order sent\" value={when(status?.lastPushAtMs)} />\n <FactRow label=\"Last shipment received\" value={when(status?.lastCallbackAtMs)} />\n </TableBody>\n </ScrollTable>\n </>\n )}\n </Stack>\n <Dialog open={editing} onClose={() => !busy && setEditing(false)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{connected ? 'Change ShippingEasy keys' : 'Connect ShippingEasy'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <DialogContentText>\n {\n 'In ShippingEasy, the API key and API secret are under Settings → API Credentials, and the ' +\n 'store API key is on your API store under Settings → Stores & Orders. The secret is kept ' +\n 'encrypted and is never shown again.'\n }\n </DialogContentText>\n <TextField\n label=\"API key\"\n value={keys.apiKey}\n onChange={(event) => setKeys((current) => ({ ...current, apiKey: event.target.value }))}\n autoComplete=\"off\"\n fullWidth\n required\n />\n <TextField\n label=\"API secret\"\n type=\"password\"\n value={keys.apiSecret}\n onChange={(event) => setKeys((current) => ({ ...current, apiSecret: event.target.value }))}\n autoComplete=\"new-password\"\n fullWidth\n required\n />\n <TextField\n label=\"Store API key\"\n value={keys.storeApiKey}\n onChange={(event) => setKeys((current) => ({ ...current, storeApiKey: event.target.value }))}\n autoComplete=\"off\"\n fullWidth\n required\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button disabled={busy} onClick={() => setEditing(false)}>\n {'Cancel'}\n </Button>\n <Button variant=\"contained\" disabled={busy || !complete} onClick={() => void save()}>\n {connected ? 'Save keys' : 'Connect'}\n </Button>\n </DialogActions>\n </Dialog>\n <Dialog open={confirmDisconnect} onClose={() => setConfirmDisconnect(false)}>\n <DialogTitle>{'Disconnect ShippingEasy?'}</DialogTitle>\n <DialogContent>\n <DialogContentText>\n {\n 'New orders stop going to ShippingEasy, and shipments it sends back are refused. Orders ' +\n 'already there stay there, and orders already shipped stay shipped. The keys are deleted ' +\n `from ${PLATFORM_BRAND_NAME}.`\n }\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setConfirmDisconnect(false)}>{'Cancel'}</Button>\n <Button\n color=\"error\"\n disabled={busy}\n onClick={() => {\n setConfirmDisconnect(false)\n void act('disconnect')\n }}\n >\n {'Disconnect'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nShippingEasyCard.displayName = 'ShippingEasyCard'\n\nexport default ShippingEasyCard\n"],"names":["pluginDocsHelp","PLATFORM_BRAND_NAME","CardDisplay","CopyField","ScrollTable","StatusChip","useSnackbar","authorizedFetch","useUser","Alert","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Stack","TableBody","TableCell","TableRow","TextField","Typography","useCallback","useEffect","useState","shippingEasyCallbackUrl","hostId","origin","base","window","location","encodeURIComponent","describeShippingEasySync","sync","parts","push","sent","already","failed","join","more","when","ms","Date","toLocaleString","undefined","dateStyle","timeStyle","FactRow","props","component","scope","label","value","EMPTY_KEYS","apiKey","apiSecret","storeApiKey","ShippingEasyCard","data","user","enqueueSnackbar","status","setStatus","loadError","setLoadError","busy","setBusy","editing","setEditing","keys","setKeys","confirmDisconnect","setConfirmDisconnect","uid","live","then","response","body","json","catch","ok","error","act","action","extra","method","headers","JSON","stringify","connector","variant","allowDuplicate","available","connected","Boolean","openKeys","actions","direction","spacing","size","disabled","onClick","color","complete","trim","save","header","help","anchor","HeaderProps","contentGutterX","contentGutterY","severity","div","lastError","tone","data-testid","message","atMs","helperText","onCopied","persist","onCopyFailed","aria-label","apiKeyEnding","storeApiKeyEnding","lastPushAtMs","lastCallbackAtMs","open","onClose","fullWidth","maxWidth","sx","pt","onChange","event","current","target","autoComplete","required","type","displayName"],"mappings":"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"}
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
*/ 'use client';
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
18
|
import { pluginDocsHelp } from "@aglyn/aglyn";
|
|
19
|
-
import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn/app-utils/platform-brand";
|
|
20
19
|
import { CardDisplay } from "@aglyn/shared-ui-jsx";
|
|
21
20
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
22
21
|
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
@@ -188,7 +187,7 @@ const when = (ms)=>ms ? new Date(ms).toLocaleString(undefined, {
|
|
|
188
187
|
return /*#__PURE__*/ _jsxs(CardDisplay, {
|
|
189
188
|
header: 'ShipStation',
|
|
190
189
|
help: pluginDocsHelp('shipStation', {
|
|
191
|
-
|
|
190
|
+
anchor: '#connect'
|
|
192
191
|
}),
|
|
193
192
|
HeaderProps: {
|
|
194
193
|
action: actions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shipstation-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n AlertTitle,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Stack,\n TableBody,\n TableCell,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useState } from 'react'\nimport ScrollTable from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { SHIPSTATION_STATUS_NAMES } from '../../model/shipstation'\n\nexport interface ShipStationCardProps {\n hostId: string\n}\n\n/** What the connectors route answers about a connection. */\ninterface ConnectionStatus {\n /** Whether this deployment can keep a ShipStation password at all. */\n available?: boolean\n connected: boolean\n username?: string\n createdAtMs?: number\n rotatedAtMs?: number\n lastExportAtMs?: number\n lastShipNoticeAtMs?: number\n}\n\n/** The address ShipStation calls: this console's own origin, never a guess. */\nexport function shipStationEndpoint(hostId: string, origin?: string): string {\n const base = origin ?? (typeof window === 'undefined' ? '' : window.location.origin)\n return `${base}/api/commerce/shipstation/${encodeURIComponent(hostId)}`\n}\n\n/** The status names to type into ShipStation's Custom Store form, in its order. */\nexport const SHIPSTATION_STATUS_ROWS: ReadonlyArray<readonly [string, string]> = [\n ['Unpaid Status', SHIPSTATION_STATUS_NAMES.unpaid],\n ['Paid Status', SHIPSTATION_STATUS_NAMES.paid],\n ['Shipped Status', SHIPSTATION_STATUS_NAMES.shipped],\n ['Canceled Status', SHIPSTATION_STATUS_NAMES.canceled],\n ['On-Hold Status', SHIPSTATION_STATUS_NAMES.onHold],\n]\n\nconst when = (ms: number | undefined): string =>\n ms ? new Date(ms).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' }) : 'Not yet'\n\n/**\n * Connect ShipStation (AGL-3613): the URL, username and password a merchant\n * pastes into ShipStation's Custom Store, the status names to map, a new\n * password and a disconnect. The password is kept sealed on the server and\n * a site admin can show it again; the card renders nothing on a deployment\n * that cannot seal one.\n */\nexport function ShipStationCard(props: ShipStationCardProps) {\n const { hostId } = props\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [status, setStatus] = useState<ConnectionStatus | null>(null)\n const [loadError, setLoadError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [password, setPassword] = useState<string | null>(null)\n const [confirmDisconnect, setConfirmDisconnect] = useState(false)\n\n // Keyed on the account, not the user object: the object is new on every\n // render, and the status is the same until somebody else signs in.\n const uid = user?.uid\n useEffect(() => {\n if (!uid || !user) return\n let live = true\n authorizedFetch(user as never, `/api/commerce/shipping-connectors?hostId=${encodeURIComponent(hostId)}`)\n .then(async (response) => {\n const body = await response.json().catch(() => ({}))\n if (!live) return\n if (!response.ok) return setLoadError(body?.error ?? 'The connection could not be read.')\n setLoadError(null)\n setStatus(body as ConnectionStatus)\n })\n .catch(() => live && setLoadError('The connection could not be read.'))\n return () => {\n live = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `user` changes identity every render; `uid` is what matters.\n }, [uid, hostId])\n\n const act = useCallback(\n async (action: 'connect' | 'rotate' | 'reveal' | 'disconnect') => {\n setBusy(true)\n try {\n const response = await authorizedFetch(user as never, '/api/commerce/shipping-connectors', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, action }),\n })\n const body = await response.json().catch(() => ({}))\n if (!response.ok) {\n enqueueSnackbar(body?.error ?? 'That did not work. Try again.', { variant: 'error', allowDuplicate: true })\n return\n }\n setStatus(body as ConnectionStatus)\n setPassword(typeof body?.password === 'string' ? body.password : null)\n if (action === 'disconnect') {\n enqueueSnackbar('ShipStation is disconnected. It can no longer read your orders.', { variant: 'success' })\n }\n } finally {\n setBusy(false)\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n const copy = (label: string, value: string) => {\n void navigator.clipboard\n ?.writeText(value)\n .then(() => enqueueSnackbar(`${label} copied`, { variant: 'success', persist: false }))\n .catch(() => enqueueSnackbar(`Select the ${label.toLowerCase()} and copy it.`, { variant: 'info' }))\n }\n\n // A deployment with no secret keyring cannot keep a password: nothing to offer.\n if (status && status.available === false && !status.connected) return null\n\n const connected = Boolean(status?.connected)\n const actions = !status ? null : connected ? (\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" disabled={busy} onClick={() => void act('rotate')}>\n {'New password'}\n </Button>\n <Button size=\"small\" color=\"error\" disabled={busy} onClick={() => setConfirmDisconnect(true)}>\n {'Disconnect'}\n </Button>\n </Stack>\n ) : (\n <Button size=\"small\" variant=\"contained\" disabled={busy} onClick={() => void act('connect')}>\n {'Connect ShipStation'}\n </Button>\n )\n\n const endpoint = shipStationEndpoint(hostId)\n const credentialRows: Array<[string, string, boolean]> = [\n ['URL to custom XML page', endpoint, true],\n ['Username', status?.username ?? '', true],\n ]\n if (password) credentialRows.push(['Password', password, true])\n\n return (\n <CardDisplay\n header={'ShipStation'}\n help={pluginDocsHelp('shipStation', {\n excerpt:\n `ShipStation imports your paid orders from ${PLATFORM_BRAND_NAME} and sends each shipment back, which ` +\n 'marks the order shipped and emails your customer the tracking link.',\n })}\n HeaderProps={{ action: actions }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {loadError ? <Alert severity=\"error\">{loadError}</Alert> : null}\n {!connected ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Already labeling in ShipStation? Connect it as a Custom Store: ShipStation imports the ' +\n 'orders you still have to ship, and each label you buy there marks the order shipped ' +\n 'here and emails your customer the tracking link. Only a site admin can connect it.'\n }\n </Typography>\n ) : (\n <>\n {password ? (\n <Alert severity=\"info\">\n <AlertTitle>{'Paste these into ShipStation'}</AlertTitle>\n {\n 'Anyone with this password can read the name and address on every order you ship. ' +\n 'If it gets out, make a new password: the old one stops working at once.'\n }\n </Alert>\n ) : null}\n <ScrollTable size=\"small\" aria-label=\"ShipStation connection\">\n <TableBody>\n {credentialRows.map(([label, value]) => (\n <TableRow key={label}>\n <TableCell component=\"th\" scope=\"row\">\n {label}\n </TableCell>\n <TableCell sx={{ wordBreak: 'break-all' }}>{value}</TableCell>\n <TableCell align=\"right\">\n <Button size=\"small\" onClick={() => copy(label, value)}>\n {'Copy'}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n {!password ? (\n <TableRow>\n <TableCell component=\"th\" scope=\"row\">\n {'Password'}\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Hidden. Only a site admin can show it.'}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Button size=\"small\" disabled={busy} onClick={() => void act('reveal')}>\n {'Show'}\n </Button>\n </TableCell>\n </TableRow>\n ) : null}\n </TableBody>\n </ScrollTable>\n <Typography variant=\"subtitle2\">{'Status names to type into ShipStation'}</Typography>\n <ScrollTable size=\"small\" aria-label=\"ShipStation status names\">\n <TableBody>\n {SHIPSTATION_STATUS_ROWS.map(([field, value]) => (\n <TableRow key={field}>\n <TableCell component=\"th\" scope=\"row\">\n {field}\n </TableCell>\n <TableCell>{value}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Last order import: ${when(status?.lastExportAtMs)}. Last shipment received: ${when(status?.lastShipNoticeAtMs)}.`}\n </Typography>\n </>\n )}\n </Stack>\n <Dialog open={confirmDisconnect} onClose={() => setConfirmDisconnect(false)}>\n <DialogTitle>{'Disconnect ShipStation?'}</DialogTitle>\n <DialogContent>\n <DialogContentText>\n {\n 'ShipStation stops importing orders from this site, and shipments it sends back are ' +\n 'refused. Orders already shipped stay shipped. You can connect again later with a new password.'\n }\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setConfirmDisconnect(false)}>{'Cancel'}</Button>\n <Button\n color=\"error\"\n disabled={busy}\n onClick={() => {\n setConfirmDisconnect(false)\n void act('disconnect')\n }}\n >\n {'Disconnect'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nShipStationCard.displayName = 'ShipStationCard'\n\nexport default ShipStationCard\n"],"names":["pluginDocsHelp","PLATFORM_BRAND_NAME","CardDisplay","useSnackbar","authorizedFetch","useUser","Alert","AlertTitle","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Stack","TableBody","TableCell","TableRow","Typography","useCallback","useEffect","useState","ScrollTable","SHIPSTATION_STATUS_NAMES","shipStationEndpoint","hostId","origin","base","window","location","encodeURIComponent","SHIPSTATION_STATUS_ROWS","unpaid","paid","shipped","canceled","onHold","when","ms","Date","toLocaleString","undefined","dateStyle","timeStyle","ShipStationCard","props","data","user","enqueueSnackbar","status","setStatus","loadError","setLoadError","busy","setBusy","password","setPassword","confirmDisconnect","setConfirmDisconnect","uid","live","then","response","body","json","catch","ok","error","act","action","method","headers","JSON","stringify","variant","allowDuplicate","copy","label","value","navigator","clipboard","writeText","persist","toLowerCase","available","connected","Boolean","actions","direction","spacing","size","disabled","onClick","color","endpoint","credentialRows","username","push","header","help","excerpt","HeaderProps","contentGutterX","contentGutterY","severity","aria-label","map","component","scope","sx","wordBreak","align","field","lastExportAtMs","lastShipNoticeAtMs","open","onClose","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;AAEA,SAASA,cAAc,QAAQ,eAAc;AAC7C,SAASC,mBAAmB,QAAQ,wCAAuC;AAC3E,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SACEC,KAAK,EACLC,UAAU,EACVC,MAAM,EACNC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,iBAAiB,EACjBC,WAAW,EACXC,KAAK,EACLC,SAAS,EACTC,SAAS,EACTC,QAAQ,EACRC,UAAU,QACL,gBAAe;AACtB,SAASC,WAAW,EAAEC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AACxD,OAAOC,iBAAiB,yDAAwD;AAChF,SAASC,wBAAwB,QAAQ,6BAAyB;AAkBlE,6EAA6E,GAC7E,OAAO,SAASC,oBAAoBC,MAAc,EAAEC,MAAe;IACjE,MAAMC,OAAOD,iBAAAA,SAAW,OAAOE,WAAW,cAAc,KAAKA,OAAOC,QAAQ,CAACH,MAAM;IACnF,OAAO,GAAGC,KAAK,0BAA0B,EAAEG,mBAAmBL,SAAS;AACzE;AAEA,iFAAiF,GACjF,OAAO,MAAMM,0BAAoE;IAC/E;QAAC;QAAiBR,yBAAyBS,MAAM;KAAC;IAClD;QAAC;QAAeT,yBAAyBU,IAAI;KAAC;IAC9C;QAAC;QAAkBV,yBAAyBW,OAAO;KAAC;IACpD;QAAC;QAAmBX,yBAAyBY,QAAQ;KAAC;IACtD;QAAC;QAAkBZ,yBAAyBa,MAAM;KAAC;CACpD,CAAA;AAED,MAAMC,OAAO,CAACC,KACZA,KAAK,IAAIC,KAAKD,IAAIE,cAAc,CAACC,WAAW;QAAEC,WAAW;QAAUC,WAAW;IAAQ,KAAK;AAE7F;;;;;;CAMC,GACD,OAAO,SAASC,gBAAgBC,KAA2B;;IACzD,MAAM,EAAEpB,MAAM,EAAE,GAAGoB;IACnB,MAAM,EAAEC,MAAMC,IAAI,EAAE,GAAG1C;IACvB,MAAM,EAAE2C,eAAe,EAAE,GAAG7C;IAC5B,MAAM,CAAC8C,QAAQC,UAAU,GAAG7B,SAAkC;IAC9D,MAAM,CAAC8B,WAAWC,aAAa,GAAG/B,SAAwB;IAC1D,MAAM,CAACgC,MAAMC,QAAQ,GAAGjC,SAAS;IACjC,MAAM,CAACkC,UAAUC,YAAY,GAAGnC,SAAwB;IACxD,MAAM,CAACoC,mBAAmBC,qBAAqB,GAAGrC,SAAS;IAE3D,wEAAwE;IACxE,mEAAmE;IACnE,MAAMsC,MAAMZ,wBAAAA,KAAMY,GAAG;IACrBvC,UAAU;QACR,IAAI,CAACuC,OAAO,CAACZ,MAAM;QACnB,IAAIa,OAAO;QACXxD,gBAAgB2C,MAAe,CAAC,yCAAyC,EAAEjB,mBAAmBL,SAAS,EACpGoC,IAAI,CAAC,OAAOC;;YACX,MAAMC,OAAO,MAAMD,SAASE,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YACjD,IAAI,CAACL,MAAM;YACX,IAAI,CAACE,SAASI,EAAE,EAAE,OAAOd,qBAAaW,wBAAAA,KAAMI,KAAK,mBAAI;YACrDf,aAAa;YACbF,UAAUa;QACZ,GACCE,KAAK,CAAC,IAAML,QAAQR,aAAa;QACpC,OAAO;YACLQ,OAAO;QACT;IACA,uHAAuH;IACzH,GAAG;QAACD;QAAKlC;KAAO;IAEhB,MAAM2C,MAAMjD,YACV,OAAOkD;QACLf,QAAQ;QACR,IAAI;YACF,MAAMQ,WAAW,MAAM1D,gBAAgB2C,MAAe,qCAAqC;gBACzFuB,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9CR,MAAMS,KAAKC,SAAS,CAAC;oBAAEhD;oBAAQ4C;gBAAO;YACxC;YACA,MAAMN,OAAO,MAAMD,SAASE,IAAI,GAAGC,KAAK,CAAC,IAAO,CAAA,CAAC,CAAA;YACjD,IAAI,CAACH,SAASI,EAAE,EAAE;;gBAChBlB,wBAAgBe,wBAAAA,KAAMI,KAAK,mBAAI,iCAAiC;oBAAEO,SAAS;oBAASC,gBAAgB;gBAAK;gBACzG;YACF;YACAzB,UAAUa;YACVP,YAAY,QAAOO,wBAAAA,KAAMR,QAAQ,MAAK,WAAWQ,KAAKR,QAAQ,GAAG;YACjE,IAAIc,WAAW,cAAc;gBAC3BrB,gBAAgB,mEAAmE;oBAAE0B,SAAS;gBAAU;YAC1G;QACF,SAAU;YACRpB,QAAQ;QACV;IACF,GACA;QAACP;QAAMtB;QAAQuB;KAAgB;IAGjC,MAAM4B,OAAO,CAACC,OAAeC;YACtBC;QAAL,OAAKA,uBAAAA,UAAUC,SAAS,qBAAnBD,qBACDE,SAAS,CAACH,OACXjB,IAAI,CAAC,IAAMb,gBAAgB,GAAG6B,MAAM,OAAO,CAAC,EAAE;gBAAEH,SAAS;gBAAWQ,SAAS;YAAM,IACnFjB,KAAK,CAAC,IAAMjB,gBAAgB,CAAC,WAAW,EAAE6B,MAAMM,WAAW,GAAG,aAAa,CAAC,EAAE;gBAAET,SAAS;YAAO;IACrG;IAEA,gFAAgF;IAChF,IAAIzB,UAAUA,OAAOmC,SAAS,KAAK,SAAS,CAACnC,OAAOoC,SAAS,EAAE,OAAO;IAEtE,MAAMA,YAAYC,QAAQrC,0BAAAA,OAAQoC,SAAS;IAC3C,MAAME,UAAU,CAACtC,SAAS,OAAOoC,0BAC/B,MAACvE;QAAM0E,WAAU;QAAMC,SAAS;;0BAC9B,KAACjF;gBAAOkF,MAAK;gBAAQC,UAAUtC;gBAAMuC,SAAS,IAAM,KAAKxB,IAAI;0BAC1D;;0BAEH,KAAC5D;gBAAOkF,MAAK;gBAAQG,OAAM;gBAAQF,UAAUtC;gBAAMuC,SAAS,IAAMlC,qBAAqB;0BACpF;;;uBAIL,KAAClD;QAAOkF,MAAK;QAAQhB,SAAQ;QAAYiB,UAAUtC;QAAMuC,SAAS,IAAM,KAAKxB,IAAI;kBAC9E;;IAIL,MAAM0B,WAAWtE,oBAAoBC;IACrC,MAAMsE,iBAAmD;QACvD;YAAC;YAA0BD;YAAU;SAAK;QAC1C;YAAC;oBAAY7C,0BAAAA,OAAQ+C,QAAQ,mBAAI;YAAI;SAAK;KAC3C;IACD,IAAIzC,UAAUwC,eAAeE,IAAI,CAAC;QAAC;QAAY1C;QAAU;KAAK;IAE9D,qBACE,MAACrD;QACCgG,QAAQ;QACRC,MAAMnG,eAAe,eAAe;YAClCoG,SACE,CAAC,0CAA0C,EAAEnG,oBAAoB,qCAAqC,CAAC,GACvG;QACJ;QACAoG,aAAa;YAAEhC,QAAQkB;QAAQ;QAC/Be,cAAc;QACdC,cAAc;;0BAEd,MAACzF;gBAAM2E,SAAS;;oBACbtC,0BAAY,KAAC7C;wBAAMkG,UAAS;kCAASrD;yBAAqB;oBAC1D,CAACkC,0BACA,KAACnE;wBAAWwD,SAAQ;wBAAQmB,OAAM;kCAE9B,4FACA,yFACA;uCAIJ;;4BACGtC,yBACC,MAACjD;gCAAMkG,UAAS;;kDACd,KAACjG;kDAAY;;oCAEX,sFACA;;iCAGF;0CACJ,KAACe;gCAAYoE,MAAK;gCAAQe,cAAW;0CACnC,cAAA,MAAC1F;;wCACEgF,eAAeW,GAAG,CAAC,CAAC,CAAC7B,OAAOC,MAAM,iBACjC,MAAC7D;;kEACC,KAACD;wDAAU2F,WAAU;wDAAKC,OAAM;kEAC7B/B;;kEAEH,KAAC7D;wDAAU6F,IAAI;4DAAEC,WAAW;wDAAY;kEAAIhC;;kEAC5C,KAAC9D;wDAAU+F,OAAM;kEACf,cAAA,KAACvG;4DAAOkF,MAAK;4DAAQE,SAAS,IAAMhB,KAAKC,OAAOC;sEAC7C;;;;+CAPQD;wCAYhB,CAACtB,yBACA,MAACtC;;8DACC,KAACD;oDAAU2F,WAAU;oDAAKC,OAAM;8DAC7B;;8DAEH,KAAC5F;8DACC,cAAA,KAACE;wDAAWwD,SAAQ;wDAAQmB,OAAM;kEAC/B;;;8DAGL,KAAC7E;oDAAU+F,OAAM;8DACf,cAAA,KAACvG;wDAAOkF,MAAK;wDAAQC,UAAUtC;wDAAMuC,SAAS,IAAM,KAAKxB,IAAI;kEAC1D;;;;6CAIL;;;;0CAGR,KAAClD;gCAAWwD,SAAQ;0CAAa;;0CACjC,KAACpD;gCAAYoE,MAAK;gCAAQe,cAAW;0CACnC,cAAA,KAAC1F;8CACEgB,wBAAwB2E,GAAG,CAAC,CAAC,CAACM,OAAOlC,MAAM,iBAC1C,MAAC7D;;8DACC,KAACD;oDAAU2F,WAAU;oDAAKC,OAAM;8DAC7BI;;8DAEH,KAAChG;8DAAW8D;;;2CAJCkC;;;0CASrB,KAAC9F;gCAAWwD,SAAQ;gCAAQmB,OAAM;0CAC/B,CAAC,mBAAmB,EAAExD,KAAKY,0BAAAA,OAAQgE,cAAc,EAAE,0BAA0B,EAAE5E,KAAKY,0BAAAA,OAAQiE,kBAAkB,EAAE,CAAC,CAAC;;;;;;0BAK3H,MAACzG;gBAAO0G,MAAM1D;gBAAmB2D,SAAS,IAAM1D,qBAAqB;;kCACnE,KAAC7C;kCAAa;;kCACd,KAACF;kCACC,cAAA,KAACC;sCAEG,wFACA;;;kCAIN,MAACF;;0CACC,KAACF;gCAAOoF,SAAS,IAAMlC,qBAAqB;0CAAS;;0CACrD,KAAClD;gCACCqF,OAAM;gCACNF,UAAUtC;gCACVuC,SAAS;oCACPlC,qBAAqB;oCACrB,KAAKU,IAAI;gCACX;0CAEC;;;;;;;;AAMb;AACAxB,gBAAgByE,WAAW,GAAG;AAE9B,eAAezE,gBAAe"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/components/console/shipstation-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 { pluginDocsHelp } from '@aglyn/aglyn'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n AlertTitle,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Stack,\n TableBody,\n TableCell,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useState } from 'react'\nimport ScrollTable from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { SHIPSTATION_STATUS_NAMES } from '../../model/shipstation'\n\nexport interface ShipStationCardProps {\n hostId: string\n}\n\n/** What the connectors route answers about a connection. */\ninterface ConnectionStatus {\n /** Whether this deployment can keep a ShipStation password at all. */\n available?: boolean\n connected: boolean\n username?: string\n createdAtMs?: number\n rotatedAtMs?: number\n lastExportAtMs?: number\n lastShipNoticeAtMs?: number\n}\n\n/** The address ShipStation calls: this console's own origin, never a guess. */\nexport function shipStationEndpoint(hostId: string, origin?: string): string {\n const base = origin ?? (typeof window === 'undefined' ? '' : window.location.origin)\n return `${base}/api/commerce/shipstation/${encodeURIComponent(hostId)}`\n}\n\n/** The status names to type into ShipStation's Custom Store form, in its order. */\nexport const SHIPSTATION_STATUS_ROWS: ReadonlyArray<readonly [string, string]> = [\n ['Unpaid Status', SHIPSTATION_STATUS_NAMES.unpaid],\n ['Paid Status', SHIPSTATION_STATUS_NAMES.paid],\n ['Shipped Status', SHIPSTATION_STATUS_NAMES.shipped],\n ['Canceled Status', SHIPSTATION_STATUS_NAMES.canceled],\n ['On-Hold Status', SHIPSTATION_STATUS_NAMES.onHold],\n]\n\nconst when = (ms: number | undefined): string =>\n ms ? new Date(ms).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' }) : 'Not yet'\n\n/**\n * Connect ShipStation (AGL-3613): the URL, username and password a merchant\n * pastes into ShipStation's Custom Store, the status names to map, a new\n * password and a disconnect. The password is kept sealed on the server and\n * a site admin can show it again; the card renders nothing on a deployment\n * that cannot seal one.\n */\nexport function ShipStationCard(props: ShipStationCardProps) {\n const { hostId } = props\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const [status, setStatus] = useState<ConnectionStatus | null>(null)\n const [loadError, setLoadError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [password, setPassword] = useState<string | null>(null)\n const [confirmDisconnect, setConfirmDisconnect] = useState(false)\n\n // Keyed on the account, not the user object: the object is new on every\n // render, and the status is the same until somebody else signs in.\n const uid = user?.uid\n useEffect(() => {\n if (!uid || !user) return\n let live = true\n authorizedFetch(user as never, `/api/commerce/shipping-connectors?hostId=${encodeURIComponent(hostId)}`)\n .then(async (response) => {\n const body = await response.json().catch(() => ({}))\n if (!live) return\n if (!response.ok) return setLoadError(body?.error ?? 'The connection could not be read.')\n setLoadError(null)\n setStatus(body as ConnectionStatus)\n })\n .catch(() => live && setLoadError('The connection could not be read.'))\n return () => {\n live = false\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `user` changes identity every render; `uid` is what matters.\n }, [uid, hostId])\n\n const act = useCallback(\n async (action: 'connect' | 'rotate' | 'reveal' | 'disconnect') => {\n setBusy(true)\n try {\n const response = await authorizedFetch(user as never, '/api/commerce/shipping-connectors', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, action }),\n })\n const body = await response.json().catch(() => ({}))\n if (!response.ok) {\n enqueueSnackbar(body?.error ?? 'That did not work. Try again.', { variant: 'error', allowDuplicate: true })\n return\n }\n setStatus(body as ConnectionStatus)\n setPassword(typeof body?.password === 'string' ? body.password : null)\n if (action === 'disconnect') {\n enqueueSnackbar('ShipStation is disconnected. It can no longer read your orders.', { variant: 'success' })\n }\n } finally {\n setBusy(false)\n }\n },\n [user, hostId, enqueueSnackbar],\n )\n\n const copy = (label: string, value: string) => {\n void navigator.clipboard\n ?.writeText(value)\n .then(() => enqueueSnackbar(`${label} copied`, { variant: 'success', persist: false }))\n .catch(() => enqueueSnackbar(`Select the ${label.toLowerCase()} and copy it.`, { variant: 'info' }))\n }\n\n // A deployment with no secret keyring cannot keep a password: nothing to offer.\n if (status && status.available === false && !status.connected) return null\n\n const connected = Boolean(status?.connected)\n const actions = !status ? null : connected ? (\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" disabled={busy} onClick={() => void act('rotate')}>\n {'New password'}\n </Button>\n <Button size=\"small\" color=\"error\" disabled={busy} onClick={() => setConfirmDisconnect(true)}>\n {'Disconnect'}\n </Button>\n </Stack>\n ) : (\n <Button size=\"small\" variant=\"contained\" disabled={busy} onClick={() => void act('connect')}>\n {'Connect ShipStation'}\n </Button>\n )\n\n const endpoint = shipStationEndpoint(hostId)\n const credentialRows: Array<[string, string, boolean]> = [\n ['URL to custom XML page', endpoint, true],\n ['Username', status?.username ?? '', true],\n ]\n if (password) credentialRows.push(['Password', password, true])\n\n return (\n <CardDisplay\n header={'ShipStation'}\n help={pluginDocsHelp('shipStation', { anchor: '#connect' })}\n HeaderProps={{ action: actions }}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={2}>\n {loadError ? <Alert severity=\"error\">{loadError}</Alert> : null}\n {!connected ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {\n 'Already labeling in ShipStation? Connect it as a Custom Store: ShipStation imports the ' +\n 'orders you still have to ship, and each label you buy there marks the order shipped ' +\n 'here and emails your customer the tracking link. Only a site admin can connect it.'\n }\n </Typography>\n ) : (\n <>\n {password ? (\n <Alert severity=\"info\">\n <AlertTitle>{'Paste these into ShipStation'}</AlertTitle>\n {\n 'Anyone with this password can read the name and address on every order you ship. ' +\n 'If it gets out, make a new password: the old one stops working at once.'\n }\n </Alert>\n ) : null}\n <ScrollTable size=\"small\" aria-label=\"ShipStation connection\">\n <TableBody>\n {credentialRows.map(([label, value]) => (\n <TableRow key={label}>\n <TableCell component=\"th\" scope=\"row\">\n {label}\n </TableCell>\n <TableCell sx={{ wordBreak: 'break-all' }}>{value}</TableCell>\n <TableCell align=\"right\">\n <Button size=\"small\" onClick={() => copy(label, value)}>\n {'Copy'}\n </Button>\n </TableCell>\n </TableRow>\n ))}\n {!password ? (\n <TableRow>\n <TableCell component=\"th\" scope=\"row\">\n {'Password'}\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Hidden. Only a site admin can show it.'}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Button size=\"small\" disabled={busy} onClick={() => void act('reveal')}>\n {'Show'}\n </Button>\n </TableCell>\n </TableRow>\n ) : null}\n </TableBody>\n </ScrollTable>\n <Typography variant=\"subtitle2\">{'Status names to type into ShipStation'}</Typography>\n <ScrollTable size=\"small\" aria-label=\"ShipStation status names\">\n <TableBody>\n {SHIPSTATION_STATUS_ROWS.map(([field, value]) => (\n <TableRow key={field}>\n <TableCell component=\"th\" scope=\"row\">\n {field}\n </TableCell>\n <TableCell>{value}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Last order import: ${when(status?.lastExportAtMs)}. Last shipment received: ${when(status?.lastShipNoticeAtMs)}.`}\n </Typography>\n </>\n )}\n </Stack>\n <Dialog open={confirmDisconnect} onClose={() => setConfirmDisconnect(false)}>\n <DialogTitle>{'Disconnect ShipStation?'}</DialogTitle>\n <DialogContent>\n <DialogContentText>\n {\n 'ShipStation stops importing orders from this site, and shipments it sends back are ' +\n 'refused. Orders already shipped stay shipped. You can connect again later with a new password.'\n }\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setConfirmDisconnect(false)}>{'Cancel'}</Button>\n <Button\n color=\"error\"\n disabled={busy}\n onClick={() => {\n setConfirmDisconnect(false)\n void act('disconnect')\n }}\n >\n {'Disconnect'}\n </Button>\n </DialogActions>\n </Dialog>\n </CardDisplay>\n )\n}\nShipStationCard.displayName = 'ShipStationCard'\n\nexport default ShipStationCard\n"],"names":["pluginDocsHelp","CardDisplay","useSnackbar","authorizedFetch","useUser","Alert","AlertTitle","Button","Dialog","DialogActions","DialogContent","DialogContentText","DialogTitle","Stack","TableBody","TableCell","TableRow","Typography","useCallback","useEffect","useState","ScrollTable","SHIPSTATION_STATUS_NAMES","shipStationEndpoint","hostId","origin","base","window","location","encodeURIComponent","SHIPSTATION_STATUS_ROWS","unpaid","paid","shipped","canceled","onHold","when","ms","Date","toLocaleString","undefined","dateStyle","timeStyle","ShipStationCard","props","data","user","enqueueSnackbar","status","setStatus","loadError","setLoadError","busy","setBusy","password","setPassword","confirmDisconnect","setConfirmDisconnect","uid","live","then","response","body","json","catch","ok","error","act","action","method","headers","JSON","stringify","variant","allowDuplicate","copy","label","value","navigator","clipboard","writeText","persist","toLowerCase","available","connected","Boolean","actions","direction","spacing","size","disabled","onClick","color","endpoint","credentialRows","username","push","header","help","anchor","HeaderProps","contentGutterX","contentGutterY","severity","aria-label","map","component","scope","sx","wordBreak","align","field","lastExportAtMs","lastShipNoticeAtMs","open","onClose","displayName"],"mappings":"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"}
|
|
@@ -129,7 +129,9 @@ import { writeSiteWideChange } from "@aglyn/tenant-feature-instance/hooks/helper
|
|
|
129
129
|
]);
|
|
130
130
|
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
131
131
|
header: 'Store settings',
|
|
132
|
-
help: pluginDocsHelp('commerce'
|
|
132
|
+
help: pluginDocsHelp('commerce', {
|
|
133
|
+
anchor: '#store-settings'
|
|
134
|
+
}),
|
|
133
135
|
contentGutterX: true,
|
|
134
136
|
contentGutterY: true,
|
|
135
137
|
children: /*#__PURE__*/ _jsxs(Stack, {
|