@mohasinac/appkit 4.40.1 → 4.41.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.
Files changed (223) hide show
  1. package/dist/features/categories/repository/categories.repository.js +2 -2
  2. package/dist/features/shell/SectionForm.js +44 -5
  3. package/dist/features/tester/seed-data/_money-flows.js +65 -49
  4. package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +6 -4
  5. package/dist/features/tester/seed-data/authored/admin__coupons.js +14 -13
  6. package/dist/features/tester/seed-data/authored/buying__bidding.js +154 -140
  7. package/dist/features/tester/seed-data/authored/buying__browsing-search.js +4 -4
  8. package/dist/features/tester/seed-data/authored/buying__buying-checkout.js +59 -56
  9. package/dist/features/tester/seed-data/authored/buying__buying-coupons.js +18 -18
  10. package/dist/features/tester/seed-data/authored/buying__cart.js +105 -79
  11. package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +6 -8
  12. package/dist/features/tester/seed-data/authored/buying__my-orders.js +13 -13
  13. package/dist/features/tester/seed-data/authored/buying__offers.js +6 -6
  14. package/dist/features/tester/seed-data/authored/buying__order-detail-actions.js +8 -7
  15. package/dist/features/tester/seed-data/authored/buying__product-detail.js +62 -56
  16. package/dist/features/tester/seed-data/authored/buying__return-request.js +6 -6
  17. package/dist/features/tester/seed-data/authored/buying__reviews-pagination.js +4 -4
  18. package/dist/features/tester/seed-data/authored/buying__reviews.js +1 -1
  19. package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.js +8 -8
  20. package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +1 -1
  21. package/dist/features/tester/seed-data/authored/content-discovery__coupons.js +29 -16
  22. package/dist/features/tester/seed-data/authored/content-discovery__event-detail-subroutes.js +9 -9
  23. package/dist/features/tester/seed-data/authored/content-discovery__events.js +69 -36
  24. package/dist/features/tester/seed-data/authored/content-discovery__notifications.js +1 -1
  25. package/dist/features/tester/seed-data/authored/cta-layout__checkout-bottom-bar.js +2 -2
  26. package/dist/features/tester/seed-data/authored/design-ux__back-to-top-button.js +2 -2
  27. package/dist/features/tester/seed-data/authored/design-ux__general-design.js +1 -1
  28. package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +1 -1
  29. package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +3 -3
  30. package/dist/features/tester/seed-data/authored/index.js +0 -2
  31. package/dist/features/tester/seed-data/authored/page-wiring__data-loss.js +2 -2
  32. package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +6 -4
  33. package/dist/features/tester/seed-data/authored/public-pages__auth-error-pages.js +2 -2
  34. package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +1 -1
  35. package/dist/features/tester/seed-data/authored/public-pages__stores-sellers-directories.js +8 -10
  36. package/dist/features/tester/seed-data/authored/selling__become-seller.js +2 -2
  37. package/dist/features/tester/seed-data/authored/selling__digital-content-delivery.js +2 -2
  38. package/dist/features/tester/seed-data/authored/selling__listing-a-product.js +2 -2
  39. package/dist/features/tester/seed-data/authored/selling__listing-edit-roundtrip.js +2 -2
  40. package/dist/features/tester/seed-data/authored/selling__listing-lifecycle.js +2 -2
  41. package/dist/features/tester/seed-data/authored/selling__seller-listing-types.js +2 -2
  42. package/dist/features/tester/seed-data/authored/seo__og-images.js +3 -4
  43. package/dist/features/tester/seed-data/authored/seo__page-metadata.js +6 -8
  44. package/dist/features/tester/seed-data/authored/seo__sitemap-and-robots.js +5 -5
  45. package/dist/features/tester/seed-data/index.d.ts +0 -10
  46. package/dist/features/tester/seed-data/index.js +0 -10
  47. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +75 -89
  48. package/dist/features/tester/seed-data/tester-responses-seed-data.js +5 -5
  49. package/dist/index.d.ts +0 -8
  50. package/dist/index.js +0 -21
  51. package/dist/seed/addresses-seed-data.js +0 -34
  52. package/dist/seed/index.d.ts +1 -1
  53. package/dist/seed/index.js +4 -3
  54. package/dist/seed/manifest.js +11 -12
  55. package/dist/seed/notifications-seed-data.js +4 -4
  56. package/dist/seed/stores-seed-data.js +7 -52
  57. package/dist/seed/users-seed-data.js +3 -83
  58. package/package.json +2 -2
  59. package/scripts/seed-cli.mjs +15 -20
  60. package/dist/_internal/server/features/classified/actions.d.ts +0 -12
  61. package/dist/_internal/server/features/classified/actions.js +0 -33
  62. package/dist/_internal/server/features/digital-code/actions.d.ts +0 -12
  63. package/dist/_internal/server/features/digital-code/actions.js +0 -33
  64. package/dist/_internal/server/features/homepage/data.d.ts +0 -18
  65. package/dist/_internal/server/features/homepage/data.js +0 -70
  66. package/dist/_internal/server/features/live/actions.d.ts +0 -6
  67. package/dist/_internal/server/features/live/actions.js +0 -27
  68. package/dist/_internal/server/features/messages/actions.d.ts +0 -6
  69. package/dist/_internal/server/features/messages/actions.js +0 -6
  70. package/dist/_internal/server/features/messages/data.d.ts +0 -10
  71. package/dist/_internal/server/features/messages/data.js +0 -24
  72. package/dist/_internal/server/features/messages/index.d.ts +0 -2
  73. package/dist/_internal/server/features/messages/index.js +0 -2
  74. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +0 -12
  75. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +0 -112
  76. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +0 -2
  77. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +0 -2
  78. package/dist/_internal/shared/features/homepage/section-gate.d.ts +0 -14
  79. package/dist/_internal/shared/features/homepage/section-gate.js +0 -51
  80. package/dist/_internal/shared/features/lottery/config-form.d.ts +0 -58
  81. package/dist/_internal/shared/features/lottery/config-form.js +0 -71
  82. package/dist/client/api/useFeatureFlags.d.ts +0 -22
  83. package/dist/client/api/useFeatureFlags.js +0 -27
  84. package/dist/features/account/address-validation.d.ts +0 -20
  85. package/dist/features/account/address-validation.js +0 -35
  86. package/dist/features/account/components/ChatList.d.ts +0 -14
  87. package/dist/features/account/components/ChatList.js +0 -9
  88. package/dist/features/account/components/ChatWindow.d.ts +0 -17
  89. package/dist/features/account/components/ChatWindow.js +0 -6
  90. package/dist/features/account/components/MessagesView.d.ts +0 -16
  91. package/dist/features/account/components/MessagesView.js +0 -5
  92. package/dist/features/account/components/UserOffersView.d.ts +0 -10
  93. package/dist/features/account/components/UserOffersView.js +0 -5
  94. package/dist/features/account/constants/addresses.d.ts +0 -13
  95. package/dist/features/account/constants/addresses.js +0 -43
  96. package/dist/features/account/hooks/useAddressForm.d.ts +0 -24
  97. package/dist/features/account/hooks/useAddressForm.js +0 -58
  98. package/dist/features/admin/actions/chat-actions.d.ts +0 -29
  99. package/dist/features/admin/actions/chat-actions.js +0 -101
  100. package/dist/features/admin/components/AdminFeatureFlagsView.d.ts +0 -9
  101. package/dist/features/admin/components/AdminFeatureFlagsView.js +0 -109
  102. package/dist/features/admin/components/AdminSiteView.d.ts +0 -10
  103. package/dist/features/admin/components/AdminSiteView.js +0 -52
  104. package/dist/features/admin/hooks/useChat.d.ts +0 -24
  105. package/dist/features/admin/hooks/useChat.js +0 -100
  106. package/dist/features/admin/permission-map.d.ts +0 -3
  107. package/dist/features/admin/permission-map.js +0 -26
  108. package/dist/features/admin/repository/chat.repository.d.ts +0 -50
  109. package/dist/features/admin/repository/chat.repository.js +0 -223
  110. package/dist/features/auth/actions/password-change-otp-actions.d.ts +0 -18
  111. package/dist/features/auth/actions/password-change-otp-actions.js +0 -88
  112. package/dist/features/auth/actions/realtime-token-actions.d.ts +0 -11
  113. package/dist/features/auth/actions/realtime-token-actions.js +0 -38
  114. package/dist/features/auth/password-change-otp.d.ts +0 -34
  115. package/dist/features/auth/password-change-otp.js +0 -56
  116. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +0 -12
  117. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +0 -49
  118. package/dist/features/events/api/route.d.ts +0 -12
  119. package/dist/features/events/api/route.js +0 -90
  120. package/dist/features/faq/api/route.d.ts +0 -11
  121. package/dist/features/faq/api/route.js +0 -52
  122. package/dist/features/faq/hooks/useFAQs.d.ts +0 -22
  123. package/dist/features/faq/hooks/useFAQs.js +0 -41
  124. package/dist/features/media/upload/video-poster.test.d.ts +0 -1
  125. package/dist/features/media/upload/video-poster.test.js +0 -42
  126. package/dist/features/messages/actions/index.d.ts +0 -2
  127. package/dist/features/messages/actions/index.js +0 -2
  128. package/dist/features/messages/actions/messages-actions.d.ts +0 -15
  129. package/dist/features/messages/actions/messages-actions.js +0 -33
  130. package/dist/features/messages/actions/ping-rtdb.d.ts +0 -2
  131. package/dist/features/messages/actions/ping-rtdb.js +0 -26
  132. package/dist/features/messages/hooks/useConversation.d.ts +0 -22
  133. package/dist/features/messages/hooks/useConversation.js +0 -155
  134. package/dist/features/messages/hooks/useConversations.d.ts +0 -15
  135. package/dist/features/messages/hooks/useConversations.js +0 -104
  136. package/dist/features/messages/index.d.ts +0 -6
  137. package/dist/features/messages/index.js +0 -4
  138. package/dist/features/messages/realtime.d.ts +0 -32
  139. package/dist/features/messages/realtime.js +0 -39
  140. package/dist/features/messages/repository/conversations.repository.d.ts +0 -71
  141. package/dist/features/messages/repository/conversations.repository.js +0 -284
  142. package/dist/features/messages/schemas/firestore.d.ts +0 -34
  143. package/dist/features/messages/schemas/firestore.js +0 -16
  144. package/dist/features/messages/schemas/index.d.ts +0 -177
  145. package/dist/features/messages/schemas/index.js +0 -35
  146. package/dist/features/messages/server.d.ts +0 -6
  147. package/dist/features/messages/server.js +0 -6
  148. package/dist/features/products/components/MakeOfferForm.d.ts +0 -20
  149. package/dist/features/products/components/MakeOfferForm.js +0 -9
  150. package/dist/features/reviews/api/route.d.ts +0 -21
  151. package/dist/features/reviews/api/route.js +0 -174
  152. package/dist/features/seller/api/offers/route.d.ts +0 -17
  153. package/dist/features/seller/api/offers/route.js +0 -63
  154. package/dist/features/seller/components/SellerAuctionsView.d.ts +0 -9
  155. package/dist/features/seller/components/SellerAuctionsView.js +0 -113
  156. package/dist/features/seller/components/SellerOffersPanel.d.ts +0 -21
  157. package/dist/features/seller/components/SellerOffersPanel.js +0 -160
  158. package/dist/features/seller/components/SellerPreOrdersView.d.ts +0 -6
  159. package/dist/features/seller/components/SellerPreOrdersView.js +0 -148
  160. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +0 -6
  161. package/dist/features/seller/components/SellerPrizeDrawsView.js +0 -152
  162. package/dist/features/seller/permission-map.d.ts +0 -5
  163. package/dist/features/seller/permission-map.js +0 -17
  164. package/dist/features/shell/StepForm.d.ts +0 -80
  165. package/dist/features/shell/StepForm.js +0 -136
  166. package/dist/features/stores/api/route.d.ts +0 -12
  167. package/dist/features/stores/api/route.js +0 -110
  168. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +0 -2
  169. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +0 -33
  170. package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +0 -2
  171. package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +0 -138
  172. package/dist/features/tester/seed-data/bids-tester-seed-data.d.ts +0 -2
  173. package/dist/features/tester/seed-data/bids-tester-seed-data.js +0 -37
  174. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +0 -2
  175. package/dist/features/tester/seed-data/blog-tester-seed-data.js +0 -40
  176. package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +0 -3
  177. package/dist/features/tester/seed-data/carts-tester-seed-data.js +0 -70
  178. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +0 -2
  179. package/dist/features/tester/seed-data/categories-tester-seed-data.js +0 -218
  180. package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +0 -2
  181. package/dist/features/tester/seed-data/coupons-tester-seed-data.js +0 -76
  182. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +0 -2
  183. package/dist/features/tester/seed-data/events-tester-seed-data.js +0 -193
  184. package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +0 -2
  185. package/dist/features/tester/seed-data/offers-tester-seed-data.js +0 -191
  186. package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +0 -2
  187. package/dist/features/tester/seed-data/orders-tester-seed-data.js +0 -329
  188. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +0 -2
  189. package/dist/features/tester/seed-data/products-tester-seed-data.js +0 -815
  190. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +0 -2
  191. package/dist/features/tester/seed-data/stores-tester-seed-data.js +0 -79
  192. package/dist/features/wishlist/components/WishlistToggleButton.d.ts +0 -12
  193. package/dist/features/wishlist/components/WishlistToggleButton.js +0 -36
  194. package/dist/monitoring/analytics.d.ts +0 -80
  195. package/dist/monitoring/analytics.js +0 -176
  196. package/dist/next/middleware/pii-redactor.d.ts +0 -32
  197. package/dist/next/middleware/pii-redactor.js +0 -84
  198. package/dist/providers/db-firebase/realtime.d.ts +0 -42
  199. package/dist/providers/db-firebase/realtime.js +0 -138
  200. package/dist/security/rbac/Can.d.ts +0 -31
  201. package/dist/security/rbac/Can.js +0 -22
  202. package/dist/security/rbac/default-roles.d.ts +0 -2
  203. package/dist/security/rbac/default-roles.js +0 -83
  204. package/dist/security/rbac/hook.d.ts +0 -20
  205. package/dist/security/rbac/hook.js +0 -30
  206. package/dist/security/rbac/index.d.ts +0 -8
  207. package/dist/security/rbac/index.js +0 -6
  208. package/dist/security/rbac/middleware.d.ts +0 -51
  209. package/dist/security/rbac/middleware.js +0 -51
  210. package/dist/security/rbac/resolver.d.ts +0 -5
  211. package/dist/security/rbac/resolver.js +0 -38
  212. package/dist/security/rbac/server.d.ts +0 -24
  213. package/dist/security/rbac/server.js +0 -42
  214. package/dist/security/rbac/types.d.ts +0 -22
  215. package/dist/security/rbac/types.js +0 -2
  216. package/dist/seed/conversations-seed-data.d.ts +0 -2
  217. package/dist/seed/conversations-seed-data.js +0 -292
  218. package/dist/utils/filter.helper.d.ts +0 -18
  219. package/dist/utils/filter.helper.js +0 -21
  220. package/dist/utils/pagination.helper.d.ts +0 -23
  221. package/dist/utils/pagination.helper.js +0 -24
  222. package/dist/utils/search-tokens.d.ts +0 -2
  223. package/dist/utils/search-tokens.js +0 -21
@@ -412,8 +412,8 @@ export class CategoriesRepository extends BaseRepository {
412
412
  *
413
413
  * 🛑 MEASURED 2026-09-14: `category` is unset on all 95 production products.
414
414
  * It is written by deriveTaxonomy, but every product here came from the seed,
415
- * which hand-writes `categorySlugs` only — so a `where("category","==",id)`
416
- * query matched nothing and this rewrite silently did nothing at all. The
415
+ * which hand-writes `categorySlugs` only — so an equality query on that
416
+ * scalar matched nothing and this rewrite silently did nothing at all. The
417
417
  * array field is the one that actually exists, and matching the MOVED node
418
418
  * gets the entire subtree in a single read because `categorySlugs` holds the
419
419
  * full ancestor chain.
@@ -158,6 +158,20 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
158
158
  // validator on every render and re-fire the effect below in a loop.
159
159
  const onValidationChangeRef = useRef(onValidationChange);
160
160
  onValidationChangeRef.current = onValidationChange;
161
+ /**
162
+ * Are two field-error maps the same set of messages?
163
+ *
164
+ * Flat string maps, so a key sweep is exact — no deep compare needed. This is
165
+ * what lets the validation effect depend on `values` (a new object every
166
+ * render) without looping: an unchanged result returns the previous object
167
+ * and React bails out of the update.
168
+ */
169
+ const sameErrors = (a, b) => {
170
+ const ak = Object.keys(a);
171
+ if (ak.length !== Object.keys(b).length)
172
+ return false;
173
+ return ak.every((k) => a[k] === b[k]);
174
+ };
161
175
  /** Section ids holding at least one error, in render order. */
162
176
  const erroringSectionIds = useCallback((errs) => {
163
177
  const keys = Object.keys(errs);
@@ -176,8 +190,8 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
176
190
  // applyZodIssues' incremental setFieldError-per-issue cannot do.
177
191
  const parsed = activeSchema.safeParse(nextValues);
178
192
  if (parsed.success) {
179
- setFieldErrors({});
180
- setForceOpenIds([]);
193
+ setFieldErrors((prev) => (Object.keys(prev).length === 0 ? prev : {}));
194
+ setForceOpenIds((prev) => (prev.length === 0 ? prev : []));
181
195
  onValidationChangeRef.current?.({}, fieldToSectionIndex);
182
196
  return;
183
197
  }
@@ -187,7 +201,12 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
187
201
  continue;
188
202
  next[issue.path.map(String).join(".")] = issue.message;
189
203
  }
190
- setFieldErrors(next);
204
+ /*
205
+ * Return the PREVIOUS object when nothing actually changed. `values` is a
206
+ * fresh object on every render, so the effect below re-validates on every
207
+ * render; handing back an equal-but-new errors map would setState in a loop.
208
+ */
209
+ setFieldErrors((prev) => (sameErrors(prev, next) ? prev : next));
191
210
  // Release a force-opened section as soon as ITS OWN errors clear, rather
192
211
  // than waiting for the whole form to become valid — otherwise fixing
193
212
  // section 1 leaves it pinned open while the user works through section 5.
@@ -198,10 +217,30 @@ export function SectionForm({ sections, values, onChange, onSubmit, submitLabel
198
217
  });
199
218
  onValidationChangeRef.current?.(next, fieldToSectionIndex);
200
219
  }, [schema, inheritedSchema, fieldToSectionIndex, erroringSectionIds]);
220
+ /*
221
+ * 🛑 `values` MUST be a dependency, and leaving it out cost the entire admin
222
+ * coupon editor.
223
+ *
224
+ * This effect used to run once on mount with an eslint-disable, so it
225
+ * validated the INITIAL draft. On an edit form that draft is the empty
226
+ * defaults: the record arrives asynchronously (useQuery → useEffect →
227
+ * patch()), and `patch` is not `handleFieldChange`, so the only other caller
228
+ * of runValidation never fired. The mount-time errors therefore outlived the
229
+ * data that fixed them.
230
+ *
231
+ * Measured on production 2026-09-15: opening ARENA25 showed "3 issues" with
232
+ * "A discount value is required." under a field containing 25 and "Give the
233
+ * campaign a name." under a populated name — the three fields that are empty
234
+ * BEFORE the load. Save then refused with "Coupon: This field is required"
235
+ * and issued no PATCH at all, so no coupon could be edited through the UI.
236
+ *
237
+ * Re-validating per render is safe because runValidation returns the previous
238
+ * errors object when nothing changed (see `sameErrors`); without that this
239
+ * would setState in a loop, which is presumably why the dep was dropped.
240
+ */
201
241
  useEffect(() => {
202
242
  runValidation(values);
203
- // eslint-disable-next-line react-hooks/exhaustive-deps
204
- }, [schema, inheritedSchema, fieldToSectionIndex]);
243
+ }, [runValidation, values]);
205
244
  const handleFieldChange = useCallback((partial) => {
206
245
  onChange(partial);
207
246
  runValidation({ ...values, ...partial });
@@ -28,23 +28,23 @@ export const moneyFlowsPages = [
28
28
  {
29
29
  pageKey: "offer-to-purchase",
30
30
  pageLabel: "Offer → accept → pay",
31
- href: "/products/product-tester-offerable",
31
+ href: "/products/product-beyblade-burst-valkyrie",
32
32
  cases: [
33
33
  {
34
34
  key: "buyer-makes-offer",
35
35
  label: "A buyer can make an offer on a seller's product and it appears in their own offers list",
36
36
  roles: ["buyer"],
37
- startPage: "/products/product-tester-offerable",
37
+ startPage: "/products/product-beyblade-burst-valkyrie",
38
38
  steps: [
39
39
  STEP_SIGNIN_BUYER,
40
- "Open /products/product-tester-offerable.",
40
+ "Open /products/product-beyblade-burst-valkyrie.",
41
41
  "Click 'Make Offer'.",
42
42
  "Enter 780 as the offer amount.",
43
43
  "Enter the note 'QA offer offer-to-purchase-buyer-makes-offer'.",
44
44
  "Click 'Submit offer'.",
45
45
  "Open /user/offers.",
46
46
  ],
47
- expectedBehaviour: "An offer is created for 780 against product-tester-offerable, owned by the signed-in buyer, and the owning seller receives an offer-received notification.",
47
+ expectedBehaviour: "An offer is created for 780 against product-beyblade-burst-valkyrie, owned by the signed-in buyer, and the owning seller receives an offer-received notification.",
48
48
  expectedUiState: "The offer modal closes and a success confirmation appears. /user/offers lists a row for the product with amount ₹780 and status Pending. The page does NOT read 'No offers yet'.",
49
49
  endResult: "After reloading /user/offers the row is still listed at ₹780 with status Pending.",
50
50
  },
@@ -52,10 +52,10 @@ export const moneyFlowsPages = [
52
52
  key: "duplicate-offer-refused",
53
53
  label: "A second offer on the same listing while one is still pending is refused",
54
54
  roles: ["buyer"],
55
- startPage: "/products/product-tester-offerable",
55
+ startPage: "/products/product-beyblade-burst-valkyrie",
56
56
  steps: [
57
57
  "Sign in as the buyer ash@pokemonpalace.in, holding the pending 780 offer from the previous case.",
58
- "Open /products/product-tester-offerable.",
58
+ "Open /products/product-beyblade-burst-valkyrie.",
59
59
  "Click 'Make Offer'.",
60
60
  "Enter 800 as the offer amount.",
61
61
  "Click 'Submit offer'.",
@@ -70,15 +70,15 @@ export const moneyFlowsPages = [
70
70
  roles: ["seller"],
71
71
  startPage: "/store/offers",
72
72
  steps: [
73
- "Sign in as tester@letitrip.in, who owns store-tester-qa-seller.",
73
+ "Sign in as tyson@beybladearena.in / TempPass123!, who owns store-beyblade-arena.",
74
74
  "Open /store/offers.",
75
- "Find the row for offer-tester-sandbox-inbound (product-tester-standard-1, offered ₹120 against a listed ₹150).",
75
+ "Find the row for offer-yugi-burst-valkyrie-pending (product-beyblade-burst-valkyrie, offered ₹780 against a listed ₹999).",
76
76
  "Open the row actions menu.",
77
77
  "Click 'Accept'.",
78
78
  "Confirm in the dialog.",
79
79
  "Reload /store/offers.",
80
80
  ],
81
- expectedBehaviour: "The offer moves to Accepted, a locked price of 120 is recorded, and the buyer receives an offer-responded notification.",
81
+ expectedBehaviour: "The offer moves to Accepted, a locked price of 780 is recorded, and the buyer receives an offer-responded notification.",
82
82
  expectedUiState: "Before acting, the row menu offers Accept, Decline and Counter as enabled controls. After confirming, the row's status badge reads Accepted.",
83
83
  endResult: "After reloading /store/offers the status still reads Accepted — not Pending.",
84
84
  href: "/store/offers",
@@ -89,15 +89,15 @@ export const moneyFlowsPages = [
89
89
  roles: ["buyer"],
90
90
  startPage: "/user/offers",
91
91
  steps: [
92
- "Sign in as tester@letitrip.in, who holds offer-tester-sandbox-accepted on product-beyblade-burst-valkyrie (listed ₹999, agreed ₹780).",
92
+ "Sign in as vivaan.kapoor@gmail.com / TempPass123!, who holds offer-kaiba-dranzer-s-accepted on product-beyblade-original-dranzer-s (listed ₹1499, agreed ₹1250).",
93
93
  "Open /user/offers.",
94
94
  "Click through to checkout from the accepted offer.",
95
95
  "Read the line item price in the checkout summary.",
96
96
  "Continue to the payment step.",
97
97
  "Read the order total.",
98
98
  ],
99
- expectedBehaviour: "Every price computed for this line uses the locked 780, not the product's current 999.",
100
- expectedUiState: "The line item reads ₹780.00 and the order total reflects 780 plus fees. ₹999 appears nowhere on the checkout screen for this line.",
99
+ expectedBehaviour: "Every price computed for this line uses the locked 1250, not the product's current 1499.",
100
+ expectedUiState: "The line item reads ₹1,250.00 and the order total reflects 1250 plus fees. ₹1,499 appears nowhere on the checkout screen for this line.",
101
101
  endResult: "Nothing is placed in this case — stop at the payment step. The check is that the two numbers never appear together for the same line.",
102
102
  href: "/user/offers",
103
103
  },
@@ -105,16 +105,16 @@ export const moneyFlowsPages = [
105
105
  key: "owning-seller-cannot-offer",
106
106
  label: "The owning seller sees no Make Offer control on their own listing",
107
107
  roles: ["seller"],
108
- startPage: "/products/product-tester-standard-1",
108
+ startPage: "/products/product-beyblade-burst-valkyrie",
109
109
  steps: [
110
- "Sign in as tester@letitrip.in, who owns store-tester-qa-seller.",
111
- "Open /products/product-tester-standard-1, a listing owned by that store.",
110
+ "Sign in as tyson@beybladearena.in / TempPass123!, who owns store-beyblade-arena.",
111
+ "Open /products/product-beyblade-burst-valkyrie, a listing owned by that store.",
112
112
  "Read the action controls on the listing.",
113
113
  ],
114
114
  expectedBehaviour: "The seller is not offered any purchase path on their own listing — you cannot make an offer on, or buy, your own item.",
115
115
  expectedUiState: "'Make Offer' is ABSENT, not merely disabled. A seller-facing control such as 'Edit listing' is shown instead.",
116
116
  endResult: "Nothing is persisted; this is a read-only check of which controls render for the owner.",
117
- href: "/products/product-tester-standard-1",
117
+ href: "/products/product-beyblade-burst-valkyrie",
118
118
  },
119
119
  {
120
120
  key: "offer-lane-blocks-other-items",
@@ -122,8 +122,9 @@ export const moneyFlowsPages = [
122
122
  roles: ["buyer"],
123
123
  startPage: "/cart",
124
124
  steps: [
125
- "Sign in as tester@letitrip.in with the accepted offer on product-beyblade-burst-valkyrie in the cart.",
126
- "Open /products/product-tester-standard-2 and click 'Add to cart'.",
125
+ "Sign in as vivaan.kapoor@gmail.com / TempPass123!, who holds offer-kaiba-dranzer-s-accepted.",
126
+ "Open /user/offers and start checkout from that accepted offer so its locked line enters the cart, then return to /products.",
127
+ "Open /products/product-beyblade-burst-valkyrie and click 'Add to cart'.",
127
128
  "Open /cart.",
128
129
  "Read which lane or tab each line sits in.",
129
130
  "Attempt to check out the standard item.",
@@ -135,17 +136,17 @@ export const moneyFlowsPages = [
135
136
  },
136
137
  {
137
138
  key: "expired-offer-cannot-checkout",
138
- label: "An accepted offer past its checkout deadline can no longer be paid",
139
+ label: "An offer past its window can no longer be paid",
139
140
  roles: ["buyer"],
140
141
  startPage: "/user/offers",
141
142
  steps: [
142
- "Sign in as tester@letitrip.in.",
143
- "Open /user/offers and find offer-tester-sandbox-expiring, whose checkout window is a fraction of the run window.",
144
- "Note the countdown shown on the row.",
145
- "Reload the page until the countdown reaches zero.",
143
+ "Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
144
+ "Open /user/offers and find offer-kaiba-x-wizard-arrow-expired (product-beyblade-x-wizard-arrow, ₹700), whose window closed two days ago.",
145
+ "Read the row's status badge and the date it names.",
146
+ "Read every control the row offers.",
146
147
  "Attempt to start checkout from that offer.",
147
148
  ],
148
- expectedBehaviour: "Checkout is refused once the deadline has passed; the offer is treated as expired rather than payable.",
149
+ expectedBehaviour: "Checkout is refused once the window has passed; the offer is treated as expired rather than payable.",
149
150
  expectedUiState: "The row reads Expired and offers no checkout control. It must not silently proceed at ₹700.",
150
151
  endResult: "After reloading /user/offers the row still reads Expired.",
151
152
  href: "/user/offers",
@@ -165,7 +166,7 @@ export const moneyFlowsPages = [
165
166
  // seed-data/fixtures/money-flows__auction-win-to-payment.mjs and closing
166
167
  // ~3 minutes after this batch starts.
167
168
  //
168
- // It used to cite auction-tester-sandbox-cycle-1, which 46 OTHER
169
+ // It used to cite auction-beyblade-original-dragoon-storm, which 46 OTHER
169
170
  // citations need LIVE — one fixture with two contradictory
170
171
  // requirements, so this case could never pass its final step and the
171
172
  // rig notes recorded it as unpassable-by-design.
@@ -261,10 +262,10 @@ export const moneyFlowsPages = [
261
262
  key: "cod-order-places",
262
263
  label: "A cash-on-delivery order can be placed and shows the COD fee before confirming",
263
264
  roles: ["buyer"],
264
- startPage: "/products/product-tester-standard-1",
265
+ startPage: "/products/product-beyblade-burst-valkyrie",
265
266
  steps: [
266
267
  STEP_SIGNIN_BUYER,
267
- "Open /products/product-tester-standard-1 and click 'Add to cart'.",
268
+ "Open /products/product-beyblade-burst-valkyrie and click 'Add to cart'.",
268
269
  "Open /checkout and complete the address step.",
269
270
  "Continue to the add-ons and fees step.",
270
271
  "Select 'Cash on Delivery'.",
@@ -282,7 +283,7 @@ export const moneyFlowsPages = [
282
283
  roles: ["buyer"],
283
284
  startPage: "/checkout",
284
285
  steps: [
285
- "Sign in as the buyer ash@pokemonpalace.in with product-tester-standard-2 in the cart.",
286
+ "Sign in as the buyer ash@pokemonpalace.in with product-beyblade-original-dranzer-s in the cart.",
286
287
  "Open /checkout and complete the address step.",
287
288
  "Select UPI / manual payment.",
288
289
  STEP_PLACE_ORDER,
@@ -319,8 +320,8 @@ export const moneyFlowsPages = [
319
320
  startPage: "/checkout",
320
321
  steps: [
321
322
  STEP_SIGNIN_BUYER,
322
- "Add product-tester-standard-1 (store-tester-sandbox) to the cart.",
323
323
  "Add product-beyblade-burst-valkyrie (store-beyblade-arena) to the cart.",
324
+ "Add prizedraw-beyblade-mystery-box (store-letitrip-official) to the cart, so the cart spans two sellers.",
324
325
  "Open /checkout and reach the payment step.",
325
326
  "Write down the exact total and every fee line.",
326
327
  STEP_PLACE_ORDER,
@@ -359,63 +360,78 @@ export const moneyFlowsPages = [
359
360
  key: "out-of-stock-blocked",
360
361
  label: "A sold-out item cannot be added to the cart or checked out",
361
362
  roles: ["buyer"],
362
- startPage: "/products/product-tester-standard-sold",
363
+ startPage: "/products/product-beyblade-burst-valtryek-v3-sold-out",
363
364
  steps: [
364
365
  STEP_SIGNIN_BUYER,
365
- "Open /products/product-tester-standard-sold, a deliberately sold-out fixture.",
366
+ "Open /products/product-beyblade-burst-valtryek-v3-sold-out, a deliberately sold-out fixture.",
366
367
  "Attempt to add it to the cart.",
367
368
  ],
368
369
  expectedBehaviour: "The add is refused and no cart line is created for an unavailable item.",
369
370
  expectedUiState: "The add control is disabled or replaced by a Sold Out state, with the reason stated on the page.",
370
371
  endResult: "After reloading /cart the item is absent.",
371
- href: "/products/product-tester-standard-sold",
372
+ href: "/products/product-beyblade-burst-valtryek-v3-sold-out",
372
373
  },
373
374
  {
375
+ /*
376
+ * 🛑 THIS CASE MOVED FROM THE CART TO THE BUNDLE EDITOR, 2026-09-15.
377
+ *
378
+ * It used to open a deliberately-banned pair of fixtures — two products
379
+ * in different stores wired into one group — and assert that the cart's
380
+ * picker refused them. Those fixtures are gone, and no replacement can
381
+ * be seeded: the guard the case tests is applied at SAVE time, so a
382
+ * cross-store group cannot exist to be opened.
383
+ *
384
+ * The refusal is therefore tested where it actually fires — an admin
385
+ * ATTEMPTS the banned shape and is refused before it is stored.
386
+ */
374
387
  key: "cross-store-group-refused",
375
- label: "A group spanning two stores cannot be added as one cart line",
376
- roles: ["buyer"],
377
- startPage: "/products/product-tester-crossstore-a",
388
+ label: "A bundle whose members span two stores is refused when saved",
389
+ roles: ["admin"],
390
+ startPage: "/admin/bundles",
378
391
  steps: [
379
- STEP_SIGNIN_BUYER,
380
- "Open /products/product-tester-crossstore-a.",
381
- "Open the group picker ('Pick items').",
382
- "Attempt to select members and add them to the cart.",
392
+ "Sign in as admin@letitrip.in / TempPass123!.",
393
+ "Open /admin/bundles and start a new bundle.",
394
+ "Name it 'QA cross-store bundle'.",
395
+ "Add product-beyblade-burst-valkyrie (store-beyblade-arena) as a member.",
396
+ "Add prizedraw-beyblade-mystery-box (store-letitrip-official) as a second member — a different store.",
397
+ "Set the bundle price to 1000 and click Save.",
383
398
  ],
384
- expectedBehaviour: "The picker refuses a cross-store selection; an order cannot span two sellers.",
385
- expectedUiState: "The picker renders read-only — no quantity column and no 'Add selected to cart' control — and explains that the group spans two stores.",
386
- endResult: "After reloading /cart no line was created.",
387
- href: "/products/product-tester-crossstore-a",
399
+ inputs: { bundleName: "QA cross-store bundle", bundlePrice: 1000 },
400
+ expectedBehaviour: "The save is refused because an order belongs to exactly one seller, and a bundle spanning two stores would produce an order containing another seller's product — with no notification, no shipping resolution and no payout for that second seller. The refusal is at save time, not at add-to-cart time, so bundles already stored keep working.",
401
+ expectedUiState: "An error names the two stores, or at least states that a bundle cannot span sellers. The editor stays open with the entered members intact rather than clearing.",
402
+ endResult: "After reloading /admin/bundles there is no bundle named 'QA cross-store bundle'.",
403
+ href: "/admin/bundles",
388
404
  },
389
405
  {
390
406
  key: "classified-has-no-cart",
391
407
  label: "A classified listing offers contact, never Add to Cart",
392
408
  roles: ["buyer"],
393
- startPage: "/classified/classified-tester-sandbox-1",
409
+ startPage: "/classified/classified-beyblade-stadium-set",
394
410
  steps: [
395
411
  STEP_SIGNIN_BUYER,
396
- "Open /classified/classified-tester-sandbox-1.",
412
+ "Open /classified/classified-beyblade-stadium-set.",
397
413
  "Read every action control on the listing.",
398
414
  ],
399
415
  expectedBehaviour: "A classified is a contact-only listing type with no cart path at all.",
400
416
  expectedUiState: "Exactly one of 'Make Offer', 'Request to Buy' or a contact-seller control is present. 'Add to Cart' and 'Buy Now' are ABSENT, not disabled.",
401
417
  endResult: "Nothing is persisted; this is a read-only check of which controls render.",
402
- href: "/classified/classified-tester-sandbox-1",
418
+ href: "/classified/classified-beyblade-stadium-set",
403
419
  },
404
420
  {
405
421
  key: "guest-gated-action-prompts-signin",
406
422
  label: "A gated action while signed out prompts sign-in and then completes the original action",
407
423
  roles: ["guest"],
408
- startPage: "/products/product-tester-standard-1",
424
+ startPage: "/products/product-beyblade-burst-valkyrie",
409
425
  steps: [
410
426
  "Sign out completely.",
411
- "Open /products/product-tester-standard-1.",
427
+ "Open /products/product-beyblade-burst-valkyrie.",
412
428
  "Click the wishlist heart.",
413
429
  "Sign in as ash@pokemonpalace.in through the prompt that appears.",
414
430
  ],
415
431
  expectedBehaviour: "The click is held while signing in and then replayed, so the item ends up on the wishlist.",
416
432
  expectedUiState: "A sign-in prompt appears rather than the click doing nothing. After signing in, the heart shows the saved state.",
417
433
  endResult: "After reloading /wishlist the item is listed.",
418
- href: "/products/product-tester-standard-1",
434
+ href: "/products/product-beyblade-burst-valkyrie",
419
435
  },
420
436
  {
421
437
  key: "banned-account-blocked",
@@ -79,18 +79,20 @@ export const authored = {
79
79
  startPage: "/admin/users",
80
80
  steps: [
81
81
  "Sign in as admin@letitrip.in / TempPass123!.",
82
- "Open /admin/users and find the account claude-tester@letitrip.in.",
83
- "Open its Edit action and write down whether the tester flags are on.",
82
+ "Open /admin/users and find the account rehan.sheikh@gmail.com.",
83
+ "Open its Edit action, turn BOTH tester flags on, and save — no seeded account carries them, so the case has to set up the field it is about.",
84
+ "Reopen the same account and confirm both flags read as on.",
84
85
  "Change ONLY its bio — append the word roundtrip.",
85
86
  "Press Save.",
86
87
  "Reload the list and reopen the same account.",
87
88
  "Read the tester flags again.",
89
+ "Turn both tester flags back off and save.",
88
90
  ],
89
- inputs: { account: "claude-tester@letitrip.in", bioSuffix: "roundtrip" },
91
+ inputs: { account: "rehan.sheikh@gmail.com", bioSuffix: "roundtrip" },
90
92
  expectedBehaviour: "An editor seeded from a LIST row can only send back what that row carried. If the list serializer omits a field the update schema accepts, the editor state defaults to false and the save rewrites it — so editing any other field silently strips it. That is why the check is on a field the form did not touch.",
91
93
  expectedUiState: "Both tester flags are still on after the reload, and the bio carries the appended word.",
92
94
  expectedData: { testerFlagsPreserved: true },
93
- endResult: "The flags survive a second reload. If this fails, restore them before moving on — the bot needs them to run at all.",
95
+ endResult: "The flags survive a second reload, and the final step leaves the account back on its seeded settings with both flags off.",
94
96
  },
95
97
  "checklist-admin-admin-detail-round-trips-store-edit-keeps-verified": {
96
98
  roles: ["admin"],
@@ -62,14 +62,14 @@ export const authored = {
62
62
  startPage: "/admin/coupons",
63
63
  steps: [
64
64
  "Sign in as admin@letitrip.in / TempPass123!.",
65
- "Open /admin/coupons, find TESTERLIMITED, and read its per-user limit, total limit and current usage.",
66
- "Sign in as vivaan.kapoor@gmail.com / TempPass123! and place an order applying TESTERLIMITED.",
65
+ "Open /admin/coupons, find ARENAVIP, and read its per-user limit (1), total limit (50) and current usage (12).",
66
+ "Sign in as vivaan.kapoor@gmail.com / TempPass123! and place an order of 2 × product-beyblade-original-dranzer-s (₹2,998, above ARENAVIP's ₹2,000 minimum) applying ARENAVIP.",
67
67
  "Attempt a second order applying it again and read the refusal.",
68
- "Sign in as rehan.sheikh@gmail.com / TempPass123! and apply TESTERLIMITED at checkout.",
68
+ "Sign in as rehan.sheikh@gmail.com / TempPass123! and apply ARENAVIP at checkout on the same two items.",
69
69
  "Sign back in as admin and read the usage count.",
70
70
  ],
71
- inputs: { code: "TESTERLIMITED", perUserLimit: 1 },
72
- expectedBehaviour: "TESTERLIMITED is one use per user with NO total limit, so exhausting it for one buyer must leave it fully usable by the next. Enforcing a per-user limit as if it were global withdraws the coupon from everybody after a single redemption.",
71
+ inputs: { code: "ARENAVIP", perUserLimit: 1 },
72
+ expectedBehaviour: "ARENAVIP is one use per user, and its total limit of 50 has 38 uses left — so exhausting it for one buyer must leave it usable by the next. Enforcing a per-user limit as if it were global withdraws the coupon from everybody after a single redemption.",
73
73
  expectedUiState: "The same buyer's second attempt is refused with a limit message. A DIFFERENT buyer's attempt is accepted. The admin usage count has risen by the number of redemptions, and the coupon still reads as active.",
74
74
  expectedData: { perUserLimit: 1 },
75
75
  endResult: "The cross-account half is the case — the same-account half alone passes under either reading.",
@@ -80,7 +80,7 @@ export const authored = {
80
80
  steps: [
81
81
  "Sign in as admin@letitrip.in / TempPass123! and confirm QAADMIN15 has a minimum purchase of 500 and a maximum discount of 200.",
82
82
  "Sign in as vivaan.kapoor@gmail.com / TempPass123!.",
83
- "Add product-tester-standard-3 (₹99) to the cart alone and apply QAADMIN15 at checkout.",
83
+ "Add product-beyblade-metal-storm-pegasus (₹99) to the cart alone and apply QAADMIN15 at checkout.",
84
84
  "Read the refusal and whether it names the required amount.",
85
85
  "Add product-beyblade-original-driger-v (₹1,799) so the subtotal clears the minimum, and apply again.",
86
86
  "Read the discount amount.",
@@ -111,17 +111,18 @@ export const authored = {
111
111
  startPage: "/admin/coupons",
112
112
  steps: [
113
113
  "Sign in as admin@letitrip.in / TempPass123!.",
114
- "Open /admin/coupons and find TESTEREXPIRED, reading its end date and its active flag.",
115
- "Note that it is still marked active while its end date is in the past.",
116
- "Sign in as vivaan.kapoor@gmail.com / TempPass123! and apply TESTEREXPIRED at checkout.",
114
+ "Open /admin/coupons, open SEALED20, and write down its end date.",
115
+ "Set its end date to yesterday and LEAVE the Active toggle ON, then save — it is now past its end date while still flagged active.",
116
+ "Sign in as vivaan.kapoor@gmail.com / TempPass123! and apply SEALED20 at checkout.",
117
117
  "Read the exact wording of the refusal.",
118
- "Sign back in as admin, deactivate an ACTIVE coupon, and apply that one as the buyer.",
118
+ "Sign back in as admin, deactivate BLADER50 without touching its dates, and apply that one as the buyer.",
119
119
  "Compare the two refusal messages.",
120
+ "Sign back in as admin, restore SEALED20's end date and reactivate BLADER50.",
120
121
  ],
121
- inputs: { expiredCode: "TESTEREXPIRED" },
122
- expectedBehaviour: "Expiry and deactivation are two separate reasons and both refuse. TESTEREXPIRED is seeded past its end date while still flagged active precisely so the DATE check is exercised rather than the flag — a validator reading only the flag would accept it.",
122
+ inputs: { expiredCode: "SEALED20", deactivatedCode: "BLADER50" },
123
+ expectedBehaviour: "Expiry and deactivation are two separate reasons and both refuse. SEALED20 is pushed past its end date while still flagged active precisely so the DATE check is exercised rather than the flag — a validator reading only the flag would accept it.",
123
124
  expectedUiState: "The expired coupon is refused with a message naming expiry. The deactivated one is refused with a different message. Two identical generic refusals mean only one of the two checks is running.",
124
- endResult: "Reactivate the coupon that was deactivated. Leaving it off silently breaks every later case that applies it.",
125
+ endResult: "SEALED20's end date is restored and BLADER50 is reactivated by the final step. Leaving either changed silently breaks every later case that applies them.",
125
126
  },
126
127
  "checklist-admin-coupons-coupon-admin-is-platform-wide": {
127
128
  roles: ["admin", "buyer"],