@mohasinac/appkit 4.61.0 β 4.62.1
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/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
- package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
- package/dist/_internal/server/features/checkout/actions.js +542 -26
- package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
- package/dist/_internal/server/features/invoicing/allocate.js +162 -0
- package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
- package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
- package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
- package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
- package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
- package/dist/_internal/server/features/invoicing/issue.js +202 -0
- package/dist/_internal/server/features/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +33 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
- package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
- package/dist/_internal/server/features/points/award.js +46 -4
- package/dist/_internal/server/features/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +239 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +15 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +51 -0
- package/dist/_internal/server/functions/scheduled.js +38 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
- package/dist/_internal/server/jobs/core/index.d.ts +2 -1
- package/dist/_internal/server/jobs/core/index.js +2 -1
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
- package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +87 -97
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.js +177 -9
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
- package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
- package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
- package/dist/_internal/shared/checkout/order-math.js +173 -0
- package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
- package/dist/_internal/shared/checkout/refundable-base.js +13 -1
- package/dist/_internal/shared/checkout/split.d.ts +87 -0
- package/dist/_internal/shared/checkout/split.js +100 -0
- package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
- package/dist/_internal/shared/content/disclaimers.js +217 -0
- package/dist/_internal/shared/content/published-values.d.ts +88 -0
- package/dist/_internal/shared/content/published-values.js +198 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
- package/dist/_internal/shared/features/admin/integrations.js +253 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
- package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
- package/dist/_internal/shared/features/points/accepts.js +43 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/fees/calculator.d.ts +33 -1
- package/dist/_internal/shared/fees/calculator.js +32 -2
- package/dist/_internal/shared/media/limits.d.ts +10 -0
- package/dist/_internal/shared/media/limits.js +10 -0
- package/dist/_internal/shared/types/action-result.js +15 -1
- package/dist/client/api/surface-error.js +16 -2
- package/dist/client/api/useApiMutation.js +17 -1
- package/dist/client.d.ts +12 -1
- package/dist/client.js +31 -1
- package/dist/constants/api-endpoints.d.ts +45 -0
- package/dist/constants/api-endpoints.js +15 -0
- package/dist/constants/field-names.d.ts +18 -0
- package/dist/constants/field-names.js +28 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/shipping.d.ts +45 -0
- package/dist/contracts/shipping.js +15 -0
- package/dist/core/server-action.js +19 -3
- package/dist/errors/error-mapping.js +26 -1
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOffersPanel.js +5 -3
- package/dist/features/account/components/UserOrdersView.js +19 -33
- package/dist/features/admin/components/AdminActionIndexView.js +100 -5
- package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselView.js +96 -4
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminMediaView.js +36 -3
- package/dist/features/admin/components/AdminNavigationView.js +4 -1
- package/dist/features/admin/components/AdminOffersView.js +2 -1
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +67 -5
- package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
- package/dist/features/admin/components/AdminProductEditorView.js +2 -2
- package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
- package/dist/features/admin/components/AdminSectionsView.js +486 -224
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
- package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
- package/dist/features/admin/constants/filter-tabs.js +6 -0
- package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminListingData.js +16 -0
- package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
- package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
- package/dist/features/admin/repository/notification.repository.d.ts +15 -0
- package/dist/features/admin/repository/notification.repository.js +32 -0
- package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
- package/dist/features/admin/schemas/action-index-form.js +115 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +30 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
- package/dist/features/audit-log/schemas/firestore.js +15 -0
- package/dist/features/auth/components/RegisterForm.js +2 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
- package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
- package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
- package/dist/features/cart/components/PointsTenderPicker.js +15 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
- package/dist/features/cart/components/index.d.ts +2 -0
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +19 -0
- package/dist/features/cart/repository/cart.repository.js +27 -0
- package/dist/features/cart/schemas/firestore.d.ts +92 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/AdminEventEditorView.js +8 -6
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
- package/dist/features/invoicing/schemas/firestore.js +45 -0
- package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
- package/dist/features/invoicing/utils/financial-year.js +53 -0
- package/dist/features/jobs/actions/enqueue-job.js +18 -1
- package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
- package/dist/features/jobs/repository/jobs.repository.js +84 -1
- package/dist/features/jobs/schemas/firestore.d.ts +80 -0
- package/dist/features/jobs/schemas/firestore.js +37 -0
- package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
- package/dist/features/lottery/schemas/zod.d.ts +6 -6
- package/dist/features/media/MediaImage.js +22 -1
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/obligations/constants/policies.js +24 -1
- package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
- package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
- package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
- package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
- package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
- package/dist/features/orders/components/RefundRequestView.js +22 -4
- package/dist/features/orders/components/index.d.ts +4 -0
- package/dist/features/orders/components/index.js +2 -0
- package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
- package/dist/features/orders/constants/order-lane-tabs.js +88 -0
- package/dist/features/orders/repository/orders.repository.d.ts +57 -0
- package/dist/features/orders/repository/orders.repository.js +145 -0
- package/dist/features/orders/schemas/firestore.d.ts +196 -1
- package/dist/features/orders/schemas/index.d.ts +60 -12
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +26 -0
- package/dist/features/orders/utils/courier-block.d.ts +25 -0
- package/dist/features/orders/utils/courier-block.js +23 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
- package/dist/features/payment-cases/schemas/firestore.js +1 -0
- package/dist/features/products/components/GroupMemberPicker.js +4 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
- package/dist/features/promotions/actions/coupon-stacking.js +21 -2
- package/dist/features/promotions/components/CouponCard.js +13 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
- package/dist/features/promotions/constants/coupon-help.js +9 -1
- package/dist/features/promotions/schemas/firestore.d.ts +24 -0
- package/dist/features/promotions/schemas/firestore.js +4 -0
- package/dist/features/promotions/types/index.d.ts +19 -1
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/seller-actions.js +33 -3
- package/dist/features/seller/components/FulfillmentView.js +3 -2
- package/dist/features/seller/components/SellerAddressesView.js +32 -4
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
- package/dist/features/seller/components/SellerBundlesView.js +53 -14
- package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
- package/dist/features/seller/components/SellerListingTypeView.js +56 -10
- package/dist/features/seller/components/SellerOrdersView.js +129 -27
- package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
- package/dist/features/seller/components/SellerProductsView.js +5 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
- package/dist/features/seller/components/SellerShippingView.js +2 -1
- package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
- package/dist/features/seller/components/SellerSupportView.js +69 -2
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/shell/SectionForm.js +20 -16
- package/dist/features/shell/build-sections.js +14 -1
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
- package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
- package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
- package/dist/features/stores/schemas/firestore.d.ts +71 -0
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/schemas/index.d.ts +6 -6
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +17 -2
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +19 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/schemas/registry.d.ts +51 -22
- package/dist/schemas/sieve.d.ts +5 -5
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +11 -2
- package/dist/server.js +40 -2
- package/dist/styles.css +64 -80
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -0
- package/dist/tokens/themes/default-light.js +1 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +1 -0
- package/dist/tokens/tokens.css +15 -0
- package/dist/ui/components/Checkbox.js +10 -2
- package/dist/ui/components/DateInput.js +8 -1
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +74 -0
- package/dist/ui/components/Input.js +22 -2
- package/dist/ui/components/PaginatedSelect.d.ts +8 -19
- package/dist/ui/components/PaginatedSelect.js +43 -9
- package/dist/ui/components/Radio.js +8 -1
- package/dist/ui/components/Select.js +10 -2
- package/dist/ui/components/TagInput.d.ts +8 -1
- package/dist/ui/components/TagInput.js +4 -3
- package/dist/ui/components/Textarea.js +10 -2
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/forms/FieldCheckbox.js +1 -1
- package/dist/ui/forms/FormErrorList.d.ts +0 -16
- package/dist/ui/forms/FormErrorList.js +34 -1
- package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
- package/dist/ui/forms/apply-zod-issues.js +23 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/rich-text/RichText.style.css +48 -79
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1831 -1822
|
@@ -236,6 +236,28 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
|
|
|
236
236
|
* π `inline-flex`, never `w-full`. In normal flow this must shrink to its
|
|
237
237
|
* own content β a full-width bar would read as a section divider and would
|
|
238
238
|
* stretch across a card body it is only a corner of.
|
|
239
|
+
*
|
|
240
|
+
* π `shrink-0` IS THE MOBILE FIX, and it is one word because the bug was
|
|
241
|
+
* one missing word.
|
|
242
|
+
*
|
|
243
|
+
* A flex item's default is `flex-shrink: 1`, so in the title/actions row
|
|
244
|
+
* this toolbar lives in β `<Row justify="between">` with an `<h1>` on the
|
|
245
|
+
* left β the heading and the toolbar compete for width, and at ~390px the
|
|
246
|
+
* toolbar LOSES. It compresses below its own content: the icons slide under
|
|
247
|
+
* each other, the padding collapses, and the 44px tap targets stop being
|
|
248
|
+
* 44px wide. That is the "works incorrectly on mobile" report.
|
|
249
|
+
*
|
|
250
|
+
* With `shrink-0` the toolbar keeps its intrinsic size and the HEADING gives
|
|
251
|
+
* way instead β which is right, because a heading wraps gracefully onto a
|
|
252
|
+
* second line and a row of hit targets does not.
|
|
253
|
+
*
|
|
254
|
+
* π Note what this is NOT: the desktop fix (`d94496c42`) gave the toolbar a
|
|
255
|
+
* container and added an audit whose two rules are about DOM ORDER β must
|
|
256
|
+
* come after the `<h1>`, must not share the heading's row unless that row is
|
|
257
|
+
* `justify="between"`. Both pass here. R2's exemption actively WHITELISTS
|
|
258
|
+
* the layout that breaks, so the surfaces that satisfy that audit are
|
|
259
|
+
* precisely the ones most likely to be wrong at phone width. A source-order
|
|
260
|
+
* rule cannot see a width problem.
|
|
239
261
|
*/
|
|
240
262
|
const boxClass = onDark
|
|
241
263
|
? /*
|
|
@@ -244,12 +266,12 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
|
|
|
244
266
|
* LIGHT background in every light theme, so a themed box on a dark hero
|
|
245
267
|
* photo is a white rectangle over the artwork.
|
|
246
268
|
*/
|
|
247
|
-
"inline-flex rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
|
|
269
|
+
"inline-flex shrink-0 rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
|
|
248
270
|
: /*
|
|
249
271
|
* Off media: the real surface tokens, so it inverts with the theme
|
|
250
272
|
* instead of being a fixed near-white that disappears in dark mode.
|
|
251
273
|
*/
|
|
252
|
-
"inline-flex rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
|
|
274
|
+
"inline-flex shrink-0 rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
|
|
253
275
|
return (_jsxs(_Fragment, { children: [_jsxs(Row, { gap: "xs", align: "center", className: `${boxClass} ${className}`.trim(),
|
|
254
276
|
/*
|
|
255
277
|
* Stops the click reaching an ancestor `<Link>` or the card's own
|
|
@@ -64,7 +64,7 @@ export function ReferenceRecordEditor({ endpoint, noun, fields, publicHref, }) {
|
|
|
64
64
|
catch (err) {
|
|
65
65
|
const e = normalizeError(err);
|
|
66
66
|
if (!cancelled)
|
|
67
|
-
setLoadError(e.
|
|
67
|
+
setLoadError(toUserMessage(e?.code, undefined, { fallback: "Could not load that record." }));
|
|
68
68
|
}
|
|
69
69
|
finally {
|
|
70
70
|
if (!cancelled)
|
|
@@ -457,6 +457,9 @@ export declare const ADMIN_COUPON_TYPE_TABS: readonly [{
|
|
|
457
457
|
}, {
|
|
458
458
|
readonly id: "buy_x_get_y";
|
|
459
459
|
readonly label: "Buy X Get Y";
|
|
460
|
+
}, {
|
|
461
|
+
readonly id: "points_earn";
|
|
462
|
+
readonly label: "Points Back";
|
|
460
463
|
}];
|
|
461
464
|
/** Empty-sentinel variant for views (like SellerBidsView) that drive the
|
|
462
465
|
* filter URL with `""` instead of `"All"` so the `"" == no filter` invariant
|
|
@@ -757,6 +760,9 @@ export declare const ADMIN_PAYMENT_CASE_KIND_TABS: readonly [{
|
|
|
757
760
|
}, {
|
|
758
761
|
readonly id: "shipping_unpriced";
|
|
759
762
|
readonly label: "Shipping unpriced";
|
|
763
|
+
}, {
|
|
764
|
+
readonly id: "courier_unavailable";
|
|
765
|
+
readonly label: "Courier unavailable";
|
|
760
766
|
}, {
|
|
761
767
|
readonly id: "dead_job";
|
|
762
768
|
readonly label: "Dead job";
|
|
@@ -245,6 +245,11 @@ export const ADMIN_COUPON_TYPE_TABS = [
|
|
|
245
245
|
{ id: "fixed", label: "Fixed" },
|
|
246
246
|
{ id: "free_shipping", label: "Free Shipping" },
|
|
247
247
|
{ id: "buy_x_get_y", label: "Buy X Get Y" },
|
|
248
|
+
// D51. `audit-filter-tab-enums`' COVERAGE rule fails on a real union value
|
|
249
|
+
// with no chip β which is what it was extended to catch after ten such gaps
|
|
250
|
+
// were found at once (Root Cause #72), including sellers being unable to
|
|
251
|
+
// filter their own `in_review` listings.
|
|
252
|
+
{ id: "points_earn", label: "Points Back" },
|
|
248
253
|
];
|
|
249
254
|
// ---------------------------------------------------------------------------
|
|
250
255
|
// Seller-dashboard filter-chip tab sets
|
|
@@ -441,6 +446,7 @@ export const ADMIN_PAYMENT_CASE_KIND_TABS = [
|
|
|
441
446
|
{ id: "protection_claim", label: "Protection claim" },
|
|
442
447
|
{ id: "grievance", label: "Grievance" },
|
|
443
448
|
{ id: "shipping_unpriced", label: "Shipping unpriced" },
|
|
449
|
+
{ id: "courier_unavailable", label: "Courier unavailable" },
|
|
444
450
|
{ id: "dead_job", label: "Dead job" },
|
|
445
451
|
{ id: "ledger_drift", label: "Points drift" },
|
|
446
452
|
];
|
|
@@ -102,7 +102,7 @@ export declare const SITE_SETTINGS_TABS: readonly [{
|
|
|
102
102
|
readonly description: "Which listing types sellers may create, and their defaults.";
|
|
103
103
|
readonly keywords: ["listing types", "auctions on", "enable", "categories"];
|
|
104
104
|
}];
|
|
105
|
-
export declare const SITE_SETTINGS_TAB_IDS: ("about" | "shipping" | "notifications" | "contact" | "auction" | "seo" | "listings" | "
|
|
105
|
+
export declare const SITE_SETTINGS_TAB_IDS: ("about" | "shipping" | "notifications" | "contact" | "auction" | "seo" | "listings" | "emi" | "legal" | "appearance" | "gst" | "watermark" | "integrations" | "limits" | "announcement" | "branding" | "themes" | "fees" | "procurement")[];
|
|
106
106
|
/**
|
|
107
107
|
* The union `?tab=` is validated against.
|
|
108
108
|
*
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { apiClient, ApiClientError } from "../../../http/index.js";
|
|
4
4
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
5
|
+
// The defining module, not a barrel (Root Cause #18). `hasEncPrefix` is a pure
|
|
6
|
+
// string test with no crypto in its import chain, so it is client-safe.
|
|
7
|
+
import { hasEncPrefix } from "../../../security/pii-mask.js";
|
|
5
8
|
function withQueryParams(endpoint, params) {
|
|
6
9
|
const hasQuery = endpoint.includes("?");
|
|
7
10
|
const query = new URLSearchParams(params).toString();
|
|
@@ -50,6 +53,19 @@ export function toRecordArray(value) {
|
|
|
50
53
|
}
|
|
51
54
|
export function toStringValue(value, fallback = "-") {
|
|
52
55
|
if (typeof value === "string") {
|
|
56
|
+
/*
|
|
57
|
+
* π Ciphertext is not a display value. See the twin in
|
|
58
|
+
* `features/seller/hooks/useSellerListingData.ts` for the full account β
|
|
59
|
+
* the short version is that `mapDoc` does not decrypt, so a route that
|
|
60
|
+
* forgets `revealOrders`/`maskedOrders` hands this a stored `enc:v1:β¦`
|
|
61
|
+
* blob which is a perfectly ordinary `string` and renders verbatim.
|
|
62
|
+
*
|
|
63
|
+
* `/api/admin/orders` already hit this ("measured live at 3 visible
|
|
64
|
+
* `enc:v1:` strings") and fixed it at the route. This is the backstop for
|
|
65
|
+
* the next one.
|
|
66
|
+
*/
|
|
67
|
+
if (hasEncPrefix(value))
|
|
68
|
+
return fallback;
|
|
53
69
|
return value.trim() || fallback;
|
|
54
70
|
}
|
|
55
71
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type QueryKey } from "@tanstack/react-query";
|
|
2
|
+
export interface UseOptimisticRowFlagOptions<T> {
|
|
3
|
+
/**
|
|
4
|
+
* Performs the write. Receives the row id and the value being set.
|
|
5
|
+
*
|
|
6
|
+
* π Send ONLY the field being changed. Several of this hook's consumers sit
|
|
7
|
+
* over repositories that merge a `config`/nested object by READING the
|
|
8
|
+
* document first; omitting that field short-circuits the read, so a toggle
|
|
9
|
+
* costs one write and no read.
|
|
10
|
+
*/
|
|
11
|
+
mutate: (id: string, next: T) => Promise<unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Query keys to invalidate. Pass every key that renders this field β
|
|
14
|
+
* several of these records are read by a PUBLIC surface under a key of its
|
|
15
|
+
* own, and forgetting it leaves the storefront stale until its own TTL.
|
|
16
|
+
*/
|
|
17
|
+
invalidate: readonly QueryKey[];
|
|
18
|
+
/** Toast copy. Receives the value that was set. */
|
|
19
|
+
successMessage: (next: T) => string;
|
|
20
|
+
/** Fallback when the thrown error carries no message. */
|
|
21
|
+
errorMessage?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface UseOptimisticRowFlagReturn<T> {
|
|
24
|
+
/** `overrides[id] ?? row.value` β the value to render. */
|
|
25
|
+
overrides: Record<string, T>;
|
|
26
|
+
/** Resolve a row's current value against any pending override. */
|
|
27
|
+
resolve: (id: string, serverValue: T) => T;
|
|
28
|
+
/** The id currently being written, for a per-row disabled state. */
|
|
29
|
+
pendingId: string | null;
|
|
30
|
+
/** Fire the change. Safe to call from an `onChange` without awaiting. */
|
|
31
|
+
set: (id: string, next: T) => Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export declare function useOptimisticRowFlag<T>({ mutate, invalidate, successMessage, errorMessage, }: UseOptimisticRowFlagOptions<T>): UseOptimisticRowFlagReturn<T>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/*
|
|
3
|
+
* WHY: Eight listing views grew the same per-row "flip one field" control in
|
|
4
|
+
* one change β homepage sections (`enabled`), carousel slides (`active`),
|
|
5
|
+
* the six seller listing-type pages (`status`), seller bundles, grouped
|
|
6
|
+
* listings, store categories, shipping configs and payout methods
|
|
7
|
+
* (`isActive`). Every one needs an optimistic override so the switch
|
|
8
|
+
* moves under the finger, a PATCH, an invalidate, and a rollback.
|
|
9
|
+
*
|
|
10
|
+
* That is well past the Duplication Framework's Rule of Three, and the
|
|
11
|
+
* bug-fix multiplier is the real argument: the rollback is subtly wrong
|
|
12
|
+
* in the shape everyone writes first.
|
|
13
|
+
*
|
|
14
|
+
* π THE ROLLBACK BUG THIS EXISTS TO MAKE UNREPEATABLE
|
|
15
|
+
*
|
|
16
|
+
* The obvious implementation captures the previous override and writes it
|
|
17
|
+
* back on failure:
|
|
18
|
+
*
|
|
19
|
+
* const prev = overrides[id]; // `undefined` on a row's
|
|
20
|
+
* setOverrides((o) => ({...o, [id]: v})); // FIRST toggle
|
|
21
|
+
* catch { setOverrides((o) => ({...o, [id]: prev})); }
|
|
22
|
+
*
|
|
23
|
+
* `prev` is `undefined` the first time any given row is toggled, and writing
|
|
24
|
+
* `undefined` back is not the same as removing the key β a reader doing
|
|
25
|
+
* `overrides[id] ?? row.value` gets `undefined`, which for a boolean renders
|
|
26
|
+
* as `false`. So a FAILED toggle of a row the server holds as `true` leaves
|
|
27
|
+
* the UI asserting the opposite of the truth, which is worse than not having
|
|
28
|
+
* rolled back at all. `AdminProductsView.tsx` has this shape.
|
|
29
|
+
*
|
|
30
|
+
* The fix is to DELETE the key, which is what `clear` below does.
|
|
31
|
+
*
|
|
32
|
+
* π The key is cleared on SUCCESS too, and only AFTER the invalidation has
|
|
33
|
+
* settled. `invalidateQueries` resolves once active refetches finish, so by
|
|
34
|
+
* then `mapRows` is already reading the fresh server value and there is no
|
|
35
|
+
* visible flip-back-then-forward. An override that is never cleared pins the
|
|
36
|
+
* row forever and masks every later server-side change to it.
|
|
37
|
+
*
|
|
38
|
+
* EXPORTS: useOptimisticRowFlag, UseOptimisticRowFlagOptions
|
|
39
|
+
*
|
|
40
|
+
* @tag domain:admin,seller
|
|
41
|
+
* @tag layer:hook
|
|
42
|
+
* @tag pattern:none
|
|
43
|
+
* @tag access:client
|
|
44
|
+
* @tag consumers:AdminSectionsView,AdminCarouselView,SellerListingTypeView,SellerBundlesView,SellerGroupedListingsView,SellerStoreCategoriesView,SellerShippingConfigsView,SellerPayoutMethodsView
|
|
45
|
+
* @tag sideEffects:network
|
|
46
|
+
*/
|
|
47
|
+
import React from "react";
|
|
48
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
49
|
+
import { useToast } from "../../../ui/index.js";
|
|
50
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
51
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
52
|
+
export function useOptimisticRowFlag({ mutate, invalidate, successMessage, errorMessage = "Could not save that change.", }) {
|
|
53
|
+
const queryClient = useQueryClient();
|
|
54
|
+
const { showToast } = useToast();
|
|
55
|
+
const [overrides, setOverrides] = React.useState({});
|
|
56
|
+
const [pendingId, setPendingId] = React.useState(null);
|
|
57
|
+
const clear = React.useCallback((id) => {
|
|
58
|
+
setOverrides((prev) => {
|
|
59
|
+
if (!(id in prev))
|
|
60
|
+
return prev;
|
|
61
|
+
const next = { ...prev };
|
|
62
|
+
// DELETE, never write back a captured value β see the header.
|
|
63
|
+
delete next[id];
|
|
64
|
+
return next;
|
|
65
|
+
});
|
|
66
|
+
}, []);
|
|
67
|
+
const set = React.useCallback(async (id, next) => {
|
|
68
|
+
setOverrides((prev) => ({ ...prev, [id]: next }));
|
|
69
|
+
setPendingId(id);
|
|
70
|
+
try {
|
|
71
|
+
await mutate(id, next);
|
|
72
|
+
await Promise.all(invalidate.map((queryKey) => queryClient.invalidateQueries({ queryKey })));
|
|
73
|
+
clear(id);
|
|
74
|
+
showToast(successMessage(next), "success");
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const normalized = normalizeError(err);
|
|
78
|
+
clear(id);
|
|
79
|
+
showToast(toUserMessage(normalized.code, undefined, { fallback: errorMessage }), "error");
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
setPendingId(null);
|
|
83
|
+
}
|
|
84
|
+
}, [mutate, invalidate, queryClient, clear, showToast, successMessage, errorMessage]);
|
|
85
|
+
const resolve = React.useCallback((id, serverValue) => (id in overrides ? overrides[id] : serverValue), [overrides]);
|
|
86
|
+
return { overrides, resolve, pendingId, set };
|
|
87
|
+
}
|
|
@@ -30,6 +30,21 @@ export declare class NotificationRepository extends BaseRepository<NotificationD
|
|
|
30
30
|
* Get unread notification count for a user.
|
|
31
31
|
*/
|
|
32
32
|
getUnreadCount(userId: string): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Mark a single notification UNREAD again.
|
|
35
|
+
*
|
|
36
|
+
* π `readAt` is cleared, not left behind. It is the timestamp of a read
|
|
37
|
+
* that the caller is now saying did not happen, and a row that is
|
|
38
|
+
* `isRead: false` with a `readAt` set is a contradiction the next reader has
|
|
39
|
+
* to resolve β the digest and the unread badge key on `isRead`, so they
|
|
40
|
+
* would disagree with anything that looked at `readAt`.
|
|
41
|
+
*
|
|
42
|
+
* There was no way to do this at all: the buyer's notifications page offered
|
|
43
|
+
* only Mark-read, and the PATCH route hard-coded `markAsRead(id)` without
|
|
44
|
+
* reading a body β so a notification clicked by accident was gone from the
|
|
45
|
+
* unread list for good.
|
|
46
|
+
*/
|
|
47
|
+
markAsUnread(id: string): Promise<NotificationDocument | null>;
|
|
33
48
|
/**
|
|
34
49
|
* Mark a single notification as read.
|
|
35
50
|
*/
|
|
@@ -59,6 +59,38 @@ export class NotificationRepository extends BaseRepository {
|
|
|
59
59
|
return 0;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Mark a single notification UNREAD again.
|
|
64
|
+
*
|
|
65
|
+
* π `readAt` is cleared, not left behind. It is the timestamp of a read
|
|
66
|
+
* that the caller is now saying did not happen, and a row that is
|
|
67
|
+
* `isRead: false` with a `readAt` set is a contradiction the next reader has
|
|
68
|
+
* to resolve β the digest and the unread badge key on `isRead`, so they
|
|
69
|
+
* would disagree with anything that looked at `readAt`.
|
|
70
|
+
*
|
|
71
|
+
* There was no way to do this at all: the buyer's notifications page offered
|
|
72
|
+
* only Mark-read, and the PATCH route hard-coded `markAsRead(id)` without
|
|
73
|
+
* reading a body β so a notification clicked by accident was gone from the
|
|
74
|
+
* unread list for good.
|
|
75
|
+
*/
|
|
76
|
+
async markAsUnread(id) {
|
|
77
|
+
try {
|
|
78
|
+
const now = new Date();
|
|
79
|
+
await this.getCollection()
|
|
80
|
+
.doc(id)
|
|
81
|
+
.update(prepareForFirestore({
|
|
82
|
+
[NOTIFICATION_FIELDS.IS_READ]: false,
|
|
83
|
+
[NOTIFICATION_FIELDS.READ_AT]: null,
|
|
84
|
+
[NOTIFICATION_FIELDS.UPDATED_AT]: now,
|
|
85
|
+
}));
|
|
86
|
+
return this.findById(id);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
void normalizeError(error);
|
|
90
|
+
serverLogger.error("Failed to mark notification unread", { id, error });
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
62
94
|
/**
|
|
63
95
|
* Mark a single notification as read.
|
|
64
96
|
*/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { QuickFieldDef } from "../../shell/QuickFormDrawer";
|
|
3
|
+
/**
|
|
4
|
+
* π `keywords` is a COMMA-SEPARATED STRING here, not an array.
|
|
5
|
+
*
|
|
6
|
+
* `QuickFormDrawer` has no array control β its field types are text / number
|
|
7
|
+
* / select / toggle / date / textarea / email / url. Declaring `z.string()`
|
|
8
|
+
* and splitting at the call site keeps the schema honest about what the form
|
|
9
|
+
* actually produces, rather than describing a shape no control can emit.
|
|
10
|
+
* `splitKeywords` below is the one place that conversion happens.
|
|
11
|
+
*/
|
|
12
|
+
export declare const actionIndexOverrideSchema: z.ZodObject<{
|
|
13
|
+
label: z.ZodString;
|
|
14
|
+
description: z.ZodOptional<z.ZodString>;
|
|
15
|
+
keywords: z.ZodOptional<z.ZodString>;
|
|
16
|
+
weight: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
label: string;
|
|
19
|
+
weight?: number | undefined;
|
|
20
|
+
description?: string | undefined;
|
|
21
|
+
keywords?: string | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
label: string;
|
|
24
|
+
weight?: number | undefined;
|
|
25
|
+
description?: string | undefined;
|
|
26
|
+
keywords?: string | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
export type ActionIndexOverrideFormInput = z.infer<typeof actionIndexOverrideSchema>;
|
|
29
|
+
/** The drawer's fields, in the order an admin reads them. */
|
|
30
|
+
export declare const ACTION_INDEX_OVERRIDE_FIELDS: QuickFieldDef[];
|
|
31
|
+
/** The single place the comma-separated field becomes the stored array. */
|
|
32
|
+
export declare function splitKeywords(raw: string | undefined): string[] | undefined;
|
|
33
|
+
/** The reverse, for seeding the form from a stored override. */
|
|
34
|
+
export declare function joinKeywords(keywords: readonly string[] | undefined): string;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: `/admin/action-index` told admins "Labels, descriptions and keywords
|
|
3
|
+
* can be edited here" while offering only a Shown toggle. The server has
|
|
4
|
+
* always accepted those fields β `ActionIndexOverride` carries `label`,
|
|
5
|
+
* `description`, `keywords` and `weight`, and `PATCH
|
|
6
|
+
* /api/admin/action-index` writes them through `setOverride`. The UI had
|
|
7
|
+
* simply never grown the control, so the sentence was a promise nothing
|
|
8
|
+
* kept.
|
|
9
|
+
*
|
|
10
|
+
* WHAT: The write contract for ONE entry override, as edited from the admin
|
|
11
|
+
* row action.
|
|
12
|
+
*
|
|
13
|
+
* ## What is deliberately absent
|
|
14
|
+
*
|
|
15
|
+
* `href`, `kind`, `portal` and every permission field. The view's own
|
|
16
|
+
* docstring draws this line and it is the right one: repointing a built-in
|
|
17
|
+
* route is routing, and relaxing a permission is access control. Neither
|
|
18
|
+
* belongs in a quick-edit drawer beside a label.
|
|
19
|
+
*
|
|
20
|
+
* `enabled` is also absent β it has its own control in the Shown column, and
|
|
21
|
+
* a second place to set it is a second source of truth for one boolean.
|
|
22
|
+
*
|
|
23
|
+
* EXPORTS:
|
|
24
|
+
* actionIndexOverrideSchema, ActionIndexOverrideFormInput,
|
|
25
|
+
* ACTION_INDEX_OVERRIDE_FIELDS
|
|
26
|
+
*
|
|
27
|
+
* @tag domain:admin,search
|
|
28
|
+
* @tag layer:schema
|
|
29
|
+
* @tag pattern:none
|
|
30
|
+
* @tag access:isomorphic
|
|
31
|
+
* @tag consumers:AdminActionIndexView
|
|
32
|
+
* @tag sideEffects:none
|
|
33
|
+
*/
|
|
34
|
+
import { z } from "zod";
|
|
35
|
+
/**
|
|
36
|
+
* π `keywords` is a COMMA-SEPARATED STRING here, not an array.
|
|
37
|
+
*
|
|
38
|
+
* `QuickFormDrawer` has no array control β its field types are text / number
|
|
39
|
+
* / select / toggle / date / textarea / email / url. Declaring `z.string()`
|
|
40
|
+
* and splitting at the call site keeps the schema honest about what the form
|
|
41
|
+
* actually produces, rather than describing a shape no control can emit.
|
|
42
|
+
* `splitKeywords` below is the one place that conversion happens.
|
|
43
|
+
*/
|
|
44
|
+
export const actionIndexOverrideSchema = z.object({
|
|
45
|
+
label: z
|
|
46
|
+
.string()
|
|
47
|
+
.trim()
|
|
48
|
+
.min(1, "A label is required β it is what the sidebar and search show.")
|
|
49
|
+
.max(80, "Keep labels under 80 characters so they fit the sidebar."),
|
|
50
|
+
description: z
|
|
51
|
+
.string()
|
|
52
|
+
.trim()
|
|
53
|
+
.max(200, "Keep descriptions under 200 characters.")
|
|
54
|
+
.optional(),
|
|
55
|
+
keywords: z
|
|
56
|
+
.string()
|
|
57
|
+
.trim()
|
|
58
|
+
.max(300, "Keep the keyword list under 300 characters.")
|
|
59
|
+
.optional(),
|
|
60
|
+
/*
|
|
61
|
+
* Ranking, not ordering. Higher sorts first in the search and the command
|
|
62
|
+
* palette. Bounded because an unbounded weight is how one entry ends up
|
|
63
|
+
* permanently above every other and nobody remembers why.
|
|
64
|
+
*/
|
|
65
|
+
weight: z.coerce
|
|
66
|
+
.number()
|
|
67
|
+
.int("Weight must be a whole number.")
|
|
68
|
+
.min(0, "Weight cannot be negative.")
|
|
69
|
+
.max(100, "Weight is capped at 100.")
|
|
70
|
+
.optional(),
|
|
71
|
+
});
|
|
72
|
+
/** The drawer's fields, in the order an admin reads them. */
|
|
73
|
+
export const ACTION_INDEX_OVERRIDE_FIELDS = [
|
|
74
|
+
{
|
|
75
|
+
name: "label",
|
|
76
|
+
label: "Label",
|
|
77
|
+
type: "text",
|
|
78
|
+
required: true,
|
|
79
|
+
helperText: "What the sidebar, header search and command palette show.",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "description",
|
|
83
|
+
label: "Description",
|
|
84
|
+
type: "textarea",
|
|
85
|
+
placeholder: "One line explaining what this entry is for",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "keywords",
|
|
89
|
+
label: "Keywords",
|
|
90
|
+
type: "text",
|
|
91
|
+
placeholder: "refunds, returns, money back",
|
|
92
|
+
helperText: "Comma-separated. Extra words that should find this entry in search.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "weight",
|
|
96
|
+
label: "Weight",
|
|
97
|
+
type: "number",
|
|
98
|
+
helperText: "Higher sorts first in search results. 0β100.",
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
/** The single place the comma-separated field becomes the stored array. */
|
|
102
|
+
export function splitKeywords(raw) {
|
|
103
|
+
if (raw === undefined)
|
|
104
|
+
return undefined;
|
|
105
|
+
const parts = raw
|
|
106
|
+
.split(",")
|
|
107
|
+
.map((part) => part.trim())
|
|
108
|
+
.filter(Boolean);
|
|
109
|
+
// An empty box means "no keywords", which is `[]` β not "leave them alone".
|
|
110
|
+
return parts;
|
|
111
|
+
}
|
|
112
|
+
/** The reverse, for seeding the form from a stored override. */
|
|
113
|
+
export function joinKeywords(keywords) {
|
|
114
|
+
return (keywords ?? []).join(", ");
|
|
115
|
+
}
|
|
@@ -82,13 +82,13 @@ export declare const lotteryPullSchema: z.ZodObject<{
|
|
|
82
82
|
userPhone: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
|
|
83
83
|
}, "strip", z.ZodTypeAny, {
|
|
84
84
|
transactionId: string;
|
|
85
|
-
sourceType: "
|
|
85
|
+
sourceType: "product" | "event";
|
|
86
86
|
paymentTime: string;
|
|
87
87
|
purchasedItemNumber: number;
|
|
88
88
|
userPhone?: string | undefined;
|
|
89
89
|
}, {
|
|
90
90
|
transactionId: string;
|
|
91
|
-
sourceType: "
|
|
91
|
+
sourceType: "product" | "event";
|
|
92
92
|
paymentTime: string;
|
|
93
93
|
purchasedItemNumber: number;
|
|
94
94
|
userPhone?: string | undefined;
|
|
@@ -690,6 +690,36 @@ export interface SiteSettingsDocument extends BaseDocument {
|
|
|
690
690
|
returns: {
|
|
691
691
|
windowDays: number;
|
|
692
692
|
};
|
|
693
|
+
/**
|
|
694
|
+
* The support integration's on/off switch.
|
|
695
|
+
*
|
|
696
|
+
* π The ONLY new integration flag. The other three the admin page switches
|
|
697
|
+
* already have an authoritative field β `payment.razorpayEnabled`,
|
|
698
|
+
* `messaging.emailEnabled` (which `guardSend` reads), and
|
|
699
|
+
* `shipping.provider`. Adding a parallel `integrationSwitches.*` beside
|
|
700
|
+
* those would be a denormalised mirror that drifts the first time one path
|
|
701
|
+
* writes only one of a pair (Root Cause #42), and the page would then
|
|
702
|
+
* disagree with the code that enforces it. The page is a VIEW over the real
|
|
703
|
+
* fields; this is the one field that did not exist.
|
|
704
|
+
*
|
|
705
|
+
* π Also not under `siteSettings.integrations` β that key is live and holds
|
|
706
|
+
* analytics/tag IDs (GA, FB Pixel, GTM).
|
|
707
|
+
*
|
|
708
|
+
* OFF hides the WHOLE support surface from BUYERS: no nav entry, no ticket
|
|
709
|
+
* list, no contact form. Staff keep full access, so an open ticket is still
|
|
710
|
+
* workable and the employee pay-per-ticket basis survives.
|
|
711
|
+
*
|
|
712
|
+
* π A buyer mid-conversation loses sight of their thread. That is the
|
|
713
|
+
* owner's decision, accepted knowingly β and the admin row must SAY it
|
|
714
|
+
* rather than implying the switch only stops new tickets.
|
|
715
|
+
*
|
|
716
|
+
* Defaults to `true`: support being reachable is the safe state, and a
|
|
717
|
+
* settings document written before this field existed must not read as
|
|
718
|
+
* "support is off".
|
|
719
|
+
*/
|
|
720
|
+
support: {
|
|
721
|
+
enabled: boolean;
|
|
722
|
+
};
|
|
693
723
|
faq: {
|
|
694
724
|
variables: {
|
|
695
725
|
shippingDays: number;
|
|
@@ -377,6 +377,11 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
|
|
|
377
377
|
returns: {
|
|
378
378
|
windowDays: 7,
|
|
379
379
|
},
|
|
380
|
+
// Reachable is the safe default, and an older settings document with no
|
|
381
|
+
// `support` group must not read as "support is off".
|
|
382
|
+
support: {
|
|
383
|
+
enabled: true,
|
|
384
|
+
},
|
|
380
385
|
announcementBar: {
|
|
381
386
|
enabled: true,
|
|
382
387
|
message: "π Up to 15% Off on PokΓ©mon TCG this week β Use code SAVE15",
|
|
@@ -44,6 +44,21 @@ export declare const AdminAuditActionValues: {
|
|
|
44
44
|
* same class as `payout_mark_paid` and `coupon_update`, which are both here.
|
|
45
45
|
*/
|
|
46
46
|
readonly POINTS_ADJUST: "points_adjust";
|
|
47
|
+
/**
|
|
48
|
+
* Staff added a buyer-facing dispute reason.
|
|
49
|
+
*
|
|
50
|
+
* π Here because the list is POLICY VOCABULARY, even though no single row
|
|
51
|
+
* decides money. The reasons are what buyers are offered when something goes
|
|
52
|
+
* wrong, so the wording shapes what gets reported and how it is triaged β
|
|
53
|
+
* and a badly-worded or leading option is the kind of thing that is only
|
|
54
|
+
* ever noticed later, by which time "who added this, and when" is the
|
|
55
|
+
* question. `createdByName` on the row answers it for the list; this answers
|
|
56
|
+
* it in the one place an admin looks for "what have staff done lately".
|
|
57
|
+
*
|
|
58
|
+
* Recorded only on a REAL creation. Re-adding wording that already exists
|
|
59
|
+
* returns the existing row, which is not an act worth logging.
|
|
60
|
+
*/
|
|
61
|
+
readonly DISPUTE_REASON_CREATE: "dispute_reason_create";
|
|
47
62
|
};
|
|
48
63
|
export type AdminAuditAction = (typeof AdminAuditActionValues)[keyof typeof AdminAuditActionValues];
|
|
49
64
|
export interface AdminAuditLogDocument extends BaseDocument {
|
|
@@ -97,5 +112,20 @@ export declare const ADMIN_AUDIT_LOG_FIELDS: {
|
|
|
97
112
|
* same class as `payout_mark_paid` and `coupon_update`, which are both here.
|
|
98
113
|
*/
|
|
99
114
|
readonly POINTS_ADJUST: "points_adjust";
|
|
115
|
+
/**
|
|
116
|
+
* Staff added a buyer-facing dispute reason.
|
|
117
|
+
*
|
|
118
|
+
* π Here because the list is POLICY VOCABULARY, even though no single row
|
|
119
|
+
* decides money. The reasons are what buyers are offered when something goes
|
|
120
|
+
* wrong, so the wording shapes what gets reported and how it is triaged β
|
|
121
|
+
* and a badly-worded or leading option is the kind of thing that is only
|
|
122
|
+
* ever noticed later, by which time "who added this, and when" is the
|
|
123
|
+
* question. `createdByName` on the row answers it for the list; this answers
|
|
124
|
+
* it in the one place an admin looks for "what have staff done lately".
|
|
125
|
+
*
|
|
126
|
+
* Recorded only on a REAL creation. Re-adding wording that already exists
|
|
127
|
+
* returns the existing row, which is not an act worth logging.
|
|
128
|
+
*/
|
|
129
|
+
readonly DISPUTE_REASON_CREATE: "dispute_reason_create";
|
|
100
130
|
};
|
|
101
131
|
};
|
|
@@ -42,6 +42,21 @@ export const AdminAuditActionValues = {
|
|
|
42
42
|
* same class as `payout_mark_paid` and `coupon_update`, which are both here.
|
|
43
43
|
*/
|
|
44
44
|
POINTS_ADJUST: "points_adjust",
|
|
45
|
+
/**
|
|
46
|
+
* Staff added a buyer-facing dispute reason.
|
|
47
|
+
*
|
|
48
|
+
* π Here because the list is POLICY VOCABULARY, even though no single row
|
|
49
|
+
* decides money. The reasons are what buyers are offered when something goes
|
|
50
|
+
* wrong, so the wording shapes what gets reported and how it is triaged β
|
|
51
|
+
* and a badly-worded or leading option is the kind of thing that is only
|
|
52
|
+
* ever noticed later, by which time "who added this, and when" is the
|
|
53
|
+
* question. `createdByName` on the row answers it for the list; this answers
|
|
54
|
+
* it in the one place an admin looks for "what have staff done lately".
|
|
55
|
+
*
|
|
56
|
+
* Recorded only on a REAL creation. Re-adding wording that already exists
|
|
57
|
+
* returns the existing row, which is not an act worth logging.
|
|
58
|
+
*/
|
|
59
|
+
DISPUTE_REASON_CREATE: "dispute_reason_create",
|
|
45
60
|
};
|
|
46
61
|
export const ADMIN_AUDIT_LOG_FIELDS = {
|
|
47
62
|
ACTOR_UID: "actorUid",
|
|
@@ -11,6 +11,7 @@ import { FormShellContext, useFormShellState } from "../../../ui/forms/FormShell
|
|
|
11
11
|
import { registerSchema, registerPasswordSchema } from "../schemas/index.js";
|
|
12
12
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
|
|
13
13
|
import { SectionForm, useSectionFormNav } from "../../shell/SectionForm.js";
|
|
14
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
14
15
|
const registerClientSchema = registerSchema.extend({
|
|
15
16
|
displayName: z.string().min(1, "Enter your name"),
|
|
16
17
|
confirmPassword: registerPasswordSchema,
|
|
@@ -81,7 +82,7 @@ export function RegisterForm({ onSubmit, isLoading = false, error, success, labe
|
|
|
81
82
|
* email, a rejected password β which is the one thing they can act on.
|
|
82
83
|
*/
|
|
83
84
|
const normalized = normalizeError(err);
|
|
84
|
-
setFieldError("email", normalized.
|
|
85
|
+
setFieldError("email", toUserMessage(normalized.code, undefined, { fallback: "We couldn't create your account. Please check your details and try again." }));
|
|
85
86
|
}
|
|
86
87
|
};
|
|
87
88
|
return (_jsx(Row, { className: `min-h-[60vh] ${className}`, align: "center", justify: "center", padding: "x-md", children: _jsxs(Stack, { className: "max-w-md w-full", gap: "lg", children: [_jsxs(Div, { className: "text-center", children: [_jsx(Row, { justify: "center", className: "mb-3", children: _jsx(SiteMark, { size: "lg" }) }), _jsx(Heading, { level: 1, className: "font-extrabold", size: "3xl", children: labels.title ?? "Create Account" }), (labels.subtitle || renderLoginLink) && (_jsxs(Text, { className: "mt-2 text-[var(--appkit-color-text-muted)]", size: "sm", children: [labels.subtitle ?? "Already have an account?", " ", renderLoginLink?.()] }))] }), success && (_jsx(Alert, { variant: "success", compact: true, children: success })), error && (_jsx(Alert, { variant: "error", compact: true, children: error })), _jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: values, onChange: (partial) => setValues((prev) => Object.assign({}, prev, partial)), onSubmit: handleSubmit, schema: registerClientSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, isLoading: isLoading, submitLabel: isLoading
|
|
@@ -344,6 +344,19 @@ export const ROUTE_PERMISSION_MAP = {
|
|
|
344
344
|
// Payment protection β admin-only, see the permission union's own π note.
|
|
345
345
|
"/admin/money-cases": "admin:payment-cases:read",
|
|
346
346
|
"/admin/protection-claims": "admin:protection-claims:read",
|
|
347
|
+
/*
|
|
348
|
+
* Phase 7 invoicing β the GSTR-1 draft export.
|
|
349
|
+
*
|
|
350
|
+
* π `settings:write` rather than a new permission, and that is a judgement
|
|
351
|
+
* worth stating. The page exports the company's tax position for a quarter
|
|
352
|
+
* plus every buyer's place of supply; the set of people who should see that
|
|
353
|
+
* is the set who can already change the GSTIN and the fee schedule, which is
|
|
354
|
+
* exactly what `settings:write` names. Minting
|
|
355
|
+
* `admin:invoicing:read` would add a permission with one route and one
|
|
356
|
+
* holder β a distinction with no decision behind it, which is how a
|
|
357
|
+
* permission union becomes unreadable.
|
|
358
|
+
*/
|
|
359
|
+
"/admin/invoicing": "admin:settings:write",
|
|
347
360
|
};
|
|
348
361
|
/**
|
|
349
362
|
* Every capability, as a runtime value.
|