@mohasinac/appkit 3.7.0 → 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 (40) hide show
  1. package/dist/_internal/server/features/categories/data.js +2 -2
  2. package/dist/_internal/shared/actions/action-registry.js +8 -0
  3. package/dist/constants/api-endpoints.d.ts +3 -0
  4. package/dist/constants/api-endpoints.js +1 -0
  5. package/dist/features/admin/components/AdminBidsView.js +6 -6
  6. package/dist/features/admin/components/AdminBrandsView.js +2 -2
  7. package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
  8. package/dist/features/admin/components/AdminReviewsView.js +5 -5
  9. package/dist/features/admin/components/AdminSublistingCategoriesView.js +1 -1
  10. package/dist/features/auth/repository/session.repository.d.ts +2 -0
  11. package/dist/features/auth/repository/session.repository.js +4 -1
  12. package/dist/features/categories/api/route.js +2 -2
  13. package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
  14. package/dist/features/events/repository/event-entry.repository.js +1 -0
  15. package/dist/features/events/schemas/firestore.d.ts +3 -1
  16. package/dist/features/events/schemas/firestore.js +1 -0
  17. package/dist/features/layout/BottomNavbar.js +1 -1
  18. package/dist/features/layout/NavItem.js +1 -1
  19. package/dist/features/layout/NavbarLayout.js +2 -2
  20. package/dist/features/products/constants/sieve.d.ts +0 -6
  21. package/dist/features/products/constants/sieve.js +4 -2
  22. package/dist/features/products/repository/products.repository.d.ts +20 -0
  23. package/dist/features/products/repository/products.repository.js +5 -0
  24. package/dist/features/promotions/components/CouponsIndexListing.js +16 -3
  25. package/dist/features/search/hooks/useNavSuggestions.js +20 -1
  26. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  27. package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
  28. package/dist/features/stores/repository/store.repository.js +3 -0
  29. package/dist/features/tester/components/AdminTesterFeedbackView.js +22 -2
  30. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +8 -0
  31. package/dist/features/tester/repository/tester-checklist-response.repository.js +98 -0
  32. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +1 -1
  33. package/dist/seed/bids-seed-data.js +63 -184
  34. package/dist/styles.css +19 -19
  35. package/dist/tailwind-utilities.css +1 -1
  36. package/dist/tokens/themes/default-dark.js +9 -9
  37. package/dist/tokens/themes/default-light.js +9 -9
  38. package/dist/tokens/tokens.css +18 -18
  39. package/dist/ui/components/ListingToolbar.js +1 -1
  40. package/package.json +1 -1
@@ -1,5 +1,15 @@
1
1
  import { BaseRepository } from "../../../providers/db-firebase";
2
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
+ }
3
13
  export class TesterChecklistResponseRepository extends BaseRepository {
4
14
  constructor() {
5
15
  super(TESTER_CHECKLIST_RESPONSE_COLLECTION);
@@ -85,6 +95,94 @@ export class TesterChecklistResponseRepository extends BaseRepository {
85
95
  totals: { totalAnswered: totalYes + totalNo, totalYes, totalNo },
86
96
  };
87
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
+ }
88
186
  }
89
187
  TesterChecklistResponseRepository.SIEVE_FIELDS = {
90
188
  testerId: { canFilter: true, canSort: false },
@@ -11,7 +11,7 @@
11
11
  * @tag layer:seed
12
12
  * @tag pattern:none
13
13
  * @tag access:server-only
14
- * @tag consumers:seed/index.ts,seed/runner.ts,SeedPanel
14
+ * @tag consumers:seed/index.ts,seed/runner.ts
15
15
  * @tag sideEffects:none
16
16
  */
17
17
  function group(groupKey, groupLabel, pages) {
@@ -1,9 +1,17 @@
1
1
  /*
2
- * WHY: Seeds auction bids representing participant offers on YGO card auctions.
3
- * WHAT: Exports 80+ bids (2–8 per auction across 20 auctions in auctions-seed-data.ts). Bidders: user-yugi-muto + user-admin-letitrip on Kaiba auctions; user-yugi-muto + user-seto-kaiba on Admin auctions. Amount progression +5–10% per step from startingBid. Status: active (current high bid), outbid (superseded), won (auction ended). Bid IDs: bid-{productSlug}-{userName}-{YYYYMMDD}-{rand6}.
2
+ * WHY: Seeds auction bids representing participant offers on the live Beyblade auctions
3
+ * in products-auctions-seed-data.ts.
4
+ * WHAT: Exports bids for the 2 seeded auctions (auction-beyblade-original-dragoon-storm,
5
+ * auction-beyblade-metal-lightning-l-drago) — bid counts and final amounts match
6
+ * each auction's `bidCount`/`currentBid` fields exactly, so the product card's
7
+ * "N bids" summary and the detail page's bid-history list agree. Bidders: 3 buyer
8
+ * personas (Meera Nair, Rohit Agarwal, Ananya Patel), never the store's own seller
9
+ * (user-tyson-blader owns store-beyblade-arena). Status: newest bid per auction is
10
+ * "active", the rest "outbid". Bid IDs: bid-{productSlug}-{userName}-{YYYYMMDD}-{rand6}.
4
11
  *
5
12
  * EXPORTS:
6
- * bidsSeedData — Array of 80+ bid documents with progressive amounts + status distribution
13
+ * bidsSeedData — Array of bid documents, one set per seeded auction, counts matching
14
+ * each auction's bidCount field
7
15
  *
8
16
  * @tag domain:auctions,bids
9
17
  * @tag layer:seed
@@ -15,9 +23,14 @@
15
23
  const NOW = new Date();
16
24
  const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
17
25
  const BIDDER_EMAILS = {
18
- "user-yugi-muto": "yugi@duelkingdom.in",
19
- "user-admin-letitrip": "admin@letitrip.in",
20
- "user-seto-kaiba": "kaiba@kaibalandmark.in",
26
+ "user-meera-bey": "meera.blader@gmail.com",
27
+ "user-rohit-collector": "rohit.collect@gmail.com",
28
+ "user-ananya-collector": "ananya.patel@gmail.com",
29
+ };
30
+ const BIDDER_NAMES = {
31
+ "user-meera-bey": "Meera Nair",
32
+ "user-rohit-collector": "Rohit Agarwal",
33
+ "user-ananya-collector": "Ananya Patel",
21
34
  };
22
35
  function withBidDefaults(b) {
23
36
  return {
@@ -29,183 +42,49 @@ function withBidDefaults(b) {
29
42
  updatedAt: (b.createdAt ?? NOW),
30
43
  };
31
44
  }
32
- const _rawBidsSeedData = [
33
- // Kaiba Auctions — Yugi & Admin bidding
34
- {
35
- id: "bid-blue-eyes-psa10-yugi-20260515-001",
36
- productId: "auction-psa10-blue-eyes-lob",
37
- userId: "user-yugi-muto",
38
- userName: "Yugi Muto",
39
- bidAmount: 60000000, // ₹6,00,000 (paise)
40
- status: "active",
41
- bidDate: daysAgo(5),
42
- createdAt: daysAgo(5),
43
- },
44
- {
45
- id: "bid-blue-eyes-psa10-admin-20260514-001",
46
- productId: "auction-psa10-blue-eyes-lob",
47
- userId: "user-admin-letitrip",
48
- userName: "LetItRip Admin",
49
- bidAmount: 50000000, // ₹5,00,000 (paise)
50
- status: "outbid",
51
- bidDate: daysAgo(6),
52
- createdAt: daysAgo(6),
53
- },
54
- {
55
- id: "bid-blue-eyes-psa10-yugi-20260513-001",
56
- productId: "auction-psa10-blue-eyes-lob",
57
- userId: "user-yugi-muto",
58
- userName: "Yugi Muto",
59
- bidAmount: 45000000, // ₹4,50,000 (paise)
60
- status: "outbid",
61
- bidDate: daysAgo(7),
62
- createdAt: daysAgo(7),
63
- },
64
- {
65
- id: "bid-blue-eyes-psa10-admin-20260512-001",
66
- productId: "auction-psa10-blue-eyes-lob",
67
- userId: "user-admin-letitrip",
68
- userName: "LetItRip Admin",
69
- bidAmount: 40000000, // ₹4,00,000 (paise)
70
- status: "outbid",
71
- bidDate: daysAgo(8),
72
- createdAt: daysAgo(8),
73
- },
74
- {
75
- id: "bid-dark-magician-psa9-yugi-20260518-002",
76
- productId: "auction-psa9-dark-magician-lob",
77
- userId: "user-yugi-muto",
78
- userName: "Yugi Muto",
79
- bidAmount: 45000000, // ₹4,50,000 (paise)
80
- status: "active",
81
- bidDate: daysAgo(2),
82
- createdAt: daysAgo(2),
83
- },
84
- {
85
- id: "bid-dark-magician-psa9-admin-20260517-002",
86
- productId: "auction-psa9-dark-magician-lob",
87
- userId: "user-admin-letitrip",
88
- userName: "LetItRip Admin",
89
- bidAmount: 40000000, // ₹4,00,000 (paise)
90
- status: "outbid",
91
- bidDate: daysAgo(3),
92
- createdAt: daysAgo(3),
93
- },
94
- {
95
- id: "bid-exodia-ended-yugi-20260513-won",
96
- productId: "auction-ended-psa10-exodia",
97
- userId: "user-yugi-muto",
98
- userName: "Yugi Muto",
99
- bidAmount: 90000000, // ₹9,00,000 (paise) — final
100
- status: "won",
101
- bidDate: daysAgo(7),
102
- createdAt: daysAgo(7),
103
- },
104
- {
105
- id: "bid-exodia-ended-admin-20260512-outbid",
106
- productId: "auction-ended-psa10-exodia",
107
- userId: "user-admin-letitrip",
108
- userName: "LetItRip Admin",
109
- bidAmount: 80000000, // ₹8,00,000 (paise)
110
- status: "outbid",
111
- bidDate: daysAgo(8),
112
- createdAt: daysAgo(8),
113
- },
114
- // [... more bids on remaining 14 Kaiba auctions: 2–8 bids each, distributed active/outbid/won ...]
115
- // Admin Store Auctions — Yugi & Kaiba bidding
116
- {
117
- id: "bid-ra-authentic-kaiba-20260519-001",
118
- productId: "auction-admin-ra-authentic-card",
119
- userId: "user-seto-kaiba",
120
- userName: "Seto Kaiba",
121
- bidAmount: 32000000, // ₹3,20,000 (paise)
122
- status: "active",
123
- bidDate: daysAgo(1),
124
- createdAt: daysAgo(1),
125
- },
126
- {
127
- id: "bid-ra-authentic-yugi-20260518-001",
128
- productId: "auction-admin-ra-authentic-card",
129
- userId: "user-yugi-muto",
130
- userName: "Yugi Muto",
131
- bidAmount: 28000000, // ₹2,80,000 (paise)
132
- status: "outbid",
133
- bidDate: daysAgo(2),
134
- createdAt: daysAgo(2),
135
- },
136
- {
137
- id: "bid-obelisk-kaiba-20260520-002",
138
- productId: "auction-admin-obelisk-authentic",
139
- userId: "user-seto-kaiba",
140
- userName: "Seto Kaiba",
141
- bidAmount: 39000000, // ₹3,90,000 (paise)
142
- status: "active",
143
- bidDate: daysAgo(0),
144
- createdAt: daysAgo(0),
145
- },
146
- {
147
- id: "bid-obelisk-yugi-20260519-002",
148
- productId: "auction-admin-obelisk-authentic",
149
- userId: "user-yugi-muto",
150
- userName: "Yugi Muto",
151
- bidAmount: 35000000, // ₹3,50,000 (paise)
152
- status: "outbid",
153
- bidDate: daysAgo(1),
154
- createdAt: daysAgo(1),
155
- },
156
- {
157
- id: "bid-yugi-promo-ended-kaiba-20260516-won",
158
- productId: "auction-admin-ended-yugi-promo",
159
- userId: "user-seto-kaiba",
160
- userName: "Seto Kaiba",
161
- bidAmount: 9000000, // ₹90,000 (paise) — final
162
- status: "won",
163
- bidDate: daysAgo(4),
164
- createdAt: daysAgo(4),
165
- },
166
- // [... more bids on remaining 3 Admin auctions ...]
167
- ];
168
- // Expand to 80+ bids with varied distributions
169
- const expandedBids = [];
170
- const auctionIds = [
171
- "auction-psa10-blue-eyes-lob",
172
- "auction-psa9-dark-magician-lob",
173
- "auction-1st-ed-pot-of-greed",
174
- "auction-psa9-chaos-emperor",
175
- "auction-1st-ed-mirror-force",
176
- "auction-bgs95-dark-magician-girl",
177
- "auction-raw-lob-complete-set",
178
- "auction-psa8-monster-reborn",
179
- "auction-admin-ra-authentic-card",
180
- "auction-admin-obelisk-authentic",
181
- ];
182
- const bidderPairs = [
183
- { id: "user-yugi-muto", name: "Yugi Muto" },
184
- { id: "user-admin-letitrip", name: "LetItRip Admin" },
185
- { id: "user-seto-kaiba", name: "Seto Kaiba" },
186
- ];
187
- for (let i = _rawBidsSeedData.length; i < 80; i++) {
188
- const auction = auctionIds[i % auctionIds.length];
189
- const bidderIdx = Math.floor(i / 8) % bidderPairs.length;
190
- const bidder = bidderPairs[bidderIdx];
191
- const baseAmount = 30000000 + Math.random() * 30000000; // ₹3,00,000 to ₹6,00,000
192
- const status = Math.random() < 0.6
193
- ? "outbid"
194
- : Math.random() < 0.3
195
- ? "active"
196
- : "won";
197
- expandedBids.push({
198
- id: `bid-${auction.split("-").pop()}-${bidder.name.split(" ").join("").toLowerCase()}-20260515-${String(i).padStart(3, "0")}`,
199
- productId: auction,
200
- userId: bidder.id,
201
- userName: bidder.name,
202
- bidAmount: Math.floor(baseAmount),
203
- status,
204
- bidDate: daysAgo(Math.floor(Math.random() * 14)),
205
- createdAt: daysAgo(Math.floor(Math.random() * 14)),
45
+ /** One ascending bid ladder per auction — last entry is "active", the rest "outbid". */
46
+ function buildLadder(params) {
47
+ const { productId, productTitle, startingBid, currentBid, bidderIds } = params;
48
+ const steps = bidderIds.length;
49
+ const range = currentBid - startingBid;
50
+ return bidderIds.map((userId, i) => {
51
+ const amount = i === steps - 1 ? currentBid : Math.round(startingBid + (range * (i + 1)) / (steps + 1));
52
+ const daysBack = steps - i;
53
+ return {
54
+ id: `bid-${productId.replace(/^auction-/, "")}-${userId.replace(/^user-/, "")}-20260601-${String(i).padStart(3, "0")}`,
55
+ productId,
56
+ productTitle,
57
+ userId,
58
+ userName: BIDDER_NAMES[userId] ?? userId,
59
+ bidAmount: amount,
60
+ status: i === steps - 1 ? "active" : "outbid",
61
+ bidDate: daysAgo(daysBack),
62
+ createdAt: daysAgo(daysBack),
63
+ };
206
64
  });
207
65
  }
208
- export const bidsSeedData = [
209
- ..._rawBidsSeedData,
210
- ...expandedBids,
211
- ].slice(0, 80).map(withBidDefaults);
66
+ const _rawBidsSeedData = [
67
+ // auction-beyblade-original-dragoon-storm — bidCount: 3, currentBid: 349900
68
+ ...buildLadder({
69
+ productId: "auction-beyblade-original-dragoon-storm",
70
+ productTitle: "Beyblade Original — Dragoon Storm (Rare Sealed)",
71
+ startingBid: 299900,
72
+ currentBid: 349900,
73
+ bidderIds: ["user-rohit-collector", "user-ananya-collector", "user-meera-bey"],
74
+ }),
75
+ // auction-beyblade-metal-lightning-l-drago — bidCount: 5, currentBid: 229900
76
+ ...buildLadder({
77
+ productId: "auction-beyblade-metal-lightning-l-drago",
78
+ productTitle: "Metal Fight Beyblade BB-99 Lightning L-Drago",
79
+ startingBid: 199900,
80
+ currentBid: 229900,
81
+ bidderIds: [
82
+ "user-meera-bey",
83
+ "user-rohit-collector",
84
+ "user-ananya-collector",
85
+ "user-rohit-collector",
86
+ "user-meera-bey",
87
+ ],
88
+ }),
89
+ ];
90
+ export const bidsSeedData = _rawBidsSeedData.map(withBidDefaults);