@mohasinac/appkit 4.33.1 → 4.34.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/_internal/server/features/checkout/actions.js +9 -1
- package/dist/_internal/server/features/checkout/expand-order-items.js +9 -0
- package/dist/_internal/server/features/media/contextGuards.d.ts +3 -3
- package/dist/_internal/server/features/media/contextGuards.js +19 -5
- package/dist/_internal/server/features/orders/actions.d.ts +9 -0
- package/dist/_internal/server/features/orders/actions.js +47 -4
- package/dist/_internal/server/features/orders/adapters.js +10 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +9 -1
- package/dist/_internal/server/features/refunds/actions.js +36 -3
- package/dist/_internal/server/features/seo/sitemap.js +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +1 -1
- package/dist/_internal/server/functions/scheduled.d.ts +1 -2
- package/dist/_internal/server/functions/scheduled.js +8 -9
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +15 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +15 -6
- package/dist/_internal/server/jobs/handlers/index.d.ts +0 -1
- package/dist/_internal/server/jobs/handlers/index.js +0 -1
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +12 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +12 -0
- package/dist/_internal/shared/features/orders/return-reasons.d.ts +59 -0
- package/dist/_internal/shared/features/orders/return-reasons.js +96 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +30 -0
- package/dist/_internal/shared/features/orders/schema.js +20 -0
- package/dist/_internal/shared/features/products/schema.d.ts +12 -0
- package/dist/_internal/shared/features/products/schema.js +4 -1
- package/dist/_internal/shared/media/limits.d.ts +14 -0
- package/dist/_internal/shared/media/limits.js +29 -0
- package/dist/client.d.ts +9 -1
- package/dist/client.js +19 -1
- package/dist/features/admin/components/AdminProductEditorView.js +6 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +2 -6
- package/dist/features/admin/components/index.d.ts +0 -2
- package/dist/features/admin/components/index.js +0 -1
- package/dist/features/admin/schemas/admin-editor-forms.js +1 -1
- package/dist/features/admin/schemas/firestore.d.ts +0 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +4 -3
- package/dist/features/auth/schemas/firestore.d.ts +1 -0
- package/dist/features/contact/components/ContactForm.js +46 -31
- package/dist/features/media/upload/MediaUploadList.d.ts +16 -1
- package/dist/features/media/upload/MediaUploadList.js +58 -8
- package/dist/features/orders/components/RefundRequestView.d.ts +26 -10
- package/dist/features/orders/components/RefundRequestView.js +108 -24
- package/dist/features/orders/schemas/firestore.d.ts +42 -0
- package/dist/features/orders/types/index.d.ts +24 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +5 -3
- package/dist/features/products/components/ProductDetailPageView.js +14 -2
- package/dist/features/products/components/ProductFeatureBadges.d.ts +9 -1
- package/dist/features/products/components/ProductFeatureBadges.js +17 -1
- package/dist/features/products/components/ProductForm.js +46 -10
- package/dist/features/products/components/ProductGrid.js +10 -1
- package/dist/features/products/constants/final-sale.d.ts +40 -0
- package/dist/features/products/constants/final-sale.js +40 -0
- package/dist/features/products/index.d.ts +1 -0
- package/dist/features/products/index.js +1 -0
- package/dist/features/products/schemas/firestore.d.ts +19 -2
- package/dist/features/products/schemas/firestore.js +4 -0
- package/dist/features/products/types/index.d.ts +2 -0
- package/dist/features/seller/components/SellerProductShell.d.ts +7 -0
- package/dist/features/seller/components/SellerProductShell.js +82 -11
- package/dist/features/seller/index.d.ts +1 -0
- package/dist/features/seller/index.js +1 -0
- package/dist/features/seller/schemas/product-form.d.ts +304 -0
- package/dist/features/seller/schemas/product-form.js +132 -0
- package/dist/features/seller/utils/product-draft-mapping.d.ts +117 -0
- package/dist/features/seller/utils/product-draft-mapping.js +147 -0
- package/dist/features/shell/SectionForm.js +62 -7
- package/dist/features/shell/form-sections.js +4 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +16 -1
- package/dist/features/tester/repository/tester-checklist-item.repository.js +32 -6
- package/dist/features/tester/seed-data/events-tester-seed-data.js +77 -5
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +23 -9
- package/dist/features/tester/seed-data/products-tester-seed-data.js +65 -49
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +182 -0
- package/dist/features/tester/seed-data/tester-window.d.ts +25 -0
- package/dist/features/tester/seed-data/tester-window.js +66 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +12 -3
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +1 -0
- package/dist/schemas/registry.d.ts +271 -0
- package/dist/schemas/registry.js +9 -0
- package/dist/seed/_helpers/final-sale-fixtures.d.ts +35 -0
- package/dist/seed/_helpers/final-sale-fixtures.js +61 -0
- package/dist/seed/products-art-seed-data.js +9 -1
- package/dist/seed/products-auctions-seed-data.js +2 -1
- package/dist/seed/products-classifieds-seed-data.js +2 -1
- package/dist/seed/products-digital-codes-seed-data.js +2 -1
- package/dist/seed/products-live-items-seed-data.js +2 -1
- package/dist/seed/products-preorders-seed-data.d.ts +2 -138
- package/dist/seed/products-preorders-seed-data.js +2 -1
- package/dist/seed/products-prize-draws-seed-data.js +2 -1
- package/dist/seed/products-standard-seed-data.js +2 -1
- package/dist/seed/products-stickers-seed-data.js +9 -1
- package/dist/seed/users-seed-data.js +107 -4
- package/dist/server.d.ts +6 -2
- package/dist/server.js +15 -2
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/motion.d.ts +12 -0
- package/dist/tokens/motion.js +12 -0
- package/dist/ui/components/CollapsibleSection.d.ts +18 -1
- package/dist/ui/components/CollapsibleSection.js +20 -3
- package/dist/ui/components/Motion.js +17 -1
- package/package.json +1 -1
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
* pre-ordering against real code paths — including navigating every entry of the store
|
|
6
6
|
* detail page's listing-type dropdown (see StoreNavTabs). Auto-expires after 7 days
|
|
7
7
|
* (testerSandboxCleanup cascades into any bids on the auction).
|
|
8
|
-
* WHAT: Exports productsTesterSeedData — 2 standard + 4 auction (3 staggered
|
|
9
|
-
*
|
|
8
|
+
* WHAT: Exports productsTesterSeedData — 2 standard + 4 auction (3 staggered "still open"
|
|
9
|
+
* cycling fixtures + 1 already-ended "won" fixture) + 1 pre-order + 1 prize-draw +
|
|
10
10
|
* 1 classified + 1 digital-code + 1 live + 1 art + 1 stickers product + 1 product-group
|
|
11
11
|
* ("Set") parent (product-tester-standard-1/2 are its children, linked via groupId).
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
*
|
|
13
|
+
* Every auction end date is recomputed on each seed run from tester-window.ts, as a
|
|
14
|
+
* FRACTION of the testing window rather than a fixed duration. At the default
|
|
15
|
+
* 180-minute window the staggered three land on the historical 1h/2h/3h; a shorter
|
|
16
|
+
* TESTER_WINDOW_MINUTES pulls all of them in together, which is what lets a run
|
|
17
|
+
* actually watch an auction cross its end time.
|
|
14
18
|
*
|
|
15
19
|
* EXPORTS:
|
|
16
20
|
* productsTesterSeedData — Array of Partial<ProductDocument> for the seed runner
|
|
@@ -26,6 +30,7 @@ import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
|
26
30
|
import { buildSearchTxt } from "../../../utils/search-txt";
|
|
27
31
|
import { seedPhoto } from "../../../seed/_helpers/media";
|
|
28
32
|
import { testDataExpiresAt } from "./tester-ttl";
|
|
33
|
+
import { windowOffset } from "./tester-window";
|
|
29
34
|
const TESTER_STORE_ID = "store-tester-sandbox";
|
|
30
35
|
const TESTER_STORE_NAME = "Tester Sandbox Store";
|
|
31
36
|
const TESTER_GROUP_TITLE = "Tester Sandbox Bundle";
|
|
@@ -45,44 +50,56 @@ function withTokens(p) {
|
|
|
45
50
|
searchTxt: buildSearchTxt([p.title, p.description, p.brand, p.brandSlug, p.categoryNames, p.tags]),
|
|
46
51
|
};
|
|
47
52
|
}
|
|
48
|
-
// Staggered, incrementing-id auction fixtures so
|
|
49
|
-
//
|
|
50
|
-
// (
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
})
|
|
53
|
+
// Staggered, incrementing-id auction fixtures so a tester can watch a live auction
|
|
54
|
+
// actually cross its end time inside one session, instead of the old single fixture
|
|
55
|
+
// that always sat 5 days out. A loop (not hand-typed ids) avoids id collisions if
|
|
56
|
+
// the stagger list is ever extended.
|
|
57
|
+
//
|
|
58
|
+
// Expressed as FRACTIONS of the testing window rather than fixed hours: with the
|
|
59
|
+
// default 180-minute window these resolve to the same 1h/2h/3h this fixture has
|
|
60
|
+
// always used, and an automated run shortens all three together by setting
|
|
61
|
+
// TESTER_WINDOW_MINUTES. Hard-coded hours cannot be shortened, which is what made
|
|
62
|
+
// "watch an auction end" untestable in any run shorter than three hours.
|
|
63
|
+
const AUCTION_CYCLE_STAGGER_FRACTIONS = [1 / 3, 2 / 3, 1];
|
|
64
|
+
/** Deliberately past the end of the run — "plenty of clock left" fixtures must NOT expire mid-test. */
|
|
65
|
+
const OUTLASTS_RUN = 2;
|
|
66
|
+
/** ~1/9 of the window: 20 minutes at the default 180, 10 at a 90-minute run. Scales with the window instead of pinning 20 minutes that a short run can never reach. */
|
|
67
|
+
const EXPIRES_MID_RUN = 1 / 9;
|
|
68
|
+
const stagedAuctions = AUCTION_CYCLE_STAGGER_FRACTIONS.map((fraction, i) => {
|
|
69
|
+
const endsAt = windowOffset(fraction);
|
|
70
|
+
const endsInMinutes = Math.max(1, Math.round((endsAt.getTime() - Date.now()) / 60000));
|
|
71
|
+
return withTokens({
|
|
72
|
+
id: `auction-tester-sandbox-cycle-${i + 1}`,
|
|
73
|
+
slug: `auction-tester-sandbox-cycle-${i + 1}`,
|
|
74
|
+
title: `Test Auction — Ends in ~${endsInMinutes}m`,
|
|
75
|
+
description: `Disposable test auction for the tester QA program, staggered to end roughly ${endsInMinutes} minute(s) after seeding so testers can watch the end-of-auction flow live. Place a bid — it auto-expires in 7 days, cascading to any bids.`,
|
|
76
|
+
categorySlugs: COLLECTIBLES_CATEGORY_SLUGS,
|
|
77
|
+
categoryNames: COLLECTIBLES_CATEGORY_NAMES,
|
|
78
|
+
brandSlug: "brand-tester-sandbox",
|
|
79
|
+
brand: "TestBrand",
|
|
80
|
+
startingBid: 15000,
|
|
81
|
+
currentBid: 15000,
|
|
82
|
+
currency: "INR",
|
|
83
|
+
price: 150,
|
|
84
|
+
stockQuantity: 1,
|
|
85
|
+
availableQuantity: 1,
|
|
86
|
+
auctionEndDate: endsAt,
|
|
87
|
+
bidCount: 0,
|
|
88
|
+
bidsHaveStarted: false,
|
|
89
|
+
isSold: false,
|
|
90
|
+
mainImage: seedPhoto(`auction-image-tester-sandbox-cycle-${i + 1}-20260101`, 900, 900),
|
|
91
|
+
images: [seedPhoto(`auction-image-tester-sandbox-cycle-${i + 1}-20260101`, 900, 900)],
|
|
92
|
+
status: PRODUCT_FIELDS.STATUS_VALUES.PUBLISHED,
|
|
93
|
+
condition: PRODUCT_FIELDS.CONDITION_VALUES.NEW,
|
|
94
|
+
listingType: "auction",
|
|
95
|
+
customFields: [],
|
|
96
|
+
customSections: [],
|
|
97
|
+
isPromoted: false,
|
|
98
|
+
isOnSale: false,
|
|
99
|
+
createdAt: new Date(),
|
|
100
|
+
updatedAt: new Date(),
|
|
101
|
+
});
|
|
102
|
+
});
|
|
86
103
|
/**
|
|
87
104
|
* Buy-Now (buyout) fixtures. Four states, because the 2026-08-24 rework made
|
|
88
105
|
* buyout availability depend on `buyNowPrice > currentBid` rather than on
|
|
@@ -90,7 +107,7 @@ const stagedAuctions = AUCTION_CYCLE_STAGGER_HOURS.map((hours, i) => withTokens(
|
|
|
90
107
|
* three of them were previously unreachable as test data.
|
|
91
108
|
*
|
|
92
109
|
* Every date is Date.now()-relative at module import, per CLAUDE.md's tester
|
|
93
|
-
* fixture standard, so
|
|
110
|
+
* fixture standard, so each seed run re-arms them; durations come from tester-window.ts.
|
|
94
111
|
*/
|
|
95
112
|
const buyoutAuctions = [
|
|
96
113
|
{
|
|
@@ -101,7 +118,7 @@ const buyoutAuctions = [
|
|
|
101
118
|
buyNowPrice: 12000,
|
|
102
119
|
bidCount: 0,
|
|
103
120
|
bidsHaveStarted: false,
|
|
104
|
-
|
|
121
|
+
endsAtFraction: OUTLASTS_RUN,
|
|
105
122
|
},
|
|
106
123
|
{
|
|
107
124
|
key: "buyout-contested",
|
|
@@ -111,7 +128,7 @@ const buyoutAuctions = [
|
|
|
111
128
|
buyNowPrice: 12000,
|
|
112
129
|
bidCount: 2,
|
|
113
130
|
bidsHaveStarted: true,
|
|
114
|
-
|
|
131
|
+
endsAtFraction: OUTLASTS_RUN,
|
|
115
132
|
},
|
|
116
133
|
{
|
|
117
134
|
key: "buyout-passed",
|
|
@@ -121,7 +138,7 @@ const buyoutAuctions = [
|
|
|
121
138
|
buyNowPrice: 9000,
|
|
122
139
|
bidCount: 5,
|
|
123
140
|
bidsHaveStarted: true,
|
|
124
|
-
|
|
141
|
+
endsAtFraction: OUTLASTS_RUN,
|
|
125
142
|
},
|
|
126
143
|
{
|
|
127
144
|
key: "buyout-expiring",
|
|
@@ -131,7 +148,7 @@ const buyoutAuctions = [
|
|
|
131
148
|
buyNowPrice: 5000,
|
|
132
149
|
bidCount: 1,
|
|
133
150
|
bidsHaveStarted: true,
|
|
134
|
-
|
|
151
|
+
endsAtFraction: EXPIRES_MID_RUN,
|
|
135
152
|
},
|
|
136
153
|
].map((f) => withTokens({
|
|
137
154
|
id: `auction-tester-sandbox-${f.key}`,
|
|
@@ -149,8 +166,7 @@ const buyoutAuctions = [
|
|
|
149
166
|
price: f.currentBid,
|
|
150
167
|
stockQuantity: 1,
|
|
151
168
|
availableQuantity: 1,
|
|
152
|
-
auctionEndDate:
|
|
153
|
-
(f.endsInMinutes ? f.endsInMinutes * 60 * 1000 : (f.endsInHours ?? 6) * 60 * 60 * 1000)),
|
|
169
|
+
auctionEndDate: windowOffset(f.endsAtFraction),
|
|
154
170
|
bidCount: f.bidCount,
|
|
155
171
|
bidsHaveStarted: f.bidsHaveStarted,
|
|
156
172
|
isSold: false,
|
|
@@ -1927,6 +1927,48 @@ const rawTesterChecklistItems = [
|
|
|
1927
1927
|
},
|
|
1928
1928
|
],
|
|
1929
1929
|
},
|
|
1930
|
+
{
|
|
1931
|
+
pageKey: "return-request",
|
|
1932
|
+
pageLabel: "Requesting a return \u2014 and what final sale actually blocks",
|
|
1933
|
+
href: "/user/orders",
|
|
1934
|
+
cases: [
|
|
1935
|
+
{
|
|
1936
|
+
key: "return-cta-only-when-returnable",
|
|
1937
|
+
label: "The \"Request Return\" button appears only on a DELIVERED order inside the 7-day window",
|
|
1938
|
+
description: "Added 2026-09-04. BEFORE there was no buyer-facing return flow at all: the action existed, the CTA was registered in ACTIONS.USER, and nothing anywhere rendered it. AFTER: open a delivered order's detail page and the button is there beside Track Shipment. Open a pending or shipped one and it is not. Open a delivered order older than 7 days and the page explains the window has closed rather than offering a form that would be refused.",
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
key: "return-not-received-accepted-on-final-sale",
|
|
1942
|
+
label: "\"It never arrived\" is accepted on a FINAL SALE order",
|
|
1943
|
+
description: "This is the whole rule. Final sale refuses a change of mind; it can never refuse a claim that the buyer did not get what they paid for. On a final-sale order, choose \"It never arrived\", tick the three acknowledgements, submit \u2014 it must go through and the order must move to Return Requested.",
|
|
1944
|
+
},
|
|
1945
|
+
{
|
|
1946
|
+
key: "return-change-of-mind-refused-by-name",
|
|
1947
|
+
label: "On a final-sale order the change-of-mind reasons are NOT OFFERED, and the panel says why",
|
|
1948
|
+
description: "Open the return form for a final-sale order. A warning panel names the situation, and the reason picker lists only the five fault reasons \u2014 never-arrived, not-as-described, damaged, wrong item, counterfeit. \"I changed my mind\" must not be selectable. On a returnable order all ten reasons appear. A generic \"request refused\" with no explanation is the failure this replaced.",
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
key: "return-reason-is-persisted",
|
|
1952
|
+
label: "The reason the buyer picked is actually STORED and visible to staff",
|
|
1953
|
+
description: "BEFORE: `requestReturnAction` parsed a free-text reason and then threw it away before writing \u2014 an order reached Return Requested with nothing recorded about why, and the seller had nothing to act on. AFTER: submit a return, then open the same order in /admin/return-requests and confirm the coded reason and any note are there.",
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
key: "return-partial-gates-selected-lines-only",
|
|
1957
|
+
label: "On a mixed order, a final-sale line does not block a return of the returnable line",
|
|
1958
|
+
description: "Final sale is snapshotted PER LINE at purchase, not per order. On an order containing one final-sale item and one returnable item, a change-of-mind return scoped to the returnable line must be accepted; the same reason scoped to the final-sale line must not.",
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
key: "return-prize-draw-still-refused-entirely",
|
|
1962
|
+
label: "A prize-draw order is still refused for EVERY reason \u2014 a different, earlier gate",
|
|
1963
|
+
description: "`isNonRefundable` is a property of the listing TYPE and admits no exception, including a seller-fault one. Open a prize-draw order's refund surface: it must refuse outright and say so, without offering a reason picker at all. Confusing this with final sale in either direction is the failure.",
|
|
1964
|
+
},
|
|
1965
|
+
{
|
|
1966
|
+
key: "return-terms-snapshotted-at-purchase",
|
|
1967
|
+
label: "Changing a listing's return terms does NOT change an order already placed",
|
|
1968
|
+
description: "Buy a returnable listing. Then, as the seller, switch that listing to final sale. Go back to the order and request a change-of-mind return \u2014 it must still be ACCEPTED, because the buyer agreed to the terms shown at checkout. The order carries its own copy; a seller must not be able to withdraw a right after the sale.",
|
|
1969
|
+
},
|
|
1970
|
+
],
|
|
1971
|
+
},
|
|
1930
1972
|
]),
|
|
1931
1973
|
...group("selling", "Selling", [
|
|
1932
1974
|
{
|
|
@@ -2162,6 +2204,30 @@ const rawTesterChecklistItems = [
|
|
|
2162
2204
|
description: "BEFORE: an invalid field produced a banner listing the problem with no way to reach it, and on the site-settings page the summary was mounted outside every form context and so displayed NOTHING at all. AFTER: submit a form with a bad value (a negative refund on an order, a non-https third-party ad URL, an AdSense id that is not ca-pub-…) and the summary must list it, tag it with its section, collapse open that section when clicked, and show a count badge on the section header.",
|
|
2163
2205
|
href: "/admin/ads",
|
|
2164
2206
|
},
|
|
2207
|
+
{
|
|
2208
|
+
key: "required-section-has-no-dead-chevron",
|
|
2209
|
+
label: "A REQUIRED section renders as a plain heading \u2014 no chevron, nothing to click",
|
|
2210
|
+
description: "Fixed 2026-09-04. BEFORE: SectionDef documented a required section as rendering \"without a collapse control so it can't be hidden\", but CollapsibleSection had no way to say that, so SectionForm passed a no-op handler and the header still drew a full button with a rotating chevron. Clicking it did nothing, on ~73 required sections across the app \u2014 which reads as a broken form. AFTER: on any sectionised form (admin product, admin user, store product), the first section's header has no chevron and no hover state, while every optional section below it still expands and collapses normally.",
|
|
2211
|
+
href: "/admin/products/new",
|
|
2212
|
+
},
|
|
2213
|
+
{
|
|
2214
|
+
key: "open-section-does-not-clip-dropdowns",
|
|
2215
|
+
label: "A dropdown opened inside an expanded section is not clipped at the panel edge",
|
|
2216
|
+
description: "BEFORE: the collapse animation kept `overflow: hidden` in its SETTLED open state, not just during the transition, so anything escaping the panel box was cut off. AFTER: expand a section containing a select or picker near its bottom edge, open it, and the list must render in full rather than being sliced by the section border.",
|
|
2217
|
+
href: "/admin/products/new",
|
|
2218
|
+
},
|
|
2219
|
+
{
|
|
2220
|
+
key: "error-jump-lands-on-the-field",
|
|
2221
|
+
label: "Jumping to an error scrolls to the section AND lands focus on the offending field",
|
|
2222
|
+
description: "BEFORE: the scroll fired one animation frame after expanding, while the panel was still ~0px tall inside overflow:hidden \u2014 so focus landed against a box that was about to grow and the field could end up off-screen. AFTER: submit a form with an error inside a COLLAPSED section. It expands, scrolls, and the invalid input is both visible and focused once the animation finishes.",
|
|
2223
|
+
href: "/admin/products/new",
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
key: "long-form-typing-is-smooth",
|
|
2227
|
+
label: "Typing in a long form does not stutter",
|
|
2228
|
+
description: "BEFORE: the section list was rebuilt on every keystroke, which re-fired the validation effect on top of the validation the keystroke already triggered \u2014 two full-tree schema parses per character on a 40-field listing form. AFTER: type a long description into the seller product form. It should keep up. This is a feel test, not a pass/fail assertion; report it if it lags.",
|
|
2229
|
+
href: "/store/products/new",
|
|
2230
|
+
},
|
|
2165
2231
|
{
|
|
2166
2232
|
key: "form-mobile-action-bar",
|
|
2167
2233
|
label: "Save and Cancel appear in the pinned mobile bar, once",
|
|
@@ -2410,6 +2476,122 @@ const rawTesterChecklistItems = [
|
|
|
2410
2476
|
},
|
|
2411
2477
|
],
|
|
2412
2478
|
},
|
|
2479
|
+
{
|
|
2480
|
+
pageKey: "final-sale-authoring",
|
|
2481
|
+
pageLabel: "Final sale \u2014 the seller switch",
|
|
2482
|
+
href: "/store/products/new",
|
|
2483
|
+
cases: [
|
|
2484
|
+
{
|
|
2485
|
+
key: "final-sale-defaults-on",
|
|
2486
|
+
label: "A brand-new listing is FINAL SALE by default \u2014 the Returns section shows \"Accept change-of-mind returns\" switched OFF",
|
|
2487
|
+
description: "Added 2026-09-04. Open the Returns section on a new listing of any type. The toggle is off and the panel explains that not-received / damaged / wrong-item / not-as-described / counterfeit claims are still accepted. Absent means final sale, so a listing saved without ever opening this section is also final sale \u2014 which is the point.",
|
|
2488
|
+
href: "/store/products/new",
|
|
2489
|
+
},
|
|
2490
|
+
{
|
|
2491
|
+
key: "final-sale-opt-out-survives-reload",
|
|
2492
|
+
label: "Turning returns ON, saving, and RELOADING shows it still on",
|
|
2493
|
+
description: "This is the case that matters, and a reload is the only way to see it. `productBaseSchema` is a plain z.object() with no .passthrough(), so it strips every key it does not name \u2014 a field missing from it saves with a success toast and is gone. BEFORE such a field existed at all. AFTER: toggle Accept returns ON, Save, reload the edit page, and the toggle must still be ON. If it reverts to off, `finalSale` is being stripped on write.",
|
|
2494
|
+
href: "/store/products",
|
|
2495
|
+
},
|
|
2496
|
+
{
|
|
2497
|
+
key: "return-policy-authorable-by-seller",
|
|
2498
|
+
label: "A seller can write their own Return policy text \u2014 for the first time",
|
|
2499
|
+
description: "BEFORE: `returnPolicy` existed on the document and the ONLY editor for it was the admin product form, so a seller could never state their own terms. AFTER: the Returns section on the seller form has a Return policy textarea. Type into it, save, reload, and confirm it persists and appears on the public listing under Delivery & Returns.",
|
|
2500
|
+
href: "/store/products",
|
|
2501
|
+
},
|
|
2502
|
+
{
|
|
2503
|
+
key: "final-sale-admin-override",
|
|
2504
|
+
label: "An admin can flip the same switch on any listing, and it is the SAME field",
|
|
2505
|
+
description: "Open a listing in the admin product editor. The \"Accept change-of-mind returns\" toggle sits directly above the Return policy box and reflects whatever the seller chose. Change it, save, then reopen the SELLER edit form for that listing \u2014 it must show the admin's value. Two independent flags would be the failure.",
|
|
2506
|
+
href: "/admin/products",
|
|
2507
|
+
},
|
|
2508
|
+
{
|
|
2509
|
+
key: "final-sale-badges-render",
|
|
2510
|
+
label: "A final-sale listing shows the pill on its card AND the line on its detail page",
|
|
2511
|
+
description: "Seed data opts one listing per type OUT of final sale, so both states are visible. On /products, a final-sale listing carries a dark \"Final Sale\" pill; the opted-out one does not. On the detail page, the Delivery & Returns block always renders a returns line \u2014 even when the seller wrote no shipping or policy text at all, which previously hid the block entirely and with it the most consequential term of the sale.",
|
|
2512
|
+
href: "/products",
|
|
2513
|
+
},
|
|
2514
|
+
],
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
pageKey: "media-limits",
|
|
2518
|
+
pageLabel: "Media \u2014 ten images and one video, on every listing type",
|
|
2519
|
+
href: "/store/products/new",
|
|
2520
|
+
cases: [
|
|
2521
|
+
{
|
|
2522
|
+
key: "media-gallery-accepts-images-and-video",
|
|
2523
|
+
label: "The gallery takes BOTH images and a video, and shows a live per-type count",
|
|
2524
|
+
description: "Changed 2026-09-04. BEFORE the single-media control on the listing form was video-only (`kind=\"video\"`), so the one \"add media\" affordance a seller saw refused photos. AFTER: the gallery accepts image files and a video file in the same control, and a counter under it reads \"N/10 images \u00b7 N/1 video\" as you add them.",
|
|
2525
|
+
href: "/store/products/new",
|
|
2526
|
+
},
|
|
2527
|
+
{
|
|
2528
|
+
key: "media-tenth-image-uploads",
|
|
2529
|
+
label: "The 10th gallery image uploads \u2014 the 5th used to fail with a 400",
|
|
2530
|
+
description: "BEFORE: the gallery was capped at 5 and sent its Nth image as index N+1, while the server's signed-URL guard rejected any index above 5 \u2014 so the LAST slot of every product form returned a 400 that read as a random upload failure. AFTER: add ten images one at a time. All ten must succeed, including the tenth.",
|
|
2531
|
+
href: "/store/products/new",
|
|
2532
|
+
},
|
|
2533
|
+
{
|
|
2534
|
+
key: "media-eleventh-image-refused-client-side",
|
|
2535
|
+
label: "The 11th image is refused by the FORM with a clear message, not by the server",
|
|
2536
|
+
description: "Try to add an 11th image. The control must refuse it before uploading, naming the limit (\"You can upload up to 10 images.\"). A server-side 400 arriving after the bytes have gone up is the wrong behaviour \u2014 the user has already waited.",
|
|
2537
|
+
href: "/store/products/new",
|
|
2538
|
+
},
|
|
2539
|
+
{
|
|
2540
|
+
key: "media-second-video-refused-first-kept",
|
|
2541
|
+
label: "A second video is refused and the FIRST one is kept",
|
|
2542
|
+
description: "With one video already in the gallery, try to add another. The message must say only one video is allowed and tell you to remove the current one \u2014 and crucially the existing video must still be there afterwards. Silently replacing it would lose work.",
|
|
2543
|
+
href: "/store/products/new",
|
|
2544
|
+
},
|
|
2545
|
+
{
|
|
2546
|
+
key: "media-caps-flat-across-types",
|
|
2547
|
+
label: "The same 10 + 1 applies to an auction and a pre-order \u2014 there is no per-type table",
|
|
2548
|
+
description: "Repeat the ten-image check on /store/auctions/new and /store/pre-orders/new. Both must allow ten. BEFORE the server carried separate AUCTION_IMAGE_MAX and PREORDER_IMAGE_MAX constants, both 5, disagreeing with everything else.",
|
|
2549
|
+
href: "/store/auctions/new",
|
|
2550
|
+
},
|
|
2551
|
+
{
|
|
2552
|
+
key: "media-live-listing-still-requires-video",
|
|
2553
|
+
label: "A live listing STILL refuses to publish without a video",
|
|
2554
|
+
description: "Unchanged behaviour, re-verified because the surrounding control changed. Create a live listing with photos but no video and press Publish. It must be refused, and the Video field must be marked required. That is a requiredness rule, not a count rule, and the media-limit work must not have removed it.",
|
|
2555
|
+
href: "/store/live/new",
|
|
2556
|
+
},
|
|
2557
|
+
],
|
|
2558
|
+
},
|
|
2559
|
+
{
|
|
2560
|
+
pageKey: "listing-type-fields-roundtrip",
|
|
2561
|
+
pageLabel: "Per-type fields survive a save \u2014 all nine listing types",
|
|
2562
|
+
href: "/store/products",
|
|
2563
|
+
cases: [
|
|
2564
|
+
{
|
|
2565
|
+
key: "roundtrip-classified-meetup",
|
|
2566
|
+
label: "A classified's meetup city, locality and pincode come back after a save",
|
|
2567
|
+
description: "Fixed 2026-09-04, and this was live data loss. BEFORE: the form held `classifiedCity` while the write schema expected `classified.meetupArea.city`, nothing translated between them, and the schema strips unnamed keys \u2014 so the seller pressed Publish, got a success, and the listing saved with none of the fields that made it a classified. AFTER: fill in the meetup details, save, reopen the edit form, and every value must be there.",
|
|
2568
|
+
href: "/store/classified/new",
|
|
2569
|
+
},
|
|
2570
|
+
{
|
|
2571
|
+
key: "roundtrip-live-species-jurisdictions",
|
|
2572
|
+
label: "A live listing's species, transport method and permitted jurisdictions survive",
|
|
2573
|
+
description: "Same bug, and the most serious instance of it: `jurisdictionAllowed` is the field that prevents shipping a live animal somewhere that forbids it, and it was being dropped. Create a live listing with species, age, sex, care info, transport method, handling fee and at least one jurisdiction. Save, reopen, confirm all of it returned.",
|
|
2574
|
+
href: "/store/live/new",
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
key: "roundtrip-digital-code-delivery",
|
|
2578
|
+
label: "A digital code's delivery method, pool size and redemption instructions survive",
|
|
2579
|
+
href: "/store/digital-codes/new",
|
|
2580
|
+
},
|
|
2581
|
+
{
|
|
2582
|
+
key: "roundtrip-print-meta",
|
|
2583
|
+
label: "An art print's size, material, finish and edition size survive \u2014 and so do a sticker sheet's",
|
|
2584
|
+
description: "Art and stickers share one `printMeta` block, so check both: create one of each with all four fields filled, save, reopen.",
|
|
2585
|
+
href: "/store/art/new",
|
|
2586
|
+
},
|
|
2587
|
+
{
|
|
2588
|
+
key: "roundtrip-edit-form-opens-populated",
|
|
2589
|
+
label: "Opening the edit form for an EXISTING listing shows its per-type fields already filled",
|
|
2590
|
+
description: "The inverse half of the same bug. BEFORE: the edit pages hand-built their initial values and listed none of the per-type keys, so the form opened blank for them even when the document held real data \u2014 which read as \"my data was deleted\". AFTER: open the edit page for any seeded classified, live, digital-code, art or sticker listing and its per-type fields are populated on arrival.",
|
|
2591
|
+
href: "/store/classified",
|
|
2592
|
+
},
|
|
2593
|
+
],
|
|
2594
|
+
},
|
|
2413
2595
|
]),
|
|
2414
2596
|
...group("content-discovery", "Content & Discovery", [
|
|
2415
2597
|
{
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Reproduces the historical 1h/2h/3h auction stagger when nothing overrides it. */
|
|
2
|
+
export declare const DEFAULT_TESTER_WINDOW_MINUTES = 180;
|
|
3
|
+
/** Below this nothing is observable — a page load alone can outlast it. */
|
|
4
|
+
export declare const MIN_TESTER_WINDOW_MINUTES = 5;
|
|
5
|
+
/** The sandbox TTL is 7 days; a window longer than that outlives its own fixtures. */
|
|
6
|
+
export declare const MAX_TESTER_WINDOW_MINUTES: number;
|
|
7
|
+
/**
|
|
8
|
+
* Length of the testing window, in minutes.
|
|
9
|
+
*
|
|
10
|
+
* Read at call time rather than captured at module scope so a caller that sets the
|
|
11
|
+
* env var before importing a seed file still wins. Anything unparseable, non-finite
|
|
12
|
+
* or out of range falls back to the default rather than throwing: a malformed env
|
|
13
|
+
* var must not be able to brick `appkit-seed load`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function windowMinutes(): number;
|
|
16
|
+
/**
|
|
17
|
+
* A point `fraction` of the way through the window, measured from now.
|
|
18
|
+
*
|
|
19
|
+
* `windowOffset(0)` is now, `windowOffset(1)` is the window's end. Fractions above 1
|
|
20
|
+
* are allowed and mean "after this run" — that is how a fixture says "still open when
|
|
21
|
+
* testing finishes" without hard-coding a duration.
|
|
22
|
+
*/
|
|
23
|
+
export declare function windowOffset(fraction: number): Date;
|
|
24
|
+
/** The mirror of windowOffset, into the past — for fixtures that must already be closed. */
|
|
25
|
+
export declare function windowAgo(fraction: number): Date;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Every time-bound tester fixture — auction end dates, coupon validity, event
|
|
3
|
+
* start/end, prize-draw reveal windows, offer expiry, lottery draw windows —
|
|
4
|
+
* must open and close INSIDE one testing session. Hand-written offsets like
|
|
5
|
+
* `Date.now() + 2 * 60 * 60 * 1000` cannot do that: they bake in a duration
|
|
6
|
+
* nobody can shorten, so a case reading "watch the auction actually end" is
|
|
7
|
+
* untestable without sitting there for two hours.
|
|
8
|
+
*
|
|
9
|
+
* This file is the one knob. Fixtures express WHEN as a fraction of the run
|
|
10
|
+
* window; the window's real length comes from TESTER_WINDOW_MINUTES.
|
|
11
|
+
*
|
|
12
|
+
* WHAT: windowMinutes() / windowOffset(fraction) / windowAgo(fraction).
|
|
13
|
+
*
|
|
14
|
+
* 🛑 The default is 180 minutes ON PURPOSE — it reproduces the pre-existing 1h/2h/3h
|
|
15
|
+
* auction stagger exactly (fractions 1/3, 2/3, 1), so an ordinary `appkit-seed load`
|
|
16
|
+
* behaves as it always has. Only the automated runner shortens it. Changing this
|
|
17
|
+
* default silently re-times every fixture for human testers too.
|
|
18
|
+
*
|
|
19
|
+
* @tag domain:tester
|
|
20
|
+
* @tag layer:seed
|
|
21
|
+
* @tag pattern:none
|
|
22
|
+
* @tag access:server-only
|
|
23
|
+
* @tag consumers:seed-data/*-tester-seed-data.ts
|
|
24
|
+
* @tag sideEffects:none
|
|
25
|
+
*/
|
|
26
|
+
/** Reproduces the historical 1h/2h/3h auction stagger when nothing overrides it. */
|
|
27
|
+
export const DEFAULT_TESTER_WINDOW_MINUTES = 180;
|
|
28
|
+
/** Below this nothing is observable — a page load alone can outlast it. */
|
|
29
|
+
export const MIN_TESTER_WINDOW_MINUTES = 5;
|
|
30
|
+
/** The sandbox TTL is 7 days; a window longer than that outlives its own fixtures. */
|
|
31
|
+
export const MAX_TESTER_WINDOW_MINUTES = 7 * 24 * 60;
|
|
32
|
+
/**
|
|
33
|
+
* Length of the testing window, in minutes.
|
|
34
|
+
*
|
|
35
|
+
* Read at call time rather than captured at module scope so a caller that sets the
|
|
36
|
+
* env var before importing a seed file still wins. Anything unparseable, non-finite
|
|
37
|
+
* or out of range falls back to the default rather than throwing: a malformed env
|
|
38
|
+
* var must not be able to brick `appkit-seed load`.
|
|
39
|
+
*/
|
|
40
|
+
export function windowMinutes() {
|
|
41
|
+
const raw = process.env.TESTER_WINDOW_MINUTES;
|
|
42
|
+
if (!raw)
|
|
43
|
+
return DEFAULT_TESTER_WINDOW_MINUTES;
|
|
44
|
+
const parsed = Number(raw);
|
|
45
|
+
if (!Number.isFinite(parsed))
|
|
46
|
+
return DEFAULT_TESTER_WINDOW_MINUTES;
|
|
47
|
+
if (parsed < MIN_TESTER_WINDOW_MINUTES)
|
|
48
|
+
return MIN_TESTER_WINDOW_MINUTES;
|
|
49
|
+
if (parsed > MAX_TESTER_WINDOW_MINUTES)
|
|
50
|
+
return MAX_TESTER_WINDOW_MINUTES;
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A point `fraction` of the way through the window, measured from now.
|
|
55
|
+
*
|
|
56
|
+
* `windowOffset(0)` is now, `windowOffset(1)` is the window's end. Fractions above 1
|
|
57
|
+
* are allowed and mean "after this run" — that is how a fixture says "still open when
|
|
58
|
+
* testing finishes" without hard-coding a duration.
|
|
59
|
+
*/
|
|
60
|
+
export function windowOffset(fraction) {
|
|
61
|
+
return new Date(Date.now() + fraction * windowMinutes() * 60000);
|
|
62
|
+
}
|
|
63
|
+
/** The mirror of windowOffset, into the past — for fixtures that must already be closed. */
|
|
64
|
+
export function windowAgo(fraction) {
|
|
65
|
+
return windowOffset(-fraction);
|
|
66
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1095,7 +1095,6 @@ export { AdminReviewsView } from "./features/admin/index";
|
|
|
1095
1095
|
export { AdminSectionsView } from "./features/admin/index";
|
|
1096
1096
|
export { AdminSessionsManager } from "./features/admin/index";
|
|
1097
1097
|
export { AdminSidebar } from "./features/admin/index";
|
|
1098
|
-
export { AdminSiteView } from "./features/admin/index";
|
|
1099
1098
|
export { AdminSublistingCategoriesView } from "./features/admin/index";
|
|
1100
1099
|
export { AdminSublistingCategoryEditorView } from "./features/admin/index";
|
|
1101
1100
|
export type { AdminSublistingCategoryEditorViewProps } from "./features/admin/index";
|
|
@@ -1198,7 +1197,6 @@ export type { AdminReviewsViewProps } from "./features/admin/index";
|
|
|
1198
1197
|
export type { AdminSectionsViewProps } from "./features/admin/index";
|
|
1199
1198
|
export type { AdminSessionsManagerProps } from "./features/admin/index";
|
|
1200
1199
|
export type { AdminSidebarProps } from "./features/admin/index";
|
|
1201
|
-
export type { AdminSiteViewProps } from "./features/admin/index";
|
|
1202
1200
|
export type { AdminSiteSettingsViewProps } from "./features/admin/index";
|
|
1203
1201
|
export type { AdminStatCardProps } from "./features/admin/index";
|
|
1204
1202
|
export type { AdminStoresViewProps } from "./features/admin/index";
|
|
@@ -2175,6 +2173,7 @@ export { RefundHistoryTable } from "./features/orders/index";
|
|
|
2175
2173
|
export type { RefundHistoryTableProps } from "./features/orders/index";
|
|
2176
2174
|
export { RefundRequestView } from "./features/orders/index";
|
|
2177
2175
|
export type { RefundRequestViewProps } from "./features/orders/index";
|
|
2176
|
+
export type { RefundRequestSubmission } from "./features/orders/components/RefundRequestView";
|
|
2178
2177
|
export { PaymentGatewayValues } from "./features/orders/index";
|
|
2179
2178
|
export { PaymentMethodValues } from "./features/orders/index";
|
|
2180
2179
|
export { OutOfStockPolicyValues } from "./features/orders/index";
|
|
@@ -2386,6 +2385,7 @@ export { getProductFilterKeys } from "./features/products/index";
|
|
|
2386
2385
|
export { getProductSortOptions } from "./features/products/index";
|
|
2387
2386
|
export { getProductTableColumns } from "./features/products/index";
|
|
2388
2387
|
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, isArtListing, isStickersListing, } from "./features/products/index";
|
|
2388
|
+
export { isFinalSale, acceptsChangeOfMindReturns, } from "./features/products/constants/final-sale";
|
|
2389
2389
|
export { isListingTypeEnabled, isCategoryTypeEnabled, enabledListingTypes, enabledCategoryTypes, ALL_LISTING_TYPES, } from "./_internal/shared/listing-types/feature-flags";
|
|
2390
2390
|
export { actionTracker, setActionTrackerSink, resetActionTrackerSink, type ActionEvent, type ActionTrackerSink, } from "./_internal/shared/listing-types/action-tracker";
|
|
2391
2391
|
export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_internal/shared/listing-types/cart-shipping";
|
|
@@ -3072,6 +3072,8 @@ export type { BundleQueryRule } from "./features/categories/schemas/firestore";
|
|
|
3072
3072
|
export { BUNDLE_MIN_ITEMS, BUNDLE_MAX_ITEMS, BUNDLE_MAX_PER_USER_DEFAULT, BUNDLES_PAGE_SIZE, BUNDLES_FEATURED_LIMIT, } from "./_internal/shared/features/categories/bundle-config";
|
|
3073
3073
|
export { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "./_internal/shared/features/categories/bundle-copy";
|
|
3074
3074
|
export { REFUND_COPY } from "./_internal/shared/features/orders/refund-copy";
|
|
3075
|
+
export { RETURN_REASON, FINAL_SALE_EXEMPT_REASONS, RETURN_REASON_LABEL, RETURN_REASON_GROUP_LABEL, isFinalSaleExempt, isReturnReason, selectableReturnReasons, } from "./_internal/shared/features/orders/return-reasons";
|
|
3076
|
+
export type { ReturnReason } from "./_internal/shared/features/orders/return-reasons";
|
|
3075
3077
|
export { bundleCreateSchema, bundleUpdateSchema, bundleQueryRuleSchema, bundleDisplaySchema, type BundleCreateBody, type BundleUpdateBody, } from "./_internal/shared/features/categories/bundle-schemas";
|
|
3076
3078
|
export type { GroupedListingDocument, GroupTheme, GroupVisibility } from "./features/grouped/schemas/firestore";
|
|
3077
3079
|
export { navItemFormSchema, navItemCreateSchema, navItemUpdateSchema, type NavItemFormValues, } from "./features/admin/schemas/nav-item-form";
|
|
@@ -3144,7 +3146,7 @@ export { LISTING_TYPE_REGISTRY, pluginFor, detectListingTypeFromSlug } from "./_
|
|
|
3144
3146
|
export type { ListingTypePlugin } from "./_internal/shared/listing-types/_registry";
|
|
3145
3147
|
export { getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_internal/shared/checkout/rules";
|
|
3146
3148
|
export type { ListingCheckoutRule, CategoryCheckoutRule, CartItemProductPair, OrderItemInput, RefundPolicy, } from "./_internal/shared/checkout/rules";
|
|
3147
|
-
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, } from "./_internal/shared/media/limits";
|
|
3149
|
+
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
|
|
3148
3150
|
export type { MediaKind, AllowedImageMime, AllowedVideoMime, AllowedDocMime, AllowedMime, } from "./_internal/shared/media/limits";
|
|
3149
3151
|
export { PRODUCT_FIELDS, PRODUCT_STATUS_TRANSITIONS, ORDER_FIELDS, REVIEW_FIELDS, BID_FIELDS, AD_FIELDS, AD_TRACKED_FIELDS, BLOG_FIELDS, BRAND_FIELDS, WISHLIST_FIELDS, HISTORY_FIELDS, COUPON_USAGE_FIELDS, CAROUSEL_FIELDS, FAQ_FIELDS, HOMEPAGE_SECTION_FIELDS, SITE_SETTINGS_FIELDS, COMMON_FIELDS, OAUTH_STATE_VALUES, SCHEMA_DEFAULTS, } from "./constants/field-names";
|
|
3150
3152
|
export { SORT_DIR, sortBy } from "./constants/sort";
|
|
@@ -3226,3 +3228,4 @@ export { ROLES_TABS, isRolesTabId, ADMIN_SETTINGS_TABS, isAdminSettingsTabId, }
|
|
|
3226
3228
|
export type { AdminPageTab, RolesTabId, AdminSettingsTabId, } from "./features/admin/constants/admin-page-tabs";
|
|
3227
3229
|
export { validateActionIndexEntry, normaliseRoute, } from "./features/search/action-index/validate-entry";
|
|
3228
3230
|
export type { ActionIndexValidationContext, ActionIndexValidationIssue, } from "./features/search/action-index/validate-entry";
|
|
3231
|
+
export { draftToProductInput, productToDraft, } from "./features/seller/utils/product-draft-mapping";
|
package/dist/index.js
CHANGED
|
@@ -2126,8 +2126,6 @@ export { AdminSessionsManager } from "./features/admin/index";
|
|
|
2126
2126
|
// AdminSidebar - Shared export for admin sidebar.
|
|
2127
2127
|
export { AdminSidebar } from "./features/admin/index";
|
|
2128
2128
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
2129
|
-
// AdminSiteView - Component for admin site view.
|
|
2130
|
-
export { AdminSiteView } from "./features/admin/index";
|
|
2131
2129
|
// AdminSublistingCategoriesView - Admin list view for sublisting categories (SC1).
|
|
2132
2130
|
export { AdminSublistingCategoriesView } from "./features/admin/index";
|
|
2133
2131
|
// AdminSublistingCategoryEditorView - Admin create/edit form for sublisting categories (SC1).
|
|
@@ -4367,6 +4365,9 @@ export { getProductTableColumns } from "./features/products/index";
|
|
|
4367
4365
|
// normalizeListingType + predicate helpers â€" canonical SB1-G accessors.
|
|
4368
4366
|
// SB-UNI-F 2026-05-13 â€" Phase 2 predicates added (classified/digital-code/live).
|
|
4369
4367
|
export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, isArtListing, isStickersListing, } from "./features/products/index";
|
|
4368
|
+
// Final sale — ABSENT MEANS TRUE, so these are the only legal reads of the
|
|
4369
|
+
// `finalSale` field. See the module docstring for why `!p.finalSale` is wrong.
|
|
4370
|
+
export { isFinalSale, acceptsChangeOfMindReturns, } from "./features/products/constants/final-sale";
|
|
4370
4371
|
// SB-UNI-X4 2026-05-13 â€" per-type feature-flag helpers.
|
|
4371
4372
|
export { isListingTypeEnabled, isCategoryTypeEnabled, enabledListingTypes, enabledCategoryTypes, ALL_LISTING_TYPES, } from "./_internal/shared/listing-types/feature-flags";
|
|
4372
4373
|
// SB-UNI-X5 2026-05-13 â€" action telemetry sink.
|
|
@@ -5454,6 +5455,10 @@ export { BUNDLE_MIN_ITEMS, BUNDLE_MAX_ITEMS, BUNDLE_MAX_PER_USER_DEFAULT, BUNDLE
|
|
|
5454
5455
|
export { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "./_internal/shared/features/categories/bundle-copy";
|
|
5455
5456
|
// S-SBUNI-RULES 2026-05-13 â€" shared copy for refund / shipping / sibling-payment UI.
|
|
5456
5457
|
export { REFUND_COPY } from "./_internal/shared/features/orders/refund-copy";
|
|
5458
|
+
// Return / refund reasons — the closed enum the final-sale gate branches on.
|
|
5459
|
+
// See the module docstring for why the exempt set is "seller or carrier fault"
|
|
5460
|
+
// rather than "serious reasons".
|
|
5461
|
+
export { RETURN_REASON, FINAL_SALE_EXEMPT_REASONS, RETURN_REASON_LABEL, RETURN_REASON_GROUP_LABEL, isFinalSaleExempt, isReturnReason, selectableReturnReasons, } from "./_internal/shared/features/orders/return-reasons";
|
|
5457
5462
|
// S-SBUNI-4 follow-up â€" shared admin bundle zod schemas.
|
|
5458
5463
|
export { bundleCreateSchema, bundleUpdateSchema, bundleQueryRuleSchema, bundleDisplaySchema, } from "./_internal/shared/features/categories/bundle-schemas";
|
|
5459
5464
|
export { navItemFormSchema, navItemCreateSchema, navItemUpdateSchema, } from "./features/admin/schemas/nav-item-form";
|
|
@@ -5520,7 +5525,7 @@ export { LISTING_TYPE_REGISTRY, pluginFor, detectListingTypeFromSlug } from "./_
|
|
|
5520
5525
|
// Checkout rule registry â€" pure TS, safe in both client and server bundles.
|
|
5521
5526
|
export { getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_internal/shared/checkout/rules";
|
|
5522
5527
|
// Media upload limits â€" pure constants, safe in both client and server bundles.
|
|
5523
|
-
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, } from "./_internal/shared/media/limits";
|
|
5528
|
+
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
|
|
5524
5529
|
// ── Field name constants — canonical source of truth for all Firestore field names ──
|
|
5525
5530
|
// NOTE: STORE_FIELDS, CATEGORY_FIELDS, EVENT_FIELDS, EVENT_ENTRY_FIELDS, PAYOUT_FIELDS,
|
|
5526
5531
|
// COUPON_FIELDS, CART_FIELDS, USER_FIELDS, ADDRESS_FIELDS, NOTIFICATION_FIELDS,
|
|
@@ -5629,3 +5634,7 @@ export { SITE_SETTINGS_TABS, SITE_SETTINGS_TAB_IDS, isSiteSettingsTabId, } from
|
|
|
5629
5634
|
export { PAYOUTS_TABS, isPayoutsTabId, SHIPPING_TABS, isShippingTabId, FULFILLMENT_TABS, isFulfillmentTabId, STOREFRONT_TABS, isStorefrontTabId, } from "./features/seller/constants/store-tabs";
|
|
5630
5635
|
export { ROLES_TABS, isRolesTabId, ADMIN_SETTINGS_TABS, isAdminSettingsTabId, } from "./features/admin/constants/admin-page-tabs";
|
|
5631
5636
|
export { validateActionIndexEntry, normaliseRoute, } from "./features/search/action-index/validate-entry";
|
|
5637
|
+
// Seller listing form draft <-> product write payload. The flat form keys and
|
|
5638
|
+
// the nested document blocks are different shapes, and nothing translated
|
|
5639
|
+
// between them — see the module docstring.
|
|
5640
|
+
export { draftToProductInput, productToDraft, } from "./features/seller/utils/product-draft-mapping";
|
|
@@ -146,6 +146,7 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
146
146
|
readonly ORDER_DETAIL: (id: string) => string;
|
|
147
147
|
readonly ORDER_CANCEL: (id: string) => string;
|
|
148
148
|
readonly ORDER_TRACK: (id: string) => string;
|
|
149
|
+
readonly ORDER_RETURN: (id: string) => string;
|
|
149
150
|
readonly ORDER_INVOICE: (id: string) => string;
|
|
150
151
|
readonly ORDER_PAYMENT: (id: string) => string;
|
|
151
152
|
readonly NOTIFICATIONS: "/user/notifications";
|
|
@@ -549,6 +550,7 @@ export declare const ROUTES: {
|
|
|
549
550
|
readonly ORDER_DETAIL: (id: string) => string;
|
|
550
551
|
readonly ORDER_CANCEL: (id: string) => string;
|
|
551
552
|
readonly ORDER_TRACK: (id: string) => string;
|
|
553
|
+
readonly ORDER_RETURN: (id: string) => string;
|
|
552
554
|
readonly ORDER_INVOICE: (id: string) => string;
|
|
553
555
|
readonly ORDER_PAYMENT: (id: string) => string;
|
|
554
556
|
readonly NOTIFICATIONS: "/user/notifications";
|
|
@@ -150,6 +150,7 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
150
150
|
ORDER_DETAIL: (id) => `/user/orders/view/${id}`,
|
|
151
151
|
ORDER_CANCEL: (id) => `/user/orders/${id}/cancel`,
|
|
152
152
|
ORDER_TRACK: (id) => `/user/orders/${id}/track`,
|
|
153
|
+
ORDER_RETURN: (id) => `/user/orders/${id}/return`,
|
|
153
154
|
ORDER_INVOICE: (id) => `/user/orders/${id}/invoice`,
|
|
154
155
|
ORDER_PAYMENT: (id) => `/user/orders/${id}/payment`,
|
|
155
156
|
NOTIFICATIONS: "/user/notifications",
|