@mohasinac/appkit 3.6.5 → 3.8.0

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 (298) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +17 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
  3. package/dist/_internal/client/features/tour/TourProvider.d.ts +5 -2
  4. package/dist/_internal/client/features/tour/TourProvider.js +125 -12
  5. package/dist/_internal/client/theme/ThemeProvider.js +1 -1
  6. package/dist/_internal/server/features/categories/data.js +2 -2
  7. package/dist/_internal/server/features/faqs/og.js +5 -1
  8. package/dist/_internal/server/features/reviews/og.js +5 -1
  9. package/dist/_internal/server/features/seo/manifest.js +2 -1
  10. package/dist/_internal/server/features/seo/og.js +7 -1
  11. package/dist/_internal/server/features/tester/visibility.d.ts +13 -0
  12. package/dist/_internal/server/features/tester/visibility.js +28 -0
  13. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  14. package/dist/_internal/server/functions/scheduled.js +9 -1
  15. package/dist/_internal/server/jobs/core/jobRunners.js +5 -0
  16. package/dist/_internal/server/jobs/core/resetOtpVerification.d.ts +17 -0
  17. package/dist/_internal/server/jobs/core/resetOtpVerification.js +88 -0
  18. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +6 -0
  19. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +35 -0
  20. package/dist/_internal/server/jobs/handlers/_helpers.d.ts +6 -0
  21. package/dist/_internal/server/jobs/handlers/_helpers.js +12 -0
  22. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  23. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  24. package/dist/_internal/server/jobs/handlers/messages.d.ts +2 -0
  25. package/dist/_internal/server/jobs/handlers/messages.js +2 -0
  26. package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +2 -0
  27. package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +2 -0
  28. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  29. package/dist/_internal/shared/actions/action-registry.js +56 -0
  30. package/dist/_internal/shared/tokens/index.d.ts +12 -12
  31. package/dist/client.d.ts +15 -3
  32. package/dist/client.js +10 -2
  33. package/dist/constants/api-endpoints.d.ts +39 -12
  34. package/dist/constants/api-endpoints.js +17 -8
  35. package/dist/constants/field-names.d.ts +9 -0
  36. package/dist/constants/field-names.js +12 -0
  37. package/dist/constants/index.d.ts +1 -1
  38. package/dist/constants/index.js +1 -1
  39. package/dist/features/account/components/LinkedAccountsSection.d.ts +11 -0
  40. package/dist/features/account/components/LinkedAccountsSection.js +11 -0
  41. package/dist/features/account/components/UserSidebar.d.ts +3 -1
  42. package/dist/features/account/components/UserSidebar.js +9 -13
  43. package/dist/features/account/hooks/useCollapsedSections.d.ts +20 -0
  44. package/dist/features/account/hooks/useCollapsedSections.js +63 -0
  45. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  46. package/dist/features/account/index.d.ts +1 -0
  47. package/dist/features/account/index.js +1 -0
  48. package/dist/features/admin/components/AdminAddressEditorView.js +2 -2
  49. package/dist/features/admin/components/AdminBidsView.js +6 -6
  50. package/dist/features/admin/components/AdminBrandsView.js +2 -2
  51. package/dist/features/admin/components/AdminCarouselView.js +2 -2
  52. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  53. package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
  54. package/dist/features/admin/components/AdminProductEditorView.js +5 -2
  55. package/dist/features/admin/components/AdminReviewsView.js +5 -5
  56. package/dist/features/admin/components/AdminSectionsView.js +3 -18
  57. package/dist/features/admin/components/AdminSidebar.js +8 -6
  58. package/dist/features/admin/components/AdminSiteSettingsView.js +133 -127
  59. package/dist/features/admin/components/AdminSublistingCategoriesView.js +1 -1
  60. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.d.ts +9 -0
  61. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +129 -0
  62. package/dist/features/admin/components/AdminTesterChecklistView.d.ts +6 -0
  63. package/dist/features/admin/components/AdminTesterChecklistView.js +93 -0
  64. package/dist/features/admin/components/AdminUserEditorView.d.ts +3 -1
  65. package/dist/features/admin/components/AdminUserEditorView.js +7 -1
  66. package/dist/features/admin/components/AdminUsersView.js +1 -1
  67. package/dist/features/admin/components/DataListingView.js +2 -2
  68. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +8 -0
  69. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +41 -0
  70. package/dist/features/admin/components/index.d.ts +6 -0
  71. package/dist/features/admin/components/index.js +3 -0
  72. package/dist/features/admin/schemas/firestore.d.ts +0 -7
  73. package/dist/features/admin/schemas/firestore.js +6 -6
  74. package/dist/features/analytics/components/PageViewTracker.d.ts +12 -0
  75. package/dist/features/analytics/components/PageViewTracker.js +22 -0
  76. package/dist/features/analytics/repository/page-views.repository.d.ts +32 -0
  77. package/dist/features/analytics/repository/page-views.repository.js +43 -0
  78. package/dist/features/analytics/types.d.ts +6 -0
  79. package/dist/features/analytics/types.js +13 -0
  80. package/dist/features/auth/components/SocialAuthButtons.js +1 -1
  81. package/dist/features/auth/hooks/useAuth.d.ts +12 -0
  82. package/dist/features/auth/hooks/useAuth.js +109 -0
  83. package/dist/features/auth/permissions/constants.d.ts +1 -1
  84. package/dist/features/auth/repository/session.repository.d.ts +2 -0
  85. package/dist/features/auth/repository/session.repository.js +4 -1
  86. package/dist/features/auth/schemas/firestore.d.ts +17 -1
  87. package/dist/features/auth/schemas/firestore.js +2 -0
  88. package/dist/features/blog/components/BlogIndexListing.js +2 -2
  89. package/dist/features/blog/schemas/firestore.d.ts +4 -0
  90. package/dist/features/cart/actions/cart-actions.js +20 -2
  91. package/dist/features/categories/api/route.js +2 -2
  92. package/dist/features/categories/components/CategoriesIndexListing.js +3 -3
  93. package/dist/features/categories/components/CategoryBundlesListing.js +2 -2
  94. package/dist/features/categories/components/CategoryDetailPageView.js +3 -1
  95. package/dist/features/categories/components/CategoryProductsListing.js +2 -2
  96. package/dist/features/categories/schemas/firestore.d.ts +4 -0
  97. package/dist/features/classified/components/ClassifiedIndexListing.js +2 -2
  98. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -2
  99. package/dist/features/email/primitives.d.ts +2 -2
  100. package/dist/features/email/primitives.js +2 -2
  101. package/dist/features/events/components/EventsIndexListing.js +2 -2
  102. package/dist/features/events/repository/event-entry.repository.js +1 -0
  103. package/dist/features/events/schemas/firestore.d.ts +7 -1
  104. package/dist/features/events/schemas/firestore.js +1 -0
  105. package/dist/features/layout/BackToTop.d.ts +6 -1
  106. package/dist/features/layout/BackToTop.js +31 -6
  107. package/dist/features/layout/BottomNavbar.js +1 -1
  108. package/dist/features/layout/NavItem.js +1 -1
  109. package/dist/features/layout/NavbarLayout.js +2 -2
  110. package/dist/features/live/components/LiveItemsIndexListing.js +2 -2
  111. package/dist/features/media/MediaVideo.js +5 -1
  112. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -2
  113. package/dist/features/products/components/AuctionsIndexListing.js +2 -2
  114. package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -2
  115. package/dist/features/products/components/ProductsIndexListing.js +2 -2
  116. package/dist/features/products/constants/action-defs.d.ts +2 -0
  117. package/dist/features/products/constants/action-defs.js +2 -0
  118. package/dist/features/products/constants/sieve.d.ts +0 -6
  119. package/dist/features/products/constants/sieve.js +4 -2
  120. package/dist/features/products/repository/products.repository.d.ts +20 -0
  121. package/dist/features/products/repository/products.repository.js +5 -0
  122. package/dist/features/products/schemas/firestore.d.ts +4 -0
  123. package/dist/features/promotions/components/CouponsIndexListing.js +18 -5
  124. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  125. package/dist/features/search/hooks/useNavSuggestions.js +20 -1
  126. package/dist/features/seller/actions/seller-actions.js +11 -6
  127. package/dist/features/seller/components/SellerAddressesView.js +2 -2
  128. package/dist/features/seller/components/SellerBidsView.js +2 -2
  129. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  130. package/dist/features/seller/components/SellerOrdersView.js +2 -2
  131. package/dist/features/seller/components/SellerProductsView.js +2 -2
  132. package/dist/features/seller/components/SellerSidebar.d.ts +3 -1
  133. package/dist/features/seller/components/SellerSidebar.js +9 -8
  134. package/dist/features/seller/components/SellerStoreCategoriesView.js +2 -2
  135. package/dist/features/seller/components/SellerTemplatesView.js +2 -2
  136. package/dist/features/shell/StepForm.js +4 -4
  137. package/dist/features/stores/actions/store-query-actions.d.ts +5 -4
  138. package/dist/features/stores/actions/store-query-actions.js +11 -8
  139. package/dist/features/stores/components/StoreAuctionsListing.js +2 -2
  140. package/dist/features/stores/components/StoreClassifiedsListing.js +2 -2
  141. package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
  142. package/dist/features/stores/components/StoreDigitalCodesListing.js +2 -2
  143. package/dist/features/stores/components/StoreLiveItemsListing.js +2 -2
  144. package/dist/features/stores/components/StorePreOrdersListing.js +2 -2
  145. package/dist/features/stores/components/StoreProductsListing.js +2 -2
  146. package/dist/features/stores/components/StoreReviewsListing.js +2 -2
  147. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  148. package/dist/features/stores/repository/store.repository.js +3 -0
  149. package/dist/features/stores/schemas/firestore.d.ts +4 -0
  150. package/dist/features/tester/actions/checklist-item-actions.d.ts +69 -0
  151. package/dist/features/tester/actions/checklist-item-actions.js +42 -0
  152. package/dist/features/tester/actions/index.d.ts +1 -0
  153. package/dist/features/tester/actions/index.js +1 -0
  154. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.d.ts +2 -0
  155. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +22 -0
  156. package/dist/features/tester/components/AdminTesterFeedbackListView.d.ts +3 -0
  157. package/dist/features/tester/components/AdminTesterFeedbackListView.js +100 -0
  158. package/dist/features/tester/components/AdminTesterFeedbackReportView.d.ts +2 -0
  159. package/dist/features/tester/components/AdminTesterFeedbackReportView.js +30 -0
  160. package/dist/features/tester/components/AdminTesterFeedbackView.d.ts +2 -0
  161. package/dist/features/tester/components/AdminTesterFeedbackView.js +29 -0
  162. package/dist/features/tester/components/TesterChecklistStepRow.d.ts +18 -0
  163. package/dist/features/tester/components/TesterChecklistStepRow.js +29 -0
  164. package/dist/features/tester/components/TesterFeedbackChart.d.ts +14 -0
  165. package/dist/features/tester/components/TesterFeedbackChart.js +18 -0
  166. package/dist/features/tester/components/TesterHubView.d.ts +5 -0
  167. package/dist/features/tester/components/TesterHubView.js +71 -0
  168. package/dist/features/tester/components/index.d.ts +11 -0
  169. package/dist/features/tester/components/index.js +7 -0
  170. package/dist/features/tester/index.d.ts +2 -0
  171. package/dist/features/tester/index.js +2 -0
  172. package/dist/features/tester/repository/index.d.ts +2 -0
  173. package/dist/features/tester/repository/index.js +2 -0
  174. package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +13 -0
  175. package/dist/features/tester/repository/tester-checklist-item.repository.js +55 -0
  176. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +50 -0
  177. package/dist/features/tester/repository/tester-checklist-response.repository.js +196 -0
  178. package/dist/features/tester/schemas/firestore.d.ts +85 -0
  179. package/dist/features/tester/schemas/firestore.js +93 -0
  180. package/dist/features/tester/schemas/index.d.ts +1 -0
  181. package/dist/features/tester/schemas/index.js +1 -0
  182. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +2 -0
  183. package/dist/features/tester/seed-data/blog-tester-seed-data.js +40 -0
  184. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +2 -0
  185. package/dist/features/tester/seed-data/categories-tester-seed-data.js +177 -0
  186. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +2 -0
  187. package/dist/features/tester/seed-data/events-tester-seed-data.js +50 -0
  188. package/dist/features/tester/seed-data/index.d.ts +6 -0
  189. package/dist/features/tester/seed-data/index.js +6 -0
  190. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +2 -0
  191. package/dist/features/tester/seed-data/products-tester-seed-data.js +151 -0
  192. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +2 -0
  193. package/dist/features/tester/seed-data/stores-tester-seed-data.js +40 -0
  194. package/dist/features/tester/seed-data/tester-checklist-seed-data.d.ts +2 -0
  195. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +248 -0
  196. package/dist/features/tester/seed-data/tester-ttl.d.ts +4 -0
  197. package/dist/features/tester/seed-data/tester-ttl.js +6 -0
  198. package/dist/features/tester/server.d.ts +9 -0
  199. package/dist/features/tester/server.js +8 -0
  200. package/dist/index.d.ts +28 -2
  201. package/dist/index.js +51 -1
  202. package/dist/next/api/api-response.js +5 -1
  203. package/dist/next/api/routeHandler.js +10 -1
  204. package/dist/next/middleware/chain.js +1 -1
  205. package/dist/next/routing/route-map.d.ts +6 -7
  206. package/dist/next/routing/route-map.js +3 -7
  207. package/dist/providers/payment-manual/index.d.ts +2 -2
  208. package/dist/providers/payment-manual/index.js +2 -2
  209. package/dist/react/contexts/SessionContext.d.ts +4 -0
  210. package/dist/react/contexts/SessionContext.js +4 -0
  211. package/dist/repositories/index.d.ts +4 -0
  212. package/dist/repositories/index.js +3 -0
  213. package/dist/security/pii-encrypt.js +12 -2
  214. package/dist/seed/addresses-seed-data.js +1 -1
  215. package/dist/seed/bids-seed-data.js +64 -185
  216. package/dist/seed/blog-posts-seed-data.js +1 -1
  217. package/dist/seed/carousel-slides-seed-data.js +22 -16
  218. package/dist/seed/carousels-seed-data.js +1 -1
  219. package/dist/seed/cart-seed-data.js +1 -1
  220. package/dist/seed/categories-seed-data.js +1 -1
  221. package/dist/seed/claimed-coupons-seed-data.js +1 -1
  222. package/dist/seed/conversations-seed-data.js +1 -1
  223. package/dist/seed/coupon-usage-seed-data.js +1 -1
  224. package/dist/seed/coupons-seed-data.js +1 -1
  225. package/dist/seed/events-seed-data.js +1 -1
  226. package/dist/seed/faq-seed-data.js +119 -3
  227. package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
  228. package/dist/seed/grouped-listings-seed-data.js +1 -1
  229. package/dist/seed/history-seed-data.js +1 -1
  230. package/dist/seed/homepage-sections-seed-data.js +1 -1
  231. package/dist/seed/index.d.ts +2 -2
  232. package/dist/seed/index.js +7 -3
  233. package/dist/seed/lottery-entries-seed-data.js +1 -1
  234. package/dist/seed/manifest.d.ts +4 -3
  235. package/dist/seed/manifest.js +13 -6
  236. package/dist/seed/notifications-seed-data.js +1 -1
  237. package/dist/seed/offers-seed-data.js +1 -1
  238. package/dist/seed/orders-seed-data.js +1 -1
  239. package/dist/seed/payouts-seed-data.js +1 -1
  240. package/dist/seed/products-art-seed-data.js +1 -1
  241. package/dist/seed/products-auctions-seed-data.js +1 -1
  242. package/dist/seed/products-classifieds-seed-data.js +1 -1
  243. package/dist/seed/products-digital-codes-seed-data.js +1 -1
  244. package/dist/seed/products-live-items-seed-data.js +1 -1
  245. package/dist/seed/products-preorders-seed-data.d.ts +2 -0
  246. package/dist/seed/products-preorders-seed-data.js +1 -1
  247. package/dist/seed/products-prize-draws-seed-data.js +1 -1
  248. package/dist/seed/products-standard-seed-data.js +1 -1
  249. package/dist/seed/products-stickers-seed-data.js +1 -1
  250. package/dist/seed/reviews-seed-data.js +1 -1
  251. package/dist/seed/scammers-seed-data.js +1 -1
  252. package/dist/seed/sessions-seed-data.js +1 -1
  253. package/dist/seed/site-settings-seed-data.js +22 -16
  254. package/dist/seed/store-addresses-seed-data.js +1 -1
  255. package/dist/seed/store-extensions-seed-data.js +1 -1
  256. package/dist/seed/stores-seed-data.js +1 -1
  257. package/dist/seed/support-tickets-seed-data.js +1 -1
  258. package/dist/seed/tester-checklist-seed-data.d.ts +2 -0
  259. package/dist/seed/tester-checklist-seed-data.js +248 -0
  260. package/dist/seed/users-seed-data.js +1 -1
  261. package/dist/seed/wishlists-seed-data.js +1 -1
  262. package/dist/server.d.ts +7 -2
  263. package/dist/server.js +19 -12
  264. package/dist/styles.css +181 -165
  265. package/dist/tailwind-utilities.css +1 -1
  266. package/dist/tokens/color-pairs.d.ts +46 -0
  267. package/dist/tokens/color-pairs.js +93 -0
  268. package/dist/tokens/index.d.ts +60 -60
  269. package/dist/tokens/index.js +30 -30
  270. package/dist/tokens/themes/cobalt-night.d.ts +3 -2
  271. package/dist/tokens/themes/cobalt-night.js +3 -2
  272. package/dist/tokens/themes/default-dark.d.ts +4 -1
  273. package/dist/tokens/themes/default-dark.js +53 -50
  274. package/dist/tokens/themes/default-light.d.ts +5 -1
  275. package/dist/tokens/themes/default-light.js +48 -44
  276. package/dist/tokens/themes/sunset.js +4 -4
  277. package/dist/tokens/tokens.css +128 -122
  278. package/dist/ui/components/CollapsibleSection.d.ts +15 -0
  279. package/dist/ui/components/CollapsibleSection.js +13 -0
  280. package/dist/ui/components/ListingToolbar.js +3 -2
  281. package/dist/ui/components/Semantic.style.css +3 -3
  282. package/dist/ui/components/SideDrawer.style.css +19 -19
  283. package/dist/ui/components/Slider.js +1 -1
  284. package/dist/ui/components/StickyToolbar.d.ts +36 -7
  285. package/dist/ui/components/StickyToolbar.js +63 -5
  286. package/dist/ui/components/Tabs.js +17 -1
  287. package/dist/ui/components/Tabs.style.css +10 -0
  288. package/dist/ui/components/Typography.js +7 -0
  289. package/dist/ui/components/surface-tokens.d.ts +0 -7
  290. package/dist/ui/components/surface-tokens.js +7 -0
  291. package/dist/ui/index.d.ts +6 -0
  292. package/dist/ui/index.js +3 -0
  293. package/dist/ui/rich-text/RichText.style.css +20 -20
  294. package/dist/utils/id-generators.d.ts +13 -0
  295. package/dist/utils/id-generators.js +17 -0
  296. package/package.json +1 -1
  297. package/scripts/seed-cli-loader.mjs +15 -0
  298. package/scripts/seed-cli.mjs +71 -17
@@ -0,0 +1,55 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { DatabaseError } from "../../../errors";
3
+ import { TESTER_CHECKLIST_ITEM_COLLECTION, TESTER_CHECKLIST_ITEM_FIELDS, createChecklistItemId, } from "../schemas/firestore";
4
+ export class TesterChecklistItemRepository extends BaseRepository {
5
+ constructor() {
6
+ super(TESTER_CHECKLIST_ITEM_COLLECTION);
7
+ }
8
+ buildSearchTokens(input) {
9
+ const rawText = [input.label, input.description, input.groupLabel, input.pageLabel]
10
+ .filter(Boolean)
11
+ .join(" ")
12
+ .toLowerCase();
13
+ return Array.from(new Set(rawText
14
+ .split(/[^a-z0-9]+/i)
15
+ .map((token) => token.trim())
16
+ .filter((token) => token.length >= 2))).slice(0, 50);
17
+ }
18
+ async createItem(input) {
19
+ const id = createChecklistItemId(input.groupKey, input.pageKey, input.label);
20
+ const searchTokens = this.buildSearchTokens(input);
21
+ return this.createWithId(id, { ...input, searchTokens });
22
+ }
23
+ async update(id, data) {
24
+ const current = await this.findById(id);
25
+ if (!current) {
26
+ throw new DatabaseError(`Failed to update checklist item: missing document ${id}`);
27
+ }
28
+ const merged = { ...current, ...data };
29
+ return super.update(id, {
30
+ ...data,
31
+ searchTokens: this.buildSearchTokens(merged),
32
+ });
33
+ }
34
+ async listActive() {
35
+ const snapshot = await this.db
36
+ .collection(this.collection)
37
+ .where(TESTER_CHECKLIST_ITEM_FIELDS.IS_ACTIVE, "==", true)
38
+ .orderBy(TESTER_CHECKLIST_ITEM_FIELDS.ORDER, "asc")
39
+ .get();
40
+ return snapshot.docs.map((doc) => this.mapDoc(doc));
41
+ }
42
+ async list(model) {
43
+ return this.sieveQuery(model, TesterChecklistItemRepository.SIEVE_FIELDS);
44
+ }
45
+ }
46
+ TesterChecklistItemRepository.SIEVE_FIELDS = {
47
+ groupKey: { canFilter: true, canSort: true },
48
+ pageKey: { canFilter: true, canSort: true },
49
+ label: { canFilter: true, canSort: false },
50
+ order: { canFilter: true, canSort: true },
51
+ isActive: { canFilter: true, canSort: false },
52
+ searchTokens: { canFilter: true, canSort: false },
53
+ createdAt: { canFilter: true, canSort: true },
54
+ };
55
+ export const testerChecklistItemRepository = new TesterChecklistItemRepository();
@@ -0,0 +1,50 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import type { FirebaseSieveFields, FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
3
+ import { type TesterAnswer, type TesterChecklistResponseDocument } from "../schemas/firestore";
4
+ export interface UpsertResponseInput {
5
+ testerId: string;
6
+ testerDisplayName: string;
7
+ checklistItemId: string;
8
+ groupKey: string;
9
+ pageKey: string;
10
+ answer?: TesterAnswer | null;
11
+ comment?: string;
12
+ screenshotUrl?: string;
13
+ }
14
+ export interface ChecklistItemCoverage {
15
+ checklistItemId: string;
16
+ groupKey: string;
17
+ pageKey: string;
18
+ yesCount: number;
19
+ noCount: number;
20
+ totalAnswered: number;
21
+ }
22
+ export interface CoverageReport {
23
+ itemCoverage: ChecklistItemCoverage[];
24
+ issues: TesterChecklistResponseDocument[];
25
+ totals: {
26
+ totalAnswered: number;
27
+ totalYes: number;
28
+ totalNo: number;
29
+ };
30
+ }
31
+ export declare class TesterChecklistResponseRepository extends BaseRepository<TesterChecklistResponseDocument> {
32
+ static readonly SIEVE_FIELDS: FirebaseSieveFields;
33
+ constructor();
34
+ /** Deterministic-ID upsert — the only mutation path for tester responses. */
35
+ upsertResponse(input: UpsertResponseInput): Promise<TesterChecklistResponseDocument>;
36
+ listForTester(testerId: string): Promise<TesterChecklistResponseDocument[]>;
37
+ list(model: SieveModel): Promise<FirebaseSieveResult<TesterChecklistResponseDocument>>;
38
+ markReviewed(id: string): Promise<TesterChecklistResponseDocument>;
39
+ /** Single collection scan (small table: testers x cases) — no join needed. */
40
+ getCoverageReport(): Promise<CoverageReport>;
41
+ /**
42
+ * Markdown dump of every answered case, joined against the checklist item
43
+ * catalog for the human-readable label/href — optimized for a future dev
44
+ * (or Claude session) to read directly and go fix the reported issues.
45
+ * Mirrors appkit/scripts/export-tester-feedback.mjs's CLI output exactly;
46
+ * keep the two in sync.
47
+ */
48
+ getMarkdownReport(siteOrigin: string): Promise<string>;
49
+ }
50
+ export declare const testerChecklistResponseRepository: TesterChecklistResponseRepository;
@@ -0,0 +1,196 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { TESTER_CHECKLIST_RESPONSE_COLLECTION, TESTER_CHECKLIST_RESPONSE_FIELDS, createChecklistResponseId, } from "../schemas/firestore";
3
+ import { testerChecklistItemRepository } from "./tester-checklist-item.repository";
4
+ function escapeMd(text) {
5
+ return (text ?? "").replace(/\r?\n/g, " ").trim();
6
+ }
7
+ function screenshotLink(screenshotUrl, siteOrigin) {
8
+ if (!screenshotUrl)
9
+ return "(none)";
10
+ const abs = screenshotUrl.startsWith("http") ? screenshotUrl : `${siteOrigin}${screenshotUrl}`;
11
+ return `[view](${abs})`;
12
+ }
13
+ export class TesterChecklistResponseRepository extends BaseRepository {
14
+ constructor() {
15
+ super(TESTER_CHECKLIST_RESPONSE_COLLECTION);
16
+ }
17
+ /** Deterministic-ID upsert — the only mutation path for tester responses. */
18
+ async upsertResponse(input) {
19
+ const id = createChecklistResponseId(input.testerId, input.checklistItemId);
20
+ const existing = await this.findById(id);
21
+ const patch = {
22
+ testerId: input.testerId,
23
+ testerDisplayName: input.testerDisplayName,
24
+ checklistItemId: input.checklistItemId,
25
+ groupKey: input.groupKey,
26
+ pageKey: input.pageKey,
27
+ status: "new",
28
+ };
29
+ if (input.answer !== undefined)
30
+ patch.answer = input.answer;
31
+ if (input.comment !== undefined)
32
+ patch.comment = input.comment;
33
+ if (input.screenshotUrl !== undefined)
34
+ patch.screenshotUrl = input.screenshotUrl;
35
+ if (existing) {
36
+ return this.update(id, patch);
37
+ }
38
+ return this.createWithId(id, {
39
+ answer: null,
40
+ ...patch,
41
+ });
42
+ }
43
+ async listForTester(testerId) {
44
+ const snapshot = await this.db
45
+ .collection(this.collection)
46
+ .where(TESTER_CHECKLIST_RESPONSE_FIELDS.TESTER_ID, "==", testerId)
47
+ .get();
48
+ return snapshot.docs.map((doc) => this.mapDoc(doc));
49
+ }
50
+ async list(model) {
51
+ return this.sieveQuery(model, TesterChecklistResponseRepository.SIEVE_FIELDS);
52
+ }
53
+ async markReviewed(id) {
54
+ return this.update(id, {
55
+ status: "reviewed",
56
+ });
57
+ }
58
+ /** Single collection scan (small table: testers x cases) — no join needed. */
59
+ async getCoverageReport() {
60
+ const snapshot = await this.db.collection(this.collection).get();
61
+ const docs = snapshot.docs.map((doc) => this.mapDoc(doc));
62
+ const byItem = new Map();
63
+ const issues = [];
64
+ let totalYes = 0;
65
+ let totalNo = 0;
66
+ for (const doc of docs) {
67
+ if (!doc.answer)
68
+ continue;
69
+ let entry = byItem.get(doc.checklistItemId);
70
+ if (!entry) {
71
+ entry = {
72
+ checklistItemId: doc.checklistItemId,
73
+ groupKey: doc.groupKey,
74
+ pageKey: doc.pageKey,
75
+ yesCount: 0,
76
+ noCount: 0,
77
+ totalAnswered: 0,
78
+ };
79
+ byItem.set(doc.checklistItemId, entry);
80
+ }
81
+ entry.totalAnswered += 1;
82
+ if (doc.answer === "yes") {
83
+ entry.yesCount += 1;
84
+ totalYes += 1;
85
+ }
86
+ else {
87
+ entry.noCount += 1;
88
+ totalNo += 1;
89
+ issues.push(doc);
90
+ }
91
+ }
92
+ return {
93
+ itemCoverage: Array.from(byItem.values()),
94
+ issues,
95
+ totals: { totalAnswered: totalYes + totalNo, totalYes, totalNo },
96
+ };
97
+ }
98
+ /**
99
+ * Markdown dump of every answered case, joined against the checklist item
100
+ * catalog for the human-readable label/href — optimized for a future dev
101
+ * (or Claude session) to read directly and go fix the reported issues.
102
+ * Mirrors appkit/scripts/export-tester-feedback.mjs's CLI output exactly;
103
+ * keep the two in sync.
104
+ */
105
+ async getMarkdownReport(siteOrigin) {
106
+ const [items, snapshot] = await Promise.all([
107
+ testerChecklistItemRepository.list({ page: "1", pageSize: "1000" }),
108
+ this.db.collection(this.collection).get(),
109
+ ]);
110
+ const itemById = new Map(items.items.map((item) => [item.id, item]));
111
+ const responses = snapshot.docs
112
+ .map((d) => this.mapDoc(d))
113
+ .filter((r) => r.answer === "yes" || r.answer === "no");
114
+ const grouped = new Map();
115
+ for (const r of responses) {
116
+ const item = itemById.get(r.checklistItemId);
117
+ const groupLabel = item?.groupLabel ?? r.groupKey ?? "Unknown group";
118
+ const pageLabel = item?.pageLabel ?? r.pageKey ?? "Unknown page";
119
+ const key = `${groupLabel}␟${pageLabel}`;
120
+ if (!grouped.has(key))
121
+ grouped.set(key, { groupLabel, pageLabel, items: [] });
122
+ grouped.get(key).items.push({ ...r, label: item?.label ?? r.checklistItemId, href: item?.href });
123
+ }
124
+ const sortedGroups = Array.from(grouped.values()).sort((a, b) => a.groupLabel.localeCompare(b.groupLabel) || a.pageLabel.localeCompare(b.pageLabel));
125
+ const issues = responses.filter((r) => r.answer === "no");
126
+ const passingWithNotes = responses.filter((r) => r.answer === "yes" && r.comment?.trim());
127
+ const lines = [];
128
+ lines.push("# Tester Feedback Report");
129
+ lines.push("");
130
+ lines.push(`Generated ${new Date().toISOString()} — ${issues.length} issue(s) ("No" answers) across ${responses.length} answered case(s), plus ${passingWithNotes.length} note(s) on passing cases.`);
131
+ lines.push("");
132
+ lines.push("---");
133
+ lines.push("");
134
+ lines.push('## Issues ("No" answers) — fix these');
135
+ lines.push("");
136
+ if (issues.length === 0) {
137
+ lines.push("_No issues reported yet._");
138
+ lines.push("");
139
+ }
140
+ else {
141
+ for (const group of sortedGroups) {
142
+ const groupIssues = group.items.filter((r) => r.answer === "no");
143
+ if (groupIssues.length === 0)
144
+ continue;
145
+ lines.push(`### ${group.groupLabel} › ${group.pageLabel}`);
146
+ lines.push("");
147
+ for (const r of groupIssues) {
148
+ lines.push(`- [ ] **${escapeMd(r.label)}**`);
149
+ lines.push(` - Tester: ${escapeMd(r.testerDisplayName)}`);
150
+ if (r.comment)
151
+ lines.push(` - Comment: ${escapeMd(r.comment)}`);
152
+ lines.push(` - Screenshot: ${screenshotLink(r.screenshotUrl, siteOrigin)}`);
153
+ if (r.href)
154
+ lines.push(` - Test this: ${r.href}`);
155
+ lines.push(` - Status: ${r.status === "reviewed" ? "reviewed" : "new"}`);
156
+ lines.push("");
157
+ }
158
+ }
159
+ }
160
+ lines.push("---");
161
+ lines.push("");
162
+ lines.push('## Notes on passing cases ("Yes" with a comment)');
163
+ lines.push("");
164
+ if (passingWithNotes.length === 0) {
165
+ lines.push("_No notes on passing cases._");
166
+ lines.push("");
167
+ }
168
+ else {
169
+ for (const group of sortedGroups) {
170
+ const groupNotes = group.items.filter((r) => r.answer === "yes" && r.comment?.trim());
171
+ if (groupNotes.length === 0)
172
+ continue;
173
+ lines.push(`### ${group.groupLabel} › ${group.pageLabel}`);
174
+ lines.push("");
175
+ for (const r of groupNotes) {
176
+ lines.push(`- **${escapeMd(r.label)}** (works)`);
177
+ lines.push(` - Tester: ${escapeMd(r.testerDisplayName)}`);
178
+ lines.push(` - Comment: ${escapeMd(r.comment)}`);
179
+ lines.push(` - Screenshot: ${screenshotLink(r.screenshotUrl, siteOrigin)}`);
180
+ lines.push("");
181
+ }
182
+ }
183
+ }
184
+ return lines.join("\n");
185
+ }
186
+ }
187
+ TesterChecklistResponseRepository.SIEVE_FIELDS = {
188
+ testerId: { canFilter: true, canSort: false },
189
+ checklistItemId: { canFilter: true, canSort: false },
190
+ groupKey: { canFilter: true, canSort: true },
191
+ pageKey: { canFilter: true, canSort: true },
192
+ answer: { canFilter: true, canSort: false },
193
+ status: { canFilter: true, canSort: false },
194
+ createdAt: { canFilter: true, canSort: true },
195
+ };
196
+ export const testerChecklistResponseRepository = new TesterChecklistResponseRepository();
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Tester Feature Firestore Document Types & Constants
3
+ *
4
+ * Two distinct collections, deliberately separate:
5
+ * - TesterChecklistItemDocument — the admin-authored catalog of test cases (mirrors FAQs).
6
+ * - TesterChecklistResponseDocument — one doc per (tester, case) answer, upserted by testerId+checklistItemId.
7
+ */
8
+ import type { BaseDocument } from "../../../_internal/shared/types/base-document";
9
+ export interface TesterChecklistItemDocument extends BaseDocument {
10
+ groupKey: string;
11
+ groupLabel: string;
12
+ pageKey: string;
13
+ pageLabel: string;
14
+ label: string;
15
+ description?: string;
16
+ href?: string;
17
+ order: number;
18
+ isActive: boolean;
19
+ searchTokens: string[];
20
+ }
21
+ export declare const TESTER_CHECKLIST_ITEM_COLLECTION: "testerChecklistItems";
22
+ export declare const TESTER_CHECKLIST_ITEM_ID_PREFIX: "checklist-";
23
+ export declare const TESTER_CHECKLIST_ITEM_INDEXED_FIELDS: readonly ["groupKey", "pageKey", "order", "isActive", "searchTokens", "createdAt"];
24
+ export declare const DEFAULT_TESTER_CHECKLIST_ITEM_DATA: Partial<TesterChecklistItemDocument>;
25
+ export type TesterChecklistItemCreateInput = Omit<TesterChecklistItemDocument, "id" | "createdAt" | "updatedAt" | "searchTokens">;
26
+ export type TesterChecklistItemUpdateInput = Partial<Pick<TesterChecklistItemDocument, "groupKey" | "groupLabel" | "pageKey" | "pageLabel" | "label" | "description" | "href" | "order" | "isActive">>;
27
+ export declare const testerChecklistItemQueryHelpers: {
28
+ readonly byGroup: (groupKey: string) => readonly ["groupKey", "==", string];
29
+ readonly byPage: (pageKey: string) => readonly ["pageKey", "==", string];
30
+ readonly active: () => readonly ["isActive", "==", true];
31
+ };
32
+ export declare function slugifyChecklistLabel(label: string): string;
33
+ export declare function createChecklistItemId(groupKey: string, pageKey: string, label: string): string;
34
+ export type TesterAnswer = "yes" | "no";
35
+ export type TesterFeedbackStatus = "new" | "reviewed";
36
+ export interface TesterChecklistResponseDocument extends BaseDocument {
37
+ testerId: string;
38
+ testerDisplayName: string;
39
+ checklistItemId: string;
40
+ groupKey: string;
41
+ pageKey: string;
42
+ answer: TesterAnswer | null;
43
+ comment?: string;
44
+ screenshotUrl?: string;
45
+ status: TesterFeedbackStatus;
46
+ }
47
+ export declare const TESTER_CHECKLIST_RESPONSE_COLLECTION: "testerChecklistResponses";
48
+ export declare const TESTER_CHECKLIST_RESPONSE_INDEXED_FIELDS: readonly ["testerId", "checklistItemId", "groupKey", "pageKey", "answer", "status", "createdAt"];
49
+ export declare const DEFAULT_TESTER_CHECKLIST_RESPONSE_DATA: Partial<TesterChecklistResponseDocument>;
50
+ export declare function createChecklistResponseId(testerId: string, checklistItemId: string): string;
51
+ export declare const testerChecklistResponseQueryHelpers: {
52
+ readonly byTester: (testerId: string) => readonly ["testerId", "==", string];
53
+ readonly byItem: (checklistItemId: string) => readonly ["checklistItemId", "==", string];
54
+ readonly byAnswer: (answer: TesterAnswer) => readonly ["answer", "==", TesterAnswer];
55
+ readonly byStatus: (status: TesterFeedbackStatus) => readonly ["status", "==", TesterFeedbackStatus];
56
+ };
57
+ export declare const TESTER_CHECKLIST_ITEM_FIELDS: {
58
+ readonly ID: "id";
59
+ readonly GROUP_KEY: "groupKey";
60
+ readonly GROUP_LABEL: "groupLabel";
61
+ readonly PAGE_KEY: "pageKey";
62
+ readonly PAGE_LABEL: "pageLabel";
63
+ readonly LABEL: "label";
64
+ readonly DESCRIPTION: "description";
65
+ readonly HREF: "href";
66
+ readonly ORDER: "order";
67
+ readonly IS_ACTIVE: "isActive";
68
+ readonly SEARCH_TOKENS: "searchTokens";
69
+ readonly CREATED_AT: "createdAt";
70
+ readonly UPDATED_AT: "updatedAt";
71
+ };
72
+ export declare const TESTER_CHECKLIST_RESPONSE_FIELDS: {
73
+ readonly ID: "id";
74
+ readonly TESTER_ID: "testerId";
75
+ readonly TESTER_DISPLAY_NAME: "testerDisplayName";
76
+ readonly CHECKLIST_ITEM_ID: "checklistItemId";
77
+ readonly GROUP_KEY: "groupKey";
78
+ readonly PAGE_KEY: "pageKey";
79
+ readonly ANSWER: "answer";
80
+ readonly COMMENT: "comment";
81
+ readonly SCREENSHOT_URL: "screenshotUrl";
82
+ readonly STATUS: "status";
83
+ readonly CREATED_AT: "createdAt";
84
+ readonly UPDATED_AT: "updatedAt";
85
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Tester Feature Firestore Document Types & Constants
3
+ *
4
+ * Two distinct collections, deliberately separate:
5
+ * - TesterChecklistItemDocument — the admin-authored catalog of test cases (mirrors FAQs).
6
+ * - TesterChecklistResponseDocument — one doc per (tester, case) answer, upserted by testerId+checklistItemId.
7
+ */
8
+ import { generateChecklistItemId } from "../../../utils/id-generators";
9
+ export const TESTER_CHECKLIST_ITEM_COLLECTION = "testerChecklistItems";
10
+ export const TESTER_CHECKLIST_ITEM_ID_PREFIX = "checklist-";
11
+ export const TESTER_CHECKLIST_ITEM_INDEXED_FIELDS = [
12
+ "groupKey",
13
+ "pageKey",
14
+ "order",
15
+ "isActive",
16
+ "searchTokens",
17
+ "createdAt",
18
+ ];
19
+ export const DEFAULT_TESTER_CHECKLIST_ITEM_DATA = {
20
+ order: 0,
21
+ isActive: true,
22
+ searchTokens: [],
23
+ };
24
+ export const testerChecklistItemQueryHelpers = {
25
+ byGroup: (groupKey) => ["groupKey", "==", groupKey],
26
+ byPage: (pageKey) => ["pageKey", "==", pageKey],
27
+ active: () => ["isActive", "==", true],
28
+ };
29
+ export function slugifyChecklistLabel(label) {
30
+ return label
31
+ .toLowerCase()
32
+ .trim()
33
+ .replace(/[^\w\s-]/g, "")
34
+ .replace(/[\s_-]+/g, "-")
35
+ .replace(/^-+|-+$/g, "")
36
+ .slice(0, 40);
37
+ }
38
+ export function createChecklistItemId(groupKey, pageKey, label) {
39
+ return generateChecklistItemId({ groupKey, pageKey, label });
40
+ }
41
+ export const TESTER_CHECKLIST_RESPONSE_COLLECTION = "testerChecklistResponses";
42
+ export const TESTER_CHECKLIST_RESPONSE_INDEXED_FIELDS = [
43
+ "testerId",
44
+ "checklistItemId",
45
+ "groupKey",
46
+ "pageKey",
47
+ "answer",
48
+ "status",
49
+ "createdAt",
50
+ ];
51
+ export const DEFAULT_TESTER_CHECKLIST_RESPONSE_DATA = {
52
+ answer: null,
53
+ status: "new",
54
+ };
55
+ export function createChecklistResponseId(testerId, checklistItemId) {
56
+ return `${testerId}__${checklistItemId}`;
57
+ }
58
+ export const testerChecklistResponseQueryHelpers = {
59
+ byTester: (testerId) => ["testerId", "==", testerId],
60
+ byItem: (checklistItemId) => ["checklistItemId", "==", checklistItemId],
61
+ byAnswer: (answer) => ["answer", "==", answer],
62
+ byStatus: (status) => ["status", "==", status],
63
+ };
64
+ // --- Field name constants -----------------------------------------------------
65
+ export const TESTER_CHECKLIST_ITEM_FIELDS = {
66
+ ID: "id",
67
+ GROUP_KEY: "groupKey",
68
+ GROUP_LABEL: "groupLabel",
69
+ PAGE_KEY: "pageKey",
70
+ PAGE_LABEL: "pageLabel",
71
+ LABEL: "label",
72
+ DESCRIPTION: "description",
73
+ HREF: "href",
74
+ ORDER: "order",
75
+ IS_ACTIVE: "isActive",
76
+ SEARCH_TOKENS: "searchTokens",
77
+ CREATED_AT: "createdAt",
78
+ UPDATED_AT: "updatedAt",
79
+ };
80
+ export const TESTER_CHECKLIST_RESPONSE_FIELDS = {
81
+ ID: "id",
82
+ TESTER_ID: "testerId",
83
+ TESTER_DISPLAY_NAME: "testerDisplayName",
84
+ CHECKLIST_ITEM_ID: "checklistItemId",
85
+ GROUP_KEY: "groupKey",
86
+ PAGE_KEY: "pageKey",
87
+ ANSWER: "answer",
88
+ COMMENT: "comment",
89
+ SCREENSHOT_URL: "screenshotUrl",
90
+ STATUS: "status",
91
+ CREATED_AT: "createdAt",
92
+ UPDATED_AT: "updatedAt",
93
+ };
@@ -0,0 +1 @@
1
+ export * from "./firestore";
@@ -0,0 +1 @@
1
+ export * from "./firestore";
@@ -0,0 +1,2 @@
1
+ import type { BlogPostDocument } from "../../blog/schemas";
2
+ export declare const blogTesterSeedData: Partial<BlogPostDocument>[];
@@ -0,0 +1,40 @@
1
+ /*
2
+ * WHY: Shared tester sandbox — 1 disposable test blog post so testers can exercise the
3
+ * blog reading flow. Auto-expires after 7 days (testerSandboxCleanup).
4
+ * WHAT: Exports blogTesterSeedData — 1 Partial<BlogPostDocument> tagged isTestData:true.
5
+ *
6
+ * EXPORTS:
7
+ * blogTesterSeedData — Array of 1 Partial<BlogPostDocument> for the seed runner
8
+ *
9
+ * @tag domain:blog,tester
10
+ * @tag layer:seed
11
+ * @tag pattern:none
12
+ * @tag access:server-only
13
+ * @tag consumers:seed/index.ts,seed-cli.mjs
14
+ * @tag sideEffects:none
15
+ */
16
+ import { seedExtMedia } from "../../../seed/_helpers/media";
17
+ import { testDataExpiresAt } from "./tester-ttl";
18
+ export const blogTesterSeedData = [
19
+ {
20
+ id: "blog-tester-sandbox-post",
21
+ title: "Welcome to the Tester Sandbox",
22
+ slug: "tester-sandbox-post",
23
+ excerpt: "A disposable test blog post for the tester QA program — check that images, formatting, and reading flow work correctly.",
24
+ content: `<h2>This is a test post</h2><p>It exists only for the tester QA program and will be deleted automatically after 7 days.</p><p>Check: does this render correctly? Are images loading? Is the layout readable?</p>`,
25
+ coverImage: { type: "image", url: seedExtMedia("https://picsum.photos/seed/blog-cover-tester-sandbox-post-20260101/1200/600"), alt: "Test cover image" },
26
+ category: "guides",
27
+ tags: ["test"],
28
+ isFeatured: false,
29
+ status: "published",
30
+ publishedAt: new Date(),
31
+ authorId: "user-admin-letitrip",
32
+ authorName: "LetItRip Admin",
33
+ readTimeMinutes: 1,
34
+ views: 0,
35
+ isTestData: true,
36
+ testDataExpiresAt: testDataExpiresAt(),
37
+ createdAt: new Date(),
38
+ updatedAt: new Date(),
39
+ },
40
+ ];
@@ -0,0 +1,2 @@
1
+ import type { CategoryDocument } from "../../categories/schemas";
2
+ export declare const categoriesTesterSeedData: Partial<CategoryDocument>[];