@mohasinac/appkit 4.42.1 → 4.42.2
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/offers/adapters.d.ts +29 -7
- package/dist/_internal/server/features/offers/adapters.js +14 -3
- package/dist/_internal/server/features/orders/adapters.js +54 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.js +1 -0
- package/dist/features/account/components/UserOrdersView.js +30 -2
- package/dist/features/account/hooks/useAddresses.d.ts +21 -6
- package/dist/features/account/hooks/useAddresses.js +1 -1
- package/dist/features/addresses/hooks/usePostalLookup.js +84 -12
- package/dist/features/admin/components/AdminCategoriesView.js +37 -2
- package/dist/features/admin/components/AdminOffersView.js +2 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +25 -0
- package/dist/features/admin/constants/filter-tabs.js +30 -0
- package/dist/features/admin/hooks/useAdminListingData.d.ts +35 -0
- package/dist/features/admin/hooks/useAdminListingData.js +45 -9
- package/dist/features/cart/schemas/index.d.ts +15 -0
- package/dist/features/cart/schemas/index.js +2 -0
- package/dist/features/cart/types/index.d.ts +22 -0
- package/dist/features/categories/components/CategoriesIndexListing.js +19 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +14 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +8 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +1 -0
- package/dist/features/events/actions/event-actions.d.ts +13 -0
- package/dist/features/events/actions/event-actions.js +75 -1
- package/dist/features/events/repository/event-entry.repository.d.ts +17 -0
- package/dist/features/events/repository/event-entry.repository.js +36 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +21 -3
- package/dist/features/orders/components/OrdersList.js +2 -1
- package/dist/features/orders/index.d.ts +1 -0
- package/dist/features/orders/index.js +1 -0
- package/dist/features/orders/types/index.d.ts +2 -0
- package/dist/features/orders/utils/order-ref.d.ts +45 -0
- package/dist/features/orders/utils/order-ref.js +58 -0
- package/dist/features/products/components/MakeOfferButton.d.ts +18 -1
- package/dist/features/products/components/MakeOfferButton.js +23 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +5 -0
- package/dist/features/products/components/ProductDetailPageView.js +3 -2
- package/dist/features/products/repository/products.repository.js +29 -2
- package/dist/features/seller/actions/offer-actions.js +32 -0
- package/dist/features/seller/components/QuickProductForm.js +15 -1
- package/dist/features/seller/components/SellerBidsView.js +3 -1
- package/dist/features/seller/components/SellerOffersView.js +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +49 -6
- package/dist/features/seller/components/SellerProductsCards.js +23 -1
- package/dist/features/seller/components/SellerProductsView.js +27 -2
- package/dist/features/seller/hooks/useSellerListingData.d.ts +0 -1
- package/dist/features/seller/hooks/useSellerListingData.js +17 -19
- package/dist/features/shell/SectionForm.js +33 -2
- package/dist/features/tester/seed-data/_happy-path.d.ts +23 -0
- package/dist/features/tester/seed-data/_happy-path.js +710 -0
- package/dist/features/tester/seed-data/_money-flows.js +34 -23
- package/dist/features/tester/seed-data/authored/account-auth__profile-settings.js +22 -3
- package/dist/features/tester/seed-data/authored/addresses__address-filters.js +17 -12
- package/dist/features/tester/seed-data/authored/addresses__postal-lookup.js +7 -6
- package/dist/features/tester/seed-data/authored/addresses__postal-validation.js +10 -4
- package/dist/features/tester/seed-data/authored/addresses__state-picker.js +13 -8
- package/dist/features/tester/seed-data/authored/addresses__unban-request.js +49 -11
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +10 -6
- package/dist/features/tester/seed-data/authored/admin__bans-and-trust.js +71 -32
- package/dist/features/tester/seed-data/authored/admin__blog-faqs.js +80 -31
- package/dist/features/tester/seed-data/authored/admin__bug-hunter-rewards.js +62 -25
- package/dist/features/tester/seed-data/authored/admin__bulk-actions.js +61 -31
- package/dist/features/tester/seed-data/authored/admin__bundles.js +84 -37
- package/dist/features/tester/seed-data/authored/admin__buyer-data-admin.js +90 -57
- package/dist/features/tester/seed-data/authored/admin__catalog-listings.js +38 -26
- package/dist/features/tester/seed-data/authored/admin__category-brand-authoring.js +47 -28
- package/dist/features/tester/seed-data/authored/admin__classifieds-digitalcodes-live.js +118 -31
- package/dist/features/tester/seed-data/authored/admin__content-deletes.js +30 -24
- package/dist/features/tester/seed-data/authored/admin__content-marketing.js +83 -46
- package/dist/features/tester/seed-data/authored/admin__coupons.js +8 -8
- package/dist/features/tester/seed-data/authored/admin__events-raffles-spin.js +9 -9
- package/dist/features/tester/seed-data/authored/admin__firebase-function-effects.js +19 -4
- package/dist/features/tester/seed-data/authored/admin__media-watermark.js +77 -33
- package/dist/features/tester/seed-data/authored/admin__orders-fulfillment.js +5 -5
- package/dist/features/tester/seed-data/authored/admin__prize-draws-lotteries.js +29 -5
- package/dist/features/tester/seed-data/authored/admin__site-system.js +45 -23
- package/dist/features/tester/seed-data/authored/admin__uncovered-admin-pages.js +53 -28
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +20 -20
- package/dist/features/tester/seed-data/authored/buying__bidding.js +19 -2
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +20 -5
- package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +28 -10
- package/dist/features/tester/seed-data/authored/buying__cart.js +10 -3
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +0 -16
- package/dist/features/tester/seed-data/authored/buying__my-orders.js +27 -5
- package/dist/features/tester/seed-data/authored/buying__offers.js +22 -7
- package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +3 -3
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +35 -4
- package/dist/features/tester/seed-data/authored/buying__return-request.js +19 -6
- package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +36 -18
- package/dist/features/tester/seed-data/authored/buying__reviews.js +16 -1
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-navigation.js +2 -1
- package/dist/features/tester/seed-data/authored/buying__user-uncovered-pages.js +16 -1
- package/dist/features/tester/seed-data/authored/community-support__public-profile.js +3 -2
- package/dist/features/tester/seed-data/authored/community-support__support-tickets.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__blog.js +19 -2
- package/dist/features/tester/seed-data/authored/content-discovery__category-brand-relations.js +4 -4
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +5 -5
- package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +41 -6
- package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +63 -8
- package/dist/features/tester/seed-data/authored/content-discovery__event-participation.js +29 -29
- package/dist/features/tester/seed-data/authored/content-discovery__events.js +2 -2
- package/dist/features/tester/seed-data/authored/content-discovery__faq-help.js +23 -1
- package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +6 -6
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +1 -1
- package/dist/features/tester/seed-data/authored/content-discovery__store-reviews-aggregate.js +1 -1
- package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +31 -6
- package/dist/features/tester/seed-data/authored/cta-layout__dialog-footers.js +34 -10
- package/dist/features/tester/seed-data/authored/cta-layout__editor-action-bar.js +35 -8
- package/dist/features/tester/seed-data/authored/cta-layout__navbar-ctas.js +31 -9
- package/dist/features/tester/seed-data/authored/cta-layout__product-bottom-bar.js +29 -3
- package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +10 -10
- package/dist/features/tester/seed-data/authored/design-ux__dashboard-layout.js +3 -3
- package/dist/features/tester/seed-data/authored/design-ux__footer-theme.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__form-validation-errors.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__general-design.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__hand-mode-layout.js +1 -1
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +8 -8
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +2 -2
- package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +39 -16
- package/dist/features/tester/seed-data/authored/page-wiring__detail-pages.js +43 -8
- package/dist/features/tester/seed-data/authored/page-wiring__drawer-pages.js +40 -14
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +31 -14
- package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +7 -6
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +14 -5
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__help-how-it-works.js +1 -1
- package/dist/features/tester/seed-data/authored/public-pages__help-scams-guides-subpages.js +4 -2
- package/dist/features/tester/seed-data/authored/public-pages__legal-policy-pages.js +3 -3
- package/dist/features/tester/seed-data/authored/public-pages__newly-wired-browse-indexes.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__store-tabs.js +5 -5
- package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +3 -3
- package/dist/features/tester/seed-data/authored/search-and-nav__employee-permissions.js +45 -11
- package/dist/features/tester/seed-data/authored/search-and-nav__header-search.js +57 -17
- package/dist/features/tester/seed-data/authored/search-and-nav__settings-deep-links.js +36 -5
- package/dist/features/tester/seed-data/authored/search-and-nav__sidebar-search.js +29 -4
- package/dist/features/tester/seed-data/authored/selling__become-seller.js +23 -0
- package/dist/features/tester/seed-data/authored/selling__final-sale-authoring.js +14 -13
- package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +14 -9
- package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +34 -7
- package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +17 -7
- package/dist/features/tester/seed-data/authored/selling__listing-type-fields-roundtrip.js +9 -9
- package/dist/features/tester/seed-data/authored/selling__media-limits.js +9 -4
- package/dist/features/tester/seed-data/authored/selling__product-upload-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__quick-add-minimum-details.js +3 -3
- package/dist/features/tester/seed-data/authored/selling__sectionised-forms.js +25 -1
- package/dist/features/tester/seed-data/authored/selling__seller-analytics-payouts.js +27 -6
- package/dist/features/tester/seed-data/authored/selling__seller-bids-bundles-filters.js +19 -1
- package/dist/features/tester/seed-data/authored/selling__seller-catalog-org.js +63 -22
- package/dist/features/tester/seed-data/authored/selling__seller-custom-brands.js +9 -5
- package/dist/features/tester/seed-data/authored/selling__seller-guide.js +7 -6
- package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +3 -2
- package/dist/features/tester/seed-data/authored/selling__seller-marketing-extras.js +18 -18
- package/dist/features/tester/seed-data/authored/selling__seller-ops-comms.js +13 -12
- package/dist/features/tester/seed-data/authored/selling__seller-orders-at-scale.js +19 -3
- package/dist/features/tester/seed-data/authored/selling__seller-orders.js +22 -4
- package/dist/features/tester/seed-data/authored/selling__seller-shipping-payouts-setup.js +20 -8
- package/dist/features/tester/seed-data/authored/selling__store-dashboard-navigation.js +4 -4
- package/dist/features/tester/seed-data/authored/selling__store-uncovered-pages.js +2 -2
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +5 -5
- package/dist/features/tester/seed-data/authored/seo__og-images.js +1 -1
- package/dist/features/tester/seed-data/authored/seo__page-metadata.js +7 -7
- package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +3 -3
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +123 -21
- package/dist/features/tester/utils/phases.d.ts +17 -0
- package/dist/features/tester/utils/phases.js +29 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/seo/json-ld.d.ts +23 -0
- package/dist/seo/json-ld.js +3 -2
- package/dist/server.d.ts +1 -0
- package/dist/server.js +2 -0
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/package.json +1 -1
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
* chips against the real union rather than trusting the labels.
|
|
14
14
|
*
|
|
15
15
|
* Categories are the other theme: a product carries its FULL ancestor chain so a
|
|
16
|
-
* parent page can match on its own id alone
|
|
17
|
-
*
|
|
16
|
+
* parent page can match on its own id alone. That chain IS derived on write, by
|
|
17
|
+
* `ProductRepository.deriveTaxonomy()`, from create and update alike — this header
|
|
18
|
+
* said the opposite until 2026-09-29 and told the tester a one-element chain was
|
|
19
|
+
* expected. A form cannot supply the chain itself (the create schema strips it), so
|
|
20
|
+
* if the derivation stops running there is no fallback and the product silently
|
|
21
|
+
* reaches only its leaf page.
|
|
18
22
|
*
|
|
19
23
|
* @tag domain:tester
|
|
20
24
|
* @tag layer:seed
|
|
@@ -26,10 +30,10 @@
|
|
|
26
30
|
export const authored = {
|
|
27
31
|
"checklist-admin-catalog-listings-brands-crud": {
|
|
28
32
|
roles: ["admin", "guest"],
|
|
29
|
-
startPage: "/admin/
|
|
33
|
+
startPage: "/admin/brands",
|
|
30
34
|
steps: [
|
|
31
35
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
32
|
-
"Open
|
|
36
|
+
"Open /admin/brands and create one named 'QA Brand admin-crud' with a website, a country and a founding year.",
|
|
33
37
|
"Upload public/test-media/sample-image.png to the field labelled for the hero or cover image.",
|
|
34
38
|
"Save, RELOAD, and read every field.",
|
|
35
39
|
"Open /brands/{slug} in a private window and read the hero banner and the 'About this brand' panel.",
|
|
@@ -64,17 +68,24 @@ export const authored = {
|
|
|
64
68
|
startPage: "/admin/products",
|
|
65
69
|
steps: [
|
|
66
70
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
67
|
-
"Open /admin/products and create a product titled 'QA Product admin-crud' at 600 with
|
|
71
|
+
"Open /admin/products and create a product titled 'QA Product admin-crud' at 600 with stock 3, an image, the brand Beyblade, and the category Burst Parts — which sits two levels below the root.",
|
|
68
72
|
"Save, RELOAD, and read every field.",
|
|
69
73
|
"Open its public page and read the title, price, stock badge, category links and brand link.",
|
|
70
|
-
"Count the category links shown.",
|
|
74
|
+
"Count the category links shown and read their names.",
|
|
71
75
|
"Edit ONLY the price to 700, save, RELOAD, and compare every other field.",
|
|
72
76
|
"Delete the product and confirm the public page no longer resolves.",
|
|
73
77
|
],
|
|
74
|
-
inputs: {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
inputs: {
|
|
79
|
+
title: "QA Product admin-crud",
|
|
80
|
+
priceBefore: 600,
|
|
81
|
+
priceAfter: 700,
|
|
82
|
+
stock: 3,
|
|
83
|
+
categoryId: "category-burst-parts",
|
|
84
|
+
brand: "Beyblade",
|
|
85
|
+
},
|
|
86
|
+
expectedBehaviour: "An admin create and edit round-trip, and an edit writes back only what changed. The category links are the second assertion: the repository derives the full ancestor chain on write, so a product filed under Burst Parts carries Beyblade Burst and Spinning Tops with it. The brand link is the same shape — products are matched to a brand by DISPLAY NAME while the picker's value is the brand row's id, so that too is resolved on write rather than stored as picked.",
|
|
87
|
+
expectedUiState: "After each reload the values hold and only the price differs after the edit. THREE category links: Burst Parts, Beyblade Burst, Spinning Tops. The brand link resolves to a page that lists this product rather than an empty one. One category link, or a brand page that does not list it, are both write-path failures and both look entirely normal on the product's own page. After the delete the public URL no longer resolves.",
|
|
88
|
+
expectedData: { unintendedFieldChanges: 0, categoryLinksShown: 3 },
|
|
78
89
|
endResult: "The product is deleted. Report the category link count either way.",
|
|
79
90
|
},
|
|
80
91
|
"checklist-admin-catalog-listings-products-default-listing-not-empty": {
|
|
@@ -161,14 +172,15 @@ export const authored = {
|
|
|
161
172
|
startPage: "/admin/products",
|
|
162
173
|
steps: [
|
|
163
174
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
164
|
-
"Open each per-type admin listing in turn —
|
|
175
|
+
"Open each per-type admin listing in turn, BY URL — there are exactly six and only /admin/classified is linked from the sidebar: /admin/prize-draws, /admin/classified, /admin/digital-codes, /admin/live, /admin/art, /admin/stickers.",
|
|
165
176
|
"On each, read the filters offered and note whether any are specific to that type.",
|
|
166
177
|
"Use each type-specific filter and confirm it changes the rows.",
|
|
167
178
|
"Write down any page offering no filters at all.",
|
|
168
179
|
"Write down any filter that changes nothing.",
|
|
180
|
+
"Now confirm auctions and pre-orders have NO page of their own: /admin/auctions and /admin/pre-orders should not resolve, and both types must instead be reachable as chips on /admin/products.",
|
|
169
181
|
],
|
|
170
|
-
expectedBehaviour: "Each type's page offers the facets that type actually has — a classified's city, a live item's species
|
|
171
|
-
expectedUiState: "
|
|
182
|
+
expectedBehaviour: "Each type's page offers the facets that type actually has — a classified's city, a live item's species — on top of the shared ones. A facet that renders and matches nothing is inert, which happens when it emits a field the query allowlist does not carry, and nothing raises. Auctions and pre-orders deliberately have no dedicated admin page; the chips on /admin/products are their only admin surface, and that is why those chips must cover all nine types.",
|
|
183
|
+
expectedUiState: "All six per-type pages resolve and offer filters including their own, each visibly changing the rows. /admin/auctions and /admin/pre-orders do not resolve, and both types filter correctly from the chips instead — a 404 there is the expected result, not a finding. A page with no filters, or a filter leaving the count identical while incrementing the badge, are findings named by page.",
|
|
172
184
|
endResult: "Read-only; nothing persists beyond the URL.",
|
|
173
185
|
},
|
|
174
186
|
"checklist-admin-catalog-listings-admin-listing-reset-restores-defaults": {
|
|
@@ -196,7 +208,7 @@ export const authored = {
|
|
|
196
208
|
"Read the URL and check it encodes the selection.",
|
|
197
209
|
"Copy the URL, open it in a new tab, and read which chip is active on arrival.",
|
|
198
210
|
"Press the browser back button and read which chip is active.",
|
|
199
|
-
"Repeat all of this on
|
|
211
|
+
"Repeat all of this on /admin/payment-methods, which has the same chip row.",
|
|
200
212
|
],
|
|
201
213
|
expectedBehaviour: "Chip state lives in the URL on both listings, so a filtered view is shareable and the back button walks the selections. A chip held in component state alone makes the URL describe a different page from the one on screen.",
|
|
202
214
|
expectedUiState: "Both listings encode the chip in the URL, the copied URL arrives with the same chip active, and back returns to the previous selection. One press per selection — two presses to undo one chip is the double-navigation failure.",
|
|
@@ -210,7 +222,7 @@ export const authored = {
|
|
|
210
222
|
"Open /admin/products with no query parameters and read the sort dropdown's selection.",
|
|
211
223
|
"Check something is selected rather than the control being blank.",
|
|
212
224
|
"Confirm the selected option is one of the options the dropdown offers.",
|
|
213
|
-
"Repeat on /admin/orders, /admin/addresses and
|
|
225
|
+
"Repeat on /admin/orders, /admin/addresses and /admin/payment-methods.",
|
|
214
226
|
"Write down any listing whose dropdown opens with nothing selected.",
|
|
215
227
|
],
|
|
216
228
|
expectedBehaviour: "A listing's default sort is one of the options it offers. Where the default names a field the dropdown does not list, the control opens blank — the list IS sorted, but nothing on screen says how, and two of these listings shipped exactly that way.",
|
|
@@ -219,10 +231,10 @@ export const authored = {
|
|
|
219
231
|
},
|
|
220
232
|
"checklist-admin-catalog-listings-sublisting-categories-crud": {
|
|
221
233
|
roles: ["admin", "guest"],
|
|
222
|
-
startPage: "/admin/categories",
|
|
234
|
+
startPage: "/admin/sublisting-categories",
|
|
223
235
|
steps: [
|
|
224
236
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
225
|
-
"Open
|
|
237
|
+
"Open /admin/sublisting-categories and read the existing rows.",
|
|
226
238
|
"Create one named 'QA Sublisting admin-crud' with a description and save.",
|
|
227
239
|
"RELOAD and read every field.",
|
|
228
240
|
"Rename it, save, RELOAD, and read the derived page title and description.",
|
|
@@ -244,20 +256,20 @@ export const authored = {
|
|
|
244
256
|
"Create a carousel named 'QA Carousel admin-crud' and add a slide with a title, an image and a link.",
|
|
245
257
|
"Save, RELOAD, and read the carousel's name, status and its slide.",
|
|
246
258
|
"Set it active and open / in a private window to find it.",
|
|
247
|
-
"
|
|
259
|
+
"Add slides to it until a sixth ACTIVE slide is refused. The cap of five is on active SLIDES, not on carousels, and the seed already ships five active ones — so the refusal may arrive on the very first slide you activate.",
|
|
248
260
|
"Delete the carousel.",
|
|
249
261
|
],
|
|
250
262
|
inputs: { name: "QA Carousel admin-crud", maxActive: 5 },
|
|
251
|
-
expectedBehaviour: "The list page lists named carousels — it once rendered the flat slide editor instead, so there was no list at all. At most five may be active at once, and
|
|
252
|
-
expectedUiState: "The page lists named carousels. After the reload the carousel holds its name, status and slide. Activating it shows it publicly.
|
|
263
|
+
expectedBehaviour: "The list page lists named carousels — it once rendered the flat slide editor instead, so there was no list at all. At most five SLIDES may be active at once — refused at write with a 409 naming the limit, not silently truncated, and there is deliberately no cap on the number of named carousels. A slide's background may be an image, a video, a colour or a gradient; a video background renders through a real video element, so its URL must be directly playable rather than routed through the image proxy, which rejects video outright.",
|
|
264
|
+
expectedUiState: "The page lists named carousels. After the reload the carousel holds its name, status and slide. Activating it shows it publicly. A sixth active SLIDE is refused with a readable message rather than silently accepted or silently dropped from the public carousel. Refusing to create a sixth CAROUSEL would be the opposite failure — there is no such cap.",
|
|
253
265
|
endResult: "The carousel is deleted by the final step.",
|
|
254
266
|
},
|
|
255
267
|
"checklist-admin-catalog-listings-sections-crud": {
|
|
256
268
|
roles: ["admin", "guest"],
|
|
257
|
-
startPage: "/admin",
|
|
269
|
+
startPage: "/admin/sections",
|
|
258
270
|
steps: [
|
|
259
271
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
260
|
-
"Open
|
|
272
|
+
"Open /admin/sections and read every section, its type and its order. It is the homepage sections editor; /admin is only a redirect to the dashboard.",
|
|
261
273
|
"Create a section, choosing a type, and configure every field it offers.",
|
|
262
274
|
"Save, RELOAD, and read every field back.",
|
|
263
275
|
"Open / in a private window and find the new section in the right position.",
|
|
@@ -276,9 +288,9 @@ export const authored = {
|
|
|
276
288
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
277
289
|
"Create an art listing titled 'QA Art Print admin-crud' at 700 with a size, material, finish and edition size.",
|
|
278
290
|
"Save, RELOAD, and read all four print fields.",
|
|
279
|
-
"Open
|
|
291
|
+
"Open /admin/art and check the listing appears there.",
|
|
280
292
|
"Read every other row in that filtered view and check none is an ordinary product.",
|
|
281
|
-
"Open
|
|
293
|
+
"Open /art publicly and check the listing appears there too. Art and stickers share that one browse route — there is no /stickers page.",
|
|
282
294
|
"Delete the listing.",
|
|
283
295
|
],
|
|
284
296
|
inputs: { title: "QA Art Print admin-crud", price: 700 },
|
|
@@ -296,8 +308,8 @@ export const authored = {
|
|
|
296
308
|
"Set its featured flag on and save.",
|
|
297
309
|
"RELOAD and confirm the flag is still on.",
|
|
298
310
|
"Open / in a private window and find the product in a featured section.",
|
|
299
|
-
"Set the promoted
|
|
300
|
-
"Turn both flags back off, save, and confirm the product leaves
|
|
311
|
+
"Set the promoted flag on, save, reload, and open /admin/featured and /admin/deals — both are real listings of flagged products and the product must appear in the matching one.",
|
|
312
|
+
"Turn both flags back off, save, and confirm the product leaves the homepage section AND leaves /admin/featured and /admin/deals.",
|
|
301
313
|
],
|
|
302
314
|
expectedBehaviour: "Featured, promoted and on-sale flags reach the public surfaces they drive, in both directions. Turning a flag off must remove the product from its section — a flag that only ever adds is half a flag, and the homepage sections that read them filter by availability too, so a sold-out featured product should not appear.",
|
|
303
315
|
expectedUiState: "Each flag survives its reload and its public surface follows it on and off. The product appears in the featured section when flagged and leaves when unflagged.",
|
|
@@ -5,12 +5,20 @@
|
|
|
5
5
|
* The WRITE side of the tree. Its read side is content-discovery/category-brand-relations;
|
|
6
6
|
* these are the operations that can corrupt what that page then renders.
|
|
7
7
|
*
|
|
8
|
-
* 🛑
|
|
9
|
-
* write path derives a product's ancestor chain
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* 🛑 THE ANCESTOR-CHAIN CASE WAS INVERTED AND IS NOW CORRECTED. This header used to
|
|
9
|
+
* say "nothing on the write path derives a product's ancestor chain", and the case
|
|
10
|
+
* encoded that as `categoryPagesListingIt: 1` — so once the gap was actually closed,
|
|
11
|
+
* the case DEMANDED the bug: correct behaviour scored a fail and a tester following
|
|
12
|
+
* it would have filed the fix as a regression.
|
|
13
|
+
*
|
|
14
|
+
* `ProductRepository.deriveTaxonomy()` has derived the chain from both `create` and
|
|
15
|
+
* `update` since 2026-09-14. It reads the leaf's `parentIds`, reverses them onto the
|
|
16
|
+
* front, and writes `categorySlugs = [leaf, ...ancestors]` — byte-identical to what
|
|
17
|
+
* the seed hand-writes. The expected answer is now ALL THREE pages.
|
|
18
|
+
*
|
|
19
|
+
* It stays a valuable case for a reason worth keeping: every SEEDED product already
|
|
20
|
+
* carries a correct chain, so a regression here is invisible to any check that reads
|
|
21
|
+
* existing data. Only a product created through a form can catch it.
|
|
14
22
|
*
|
|
15
23
|
* @tag domain:tester
|
|
16
24
|
* @tag layer:seed
|
|
@@ -79,16 +87,14 @@ export const authored = {
|
|
|
79
87
|
"Open /categories/category-burst-parts publicly and check the product is listed.",
|
|
80
88
|
"Open /categories/category-beyblade-burst and search for it.",
|
|
81
89
|
"Open /categories/category-spinning-tops and search for it.",
|
|
82
|
-
"Record on which of the three pages it appears.",
|
|
90
|
+
"Record on which of the three pages it appears — Burst Parts sits two levels below the root, so all three are expected.",
|
|
83
91
|
"Delete the product afterwards.",
|
|
84
92
|
],
|
|
85
93
|
inputs: { title: "QA Ancestor Chain Probe", price: 499, categoryId: "category-burst-parts" },
|
|
86
|
-
expectedBehaviour: "A category page matches on its own id alone, which only
|
|
87
|
-
expectedUiState: "
|
|
88
|
-
expectedData: { categoryPagesListingIt:
|
|
94
|
+
expectedBehaviour: "A category page matches on its own id alone, which works only if the product carries its full ancestor chain — and the repository derives that chain on write, from create and update alike. A form cannot supply it: the create schema strips any categorySlugs it is sent, so if the derivation stops running there is no fallback and the product silently reaches its leaf page only.",
|
|
95
|
+
expectedUiState: "All THREE pages list it: Burst Parts, Beyblade Burst and Spinning Tops. Visible only on Burst Parts is the failure — and it is a silent one, because the product's own page looks entirely normal and every seeded product around it still shows a correct chain.",
|
|
96
|
+
expectedData: { categoryPagesListingIt: 3 },
|
|
89
97
|
endResult: "The QA product is deleted.",
|
|
90
|
-
needsReview: true,
|
|
91
|
-
reviewNote: "Open gap by design — the write side does not append ancestors. The case exists to measure it, and its answer should change once that lands.",
|
|
92
98
|
},
|
|
93
99
|
"checklist-admin-category-brand-authoring-brand-rename-orphan-check": {
|
|
94
100
|
roles: ["admin"],
|
|
@@ -130,29 +136,42 @@ export const authored = {
|
|
|
130
136
|
steps: [
|
|
131
137
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
132
138
|
"Open /admin/categories and start creating a category.",
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
139
|
+
"Read every field the create form offers and confirm there is NO dropdown for the row's kind or type — the fields are name, slug, description, parent, order, active and show-in-menu.",
|
|
140
|
+
"Create one named 'QA Kind Probe' with no parent and save it.",
|
|
141
|
+
"Open /admin/brands and confirm 'QA Kind Probe' is NOT listed there.",
|
|
142
|
+
"Create a brand named 'QA Brand Probe' on /admin/brands and save it.",
|
|
143
|
+
"Return to /admin/categories and confirm 'QA Brand Probe' is NOT listed among the categories.",
|
|
144
|
+
"Open /brands publicly and confirm QA Brand Probe appears; open /categories and confirm QA Kind Probe appears and QA Brand Probe does not.",
|
|
145
|
+
"Delete both QA rows.",
|
|
137
146
|
],
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
inputs: { categoryName: "QA Kind Probe", brandName: "QA Brand Probe" },
|
|
148
|
+
expectedBehaviour: "Brands, bundles, sub-listing groups and ordinary categories are all rows in ONE collection distinguished by a kind field — but the author never picks it. The surface determines it: /admin/brands lists and writes kind=brand, /admin/categories writes an ordinary category. That is deliberate, so a missing kind dropdown is correct rather than a gap. What must hold is that the two surfaces stay disjoint.",
|
|
149
|
+
expectedUiState: "No kind dropdown on either form. Each row appears on exactly ONE admin surface and exactly one public surface. A brand leaking into the category tree — or a category into the brand list — means the kind was not set from the surface, and that row will render in a place built for a different shape.",
|
|
150
|
+
endResult: "Both QA rows are deleted. If a row appeared on both surfaces, record which, and delete it — a mistyped row is worse left in place than removed.",
|
|
141
151
|
},
|
|
142
152
|
"checklist-admin-category-brand-authoring-delete-category-with-children-refused": {
|
|
143
153
|
roles: ["admin"],
|
|
144
154
|
startPage: "/admin/categories",
|
|
145
155
|
steps: [
|
|
146
156
|
"Sign in as admin@letitrip.in / TempPass123!.",
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
157
|
+
"Create a category 'QA Doomed Parent' under Beyblade Burst, then a category 'QA Orphan Child' under QA Doomed Parent.",
|
|
158
|
+
"Create a product 'QA Cascade Probe' priced at 499 filed directly under QA Doomed Parent, and publish it.",
|
|
159
|
+
"Open /admin/categories and delete QA Doomed Parent.",
|
|
160
|
+
"Open /categories/category-beyblade-burst publicly and look for QA Orphan Child — it should now sit directly under Beyblade Burst.",
|
|
161
|
+
"Open the QA Cascade Probe product's public page and read its category links.",
|
|
162
|
+
"Open /categories/category-beyblade-burst and confirm the product is listed there.",
|
|
163
|
+
"Delete QA Orphan Child and the product afterwards.",
|
|
152
164
|
],
|
|
153
|
-
inputs: {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
165
|
+
inputs: {
|
|
166
|
+
doomedParent: "QA Doomed Parent",
|
|
167
|
+
orphanChild: "QA Orphan Child",
|
|
168
|
+
productTitle: "QA Cascade Probe",
|
|
169
|
+
price: 499,
|
|
170
|
+
grandparentId: "category-beyblade-burst",
|
|
171
|
+
},
|
|
172
|
+
expectedBehaviour: "Deleting a category CASCADES TO ITS PARENT rather than refusing: the children re-parent to the grandparent and the products are re-filed there, through the repository so each product's ancestor chain is rebuilt from its new leaf. This replaced an older flat refusal. The order is load-bearing — products must move while the document still exists — so the failure mode to watch for is a product left pointing at a category that is gone.",
|
|
173
|
+
expectedUiState: "QA Orphan Child now sits directly under Beyblade Burst. QA Cascade Probe is filed under Beyblade Burst and its category links show the rebuilt chain, with no link naming the deleted QA Doomed Parent. A product with a dead category link, a one-element chain, or one missing from Beyblade Burst entirely is the failure.",
|
|
174
|
+
expectedData: { childReparentedToGrandparent: true, deadCategoryLinks: 0 },
|
|
175
|
+
endResult: "Only the two QA rows and the QA product were touched; the seeded tree is unchanged. Do NOT test this by deleting a seeded category — the whole Burst subtree would re-parent and every other case reading it in this run would then be testing a different tree.",
|
|
157
176
|
},
|
|
158
177
|
};
|
|
@@ -11,6 +11,45 @@
|
|
|
11
11
|
* as a rejection that leaves the item live — both look correct from the admin's
|
|
12
12
|
* seat, and only the public view can tell them apart.
|
|
13
13
|
*
|
|
14
|
+
* 🛑 STATUS IS CHANGED FROM THE LIST, NOT THE EDITOR (corrected 2026-09-29).
|
|
15
|
+
*
|
|
16
|
+
* All three cases said "open its detail" and then "change its status and save".
|
|
17
|
+
* `AdminProductEditorView` has exactly three sections — Listing type, Details,
|
|
18
|
+
* Inventory — and `status` appears in it only as a default and as read-only
|
|
19
|
+
* header text. There is no status control to use. The real ones are on
|
|
20
|
+
* /admin/products' row menu: 'Approve' (writes published), 'Reject', and a
|
|
21
|
+
* 'Quick edit' drawer whose Status select is the one place an admin picks a
|
|
22
|
+
* value. So the procedure now names the row menu.
|
|
23
|
+
*
|
|
24
|
+
* 🛑 AND THE TYPE TABS IN THAT EDITOR RENDER NOTHING. Every `<TabsContent>` is
|
|
25
|
+
* self-closing: the strip flips the `listingType` discriminator and shows no
|
|
26
|
+
* type-specific fields at all. So a moderator opening a live listing sees no
|
|
27
|
+
* species, no jurisdiction list and no video — which is precisely the
|
|
28
|
+
* "deciding blind" failure the live case was written to catch, and it is real
|
|
29
|
+
* today. The case now says where to look and what the finding is, rather than
|
|
30
|
+
* sending a tester to hunt a panel that does not exist. Recorded in OUTOFSCOPE.
|
|
31
|
+
*
|
|
32
|
+
* 🛑 THE CODE POOL IS EDIT-MODE ONLY. `renderDigitalContentPool` is gated on
|
|
33
|
+
* `mode === "edit" && listingType === "digital-code" && productId`, so a
|
|
34
|
+
* digital-code listing cannot be created WITH codes in one pass — it is created,
|
|
35
|
+
* saved, and reopened. "Code Pool Size (optional)" on the create form is a
|
|
36
|
+
* seller-typed number that the server recounts from the real subcollection; it
|
|
37
|
+
* is not the pool.
|
|
38
|
+
*
|
|
39
|
+
* 🛑 /admin/classified, /admin/digital-codes AND /admin/live ARE BROWSE-ONLY.
|
|
40
|
+
* All three are thin wrappers over `buildListingTypeListingConfig`, which
|
|
41
|
+
* supplies a search box, a sort and a `status` filter — and no row actions, no
|
|
42
|
+
* bulk actions, no Approve/Reject and no Quick edit. The catalogue `href`s
|
|
43
|
+
* pointed at them, which is a real page where the case's central action cannot
|
|
44
|
+
* be performed; they now point at /admin/products, and `startPage` says the
|
|
45
|
+
* same. Both Status and Type on that page are chip groups inside the FILTER
|
|
46
|
+
* DRAWER, not an always-visible row, and Type is multi-select.
|
|
47
|
+
*
|
|
48
|
+
* Field labels used below are the real ones: 'City' / 'Preferred Contact Method'
|
|
49
|
+
* (section "Meetup Details"), 'Code Pool Size (optional)' /
|
|
50
|
+
* 'Redemption Instructions (optional)', 'Species / Common Name' /
|
|
51
|
+
* 'Jurisdictions where sale is permitted'.
|
|
52
|
+
*
|
|
14
53
|
* @tag domain:tester
|
|
15
54
|
* @tag layer:seed
|
|
16
55
|
* @tag pattern:none
|
|
@@ -23,56 +62,104 @@ export const authored = {
|
|
|
23
62
|
roles: ["admin", "seller", "guest"],
|
|
24
63
|
startPage: "/admin/products",
|
|
25
64
|
steps: [
|
|
26
|
-
"Sign in as tyson@beybladearena.in / TempPass123
|
|
27
|
-
"Sign
|
|
28
|
-
"Open the
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"Open the listing's public URL
|
|
32
|
-
"
|
|
65
|
+
"Sign in as tyson@beybladearena.in / TempPass123!, open /store/classified/new, and create a listing titled 'QA Classified admin-moderate' at 900 with City 'Pune' under Meetup Details. Publish it and note the slug on its public URL.",
|
|
66
|
+
"Sign in as admin@letitrip.in / TempPass123! and open /admin/products.",
|
|
67
|
+
"Open the filter drawer, tick Classified in its multi-select Type chip group, apply, and find the listing.",
|
|
68
|
+
"In that same filter drawer read the Status chip group — All, Pending, Published, Draft, Archived — and select each in turn, noting which one returns this listing.",
|
|
69
|
+
"Open the row menu, choose 'Quick edit', set Status to Archived, and save.",
|
|
70
|
+
"Open the listing's public URL and read what is shown.",
|
|
71
|
+
"Quick-edit it back to Published, then sign in as tyson@beybladearena.in and delete it from /store/classified.",
|
|
33
72
|
],
|
|
34
|
-
inputs: { title: "QA Classified admin-moderate", price: 900, city: "Pune" },
|
|
73
|
+
inputs: { title: "QA Classified admin-moderate", price: 900, city: "Pune", archivedVia: "Quick edit" },
|
|
35
74
|
expectedBehaviour: "An admin status change reaches the public read path. Every status chip must also name a value the documents actually hold — a chip whose id is a display label rather than the stored value matches nothing forever and returns an empty list with no error.",
|
|
36
|
-
expectedUiState: "
|
|
37
|
-
|
|
75
|
+
expectedUiState: "While published the listing is returned by the Published chip and by no other. After the archive the public URL no longer renders it as a live listing, and the Archived chip returns it — the admin row will look correct either way, so the public page is the oracle. A chip returning nothing while All holds rows of that status is a separate finding worth naming.",
|
|
76
|
+
expectedData: { statusAfterArchive: "archived", chipReturningIt: "Archived" },
|
|
77
|
+
endResult: "The listing is Published again and then deleted. Re-open the public URL after the delete: a page still rendering it is a cache or projection failure.",
|
|
38
78
|
},
|
|
39
79
|
"checklist-admin-classifieds-digitalcodes-live-digitalcode-create-moderate": {
|
|
40
80
|
roles: ["admin", "seller", "guest"],
|
|
41
81
|
startPage: "/admin/products",
|
|
42
82
|
steps: [
|
|
43
|
-
"Sign in as tyson@beybladearena.in / TempPass123
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"Read
|
|
47
|
-
"
|
|
48
|
-
"Open the public URL
|
|
49
|
-
"
|
|
83
|
+
"Sign in as tyson@beybladearena.in / TempPass123!, open /store/digital-codes/new, and create a listing titled 'QA Digital Code admin-moderate' at 350. Publish it.",
|
|
84
|
+
"REOPEN it from /store/digital-codes for editing — the code pool is only mounted in edit mode — and add three codes QA-CODE-1, QA-CODE-2, QA-CODE-3. Note the slug on its public URL.",
|
|
85
|
+
"Sign in as admin@letitrip.in / TempPass123!, open /admin/products, open the filter drawer, tick Digital Code in its Type chip group, apply, and find the listing.",
|
|
86
|
+
"Read the row and every column: are the actual CODE STRINGS visible anywhere in the list?",
|
|
87
|
+
"Open the row menu, choose 'Quick edit', set Status to Archived, and save.",
|
|
88
|
+
"Open the listing's public URL and read what is shown.",
|
|
89
|
+
"Quick-edit it back to Published, then sign in as tyson@beybladearena.in and delete it from /store/digital-codes.",
|
|
50
90
|
],
|
|
51
|
-
inputs: {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
inputs: {
|
|
92
|
+
title: "QA Digital Code admin-moderate",
|
|
93
|
+
price: 350,
|
|
94
|
+
codeCount: 3,
|
|
95
|
+
codes: "QA-CODE-1, QA-CODE-2, QA-CODE-3",
|
|
96
|
+
},
|
|
97
|
+
expectedBehaviour: "Moderation works the same for this type, and the code pool is the thing worth watching: an unsold code is inventory a buyer will pay for, so it must not be readable from a list view. Availability for this type reads the nested pool count rather than a stock figure, which is why the pool has to exist before the listing can be judged available at all.",
|
|
98
|
+
expectedUiState: "The listing is findable under the Digital Code chip and its archive takes effect publicly. NO unredeemed code string appears in the admin list — not in a column, not in a tooltip, not in the quick-edit drawer. If any is visible, quote it and say exactly where; that is a finding, not a pass.",
|
|
99
|
+
expectedData: { codesVisibleInAdminList: false, statusAfterArchive: "archived" },
|
|
100
|
+
endResult: "The listing is Published again and then deleted. Record whether an admin can read an unredeemed code anywhere in the product — a 'no' here is a leak of paid inventory.",
|
|
55
101
|
},
|
|
56
102
|
"checklist-admin-classifieds-digitalcodes-live-live-create-moderate": {
|
|
57
103
|
roles: ["admin", "seller", "guest"],
|
|
58
104
|
startPage: "/admin/products",
|
|
59
105
|
steps: [
|
|
60
|
-
"Sign in as tyson@beybladearena.in / TempPass123
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"Read whether the species, jurisdiction list and video
|
|
64
|
-
"
|
|
65
|
-
"Open the public URL
|
|
66
|
-
"
|
|
106
|
+
"Sign in as tyson@beybladearena.in / TempPass123!, open /store/live/new, and create a listing titled 'QA Live Item admin-moderate' at 3000 with 'Species / Common Name' set to Dog and 'Jurisdictions where sale is permitted' set to Maharashtra only.",
|
|
107
|
+
"Attach /test-media/sample-video.mp4 as its video and publish it. Note the slug on its public URL.",
|
|
108
|
+
"Sign in as admin@letitrip.in / TempPass123!, open /admin/products, open the filter drawer, tick Live Item in its Type chip group, apply, and find the listing.",
|
|
109
|
+
"Open the row menu and then the editor at /admin/products/{its id}/edit. Read every section and record whether the species, the jurisdiction list and the video appear ANYWHERE.",
|
|
110
|
+
"Return to /admin/products, open the row menu, choose 'Quick edit', set Status to Archived, and save.",
|
|
111
|
+
"Open the listing's public URL and read what is shown.",
|
|
112
|
+
"Quick-edit it back to Published, then sign in as tyson@beybladearena.in and delete it from /store/live.",
|
|
67
113
|
],
|
|
68
114
|
inputs: {
|
|
69
115
|
title: "QA Live Item admin-moderate",
|
|
70
116
|
price: 3000,
|
|
71
117
|
species: "Dog",
|
|
72
118
|
jurisdictions: "Maharashtra",
|
|
119
|
+
video: "/test-media/sample-video.mp4",
|
|
73
120
|
},
|
|
74
|
-
expectedBehaviour: "A live listing is the one type where moderation has a duty beyond commerce — the jurisdiction list decides who may lawfully receive the animal or plant, and the
|
|
75
|
-
expectedUiState: "The admin
|
|
76
|
-
|
|
121
|
+
expectedBehaviour: "A live listing is the one type where moderation has a duty beyond commerce — the jurisdiction list decides who may lawfully receive the animal or plant, and the video is what the moderator judges the listing on. Both must be visible to the admin, or the moderator is approving and rejecting blind.",
|
|
122
|
+
expectedUiState: "🛑 EXPECT THIS TO FAIL ON THE ADMIN HALF, AND RECORD IT AS A FAILURE. The admin editor has three sections — Listing type, Details, Inventory — and its listing-type tabs render EMPTY content, so no species, no jurisdiction list and no video is shown. Confirm that by reading the sections and say so, naming the three sections you found. The archive half does hold: it takes effect on the public page. Keep the two apart in the comment — the moderation mechanics work and the information the moderator needs is absent.",
|
|
123
|
+
expectedData: { adminSectionsSeen: 3, speciesShownToAdmin: false, videoShownToAdmin: false },
|
|
124
|
+
endResult: "The listing is Published again and then deleted. Record specifically whether the video was viewable from any admin surface — that is the finding, not the status change.",
|
|
125
|
+
},
|
|
126
|
+
/*
|
|
127
|
+
* 🛑 ADDED 2026-09-29. The three cases above all archive; none of them used
|
|
128
|
+
* the 'Reject' action, and it is the one that writes a value the rest of the
|
|
129
|
+
* product cannot represent.
|
|
130
|
+
*
|
|
131
|
+
* `ProductStatus` is draft | published | in_review | archived — four values,
|
|
132
|
+
* no 'rejected'. `ADMIN_PRODUCT_STATUS_TABS` mirrors those four plus All.
|
|
133
|
+
* But AdminProductsView's Reject row action writes `{ status: "rejected" }`,
|
|
134
|
+
* and the PATCH schema types status as a bare `z.string().optional()`, so it
|
|
135
|
+
* is persisted verbatim with no validation.
|
|
136
|
+
*
|
|
137
|
+
* The effect looks right — every public query filters `status == published`,
|
|
138
|
+
* so the listing does disappear — which is exactly why nobody noticed. The
|
|
139
|
+
* cost is on the way back: the row now matches no status chip, so an admin
|
|
140
|
+
* can only reach it under All, and the seller's own status filters cannot
|
|
141
|
+
* isolate it either. Root Cause #33's shape on the WRITE side.
|
|
142
|
+
*
|
|
143
|
+
* Written as a case rather than fixed: choosing between adding 'rejected' to
|
|
144
|
+
* the union and making Reject write 'archived' is a product decision.
|
|
145
|
+
*/
|
|
146
|
+
"checklist-admin-classifieds-digitalcodes-live-reject-status-has-no-chip": {
|
|
147
|
+
roles: ["admin", "seller"],
|
|
148
|
+
startPage: "/admin/products",
|
|
149
|
+
steps: [
|
|
150
|
+
"Sign in as tyson@beybladearena.in / TempPass123!, open /store/classified/new, and create a listing titled 'QA Classified reject-status' at 700 with City 'Pune'. Publish it.",
|
|
151
|
+
"Sign in as admin@letitrip.in / TempPass123! and open /admin/products.",
|
|
152
|
+
"Open the filter drawer, tick Classified in its Type chip group, apply, then find the listing, open its row menu and click 'Reject'.",
|
|
153
|
+
"Read the status shown on the row.",
|
|
154
|
+
"In the filter drawer select each Status chip in turn — Pending, Published, Draft, Archived — and record whether ANY of them returns the listing.",
|
|
155
|
+
"Select the All status chip and confirm it is still there.",
|
|
156
|
+
"Open the listing's public URL and read what is shown.",
|
|
157
|
+
"Quick-edit it back to Published, then sign in as tyson@beybladearena.in and delete it from /store/classified.",
|
|
158
|
+
],
|
|
159
|
+
inputs: { title: "QA Classified reject-status", price: 700, city: "Pune", action: "Reject" },
|
|
160
|
+
expectedBehaviour: "A rejected listing must stay reachable by the admin who rejected it. Hiding it from buyers is only half the job — a moderator needs to find it again to explain, reverse or escalate the decision, and the status filters are the only tool for that.",
|
|
161
|
+
expectedUiState: "🛑 EXPECT THE FILTER HALF TO FAIL. The row reads 'rejected', a value ProductStatus does not have, so none of the four status chips returns it and it is reachable only under All. Record which chips you tried and that each came back without it. The public half should hold — the listing is gone from its public URL, because every public query filters on published.",
|
|
162
|
+
expectedData: { statusOnRow: "rejected", chipsReturningIt: 0 },
|
|
163
|
+
endResult: "The listing is Published again and then deleted. Two findings to keep separate: a status no chip matches, and — if the public page still renders it — a far worse one.",
|
|
77
164
|
},
|
|
78
165
|
};
|