@mohasinac/appkit 4.9.1 → 4.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/admin/repository/notification.repository.js +2 -2
- package/dist/features/auctions/repository/bid.repository.d.ts +3 -1
- package/dist/features/auctions/repository/bid.repository.js +3 -3
- package/dist/features/auth/repository/session.repository.js +4 -4
- package/dist/features/auth/repository/user.repository.js +3 -3
- package/dist/features/blog/repository/blog.repository.d.ts +4 -1
- package/dist/features/blog/repository/blog.repository.js +4 -4
- package/dist/features/cart/repository/cart.repository.js +3 -3
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +2 -1
- package/dist/features/catalogue/repository/catalogue.repository.js +2 -2
- package/dist/features/categories/repository/categories.repository.js +2 -2
- package/dist/features/events/repository/event-entry.repository.js +2 -2
- package/dist/features/events/repository/events.repository.js +4 -4
- package/dist/features/faq/repository/faqs.repository.js +2 -2
- package/dist/features/homepage/repository/carousel.repository.js +3 -3
- package/dist/features/homepage/repository/homepage-sections.repository.js +3 -3
- package/dist/features/lottery/repository/lottery-entry.repository.js +2 -2
- package/dist/features/orders/repository/orders.repository.d.ts +5 -1
- package/dist/features/orders/repository/orders.repository.js +5 -5
- package/dist/features/payments/repository/payout.repository.d.ts +4 -1
- package/dist/features/payments/repository/payout.repository.js +4 -4
- package/dist/features/products/repository/products.repository.d.ts +7 -1
- package/dist/features/products/repository/products.repository.js +7 -7
- package/dist/features/promotions/repository/coupons.repository.d.ts +4 -1
- package/dist/features/promotions/repository/coupons.repository.js +4 -2
- package/dist/features/reviews/repository/reviews.repository.d.ts +3 -1
- package/dist/features/reviews/repository/reviews.repository.js +3 -3
- package/dist/features/scams/repository/scammer.repository.d.ts +2 -1
- package/dist/features/scams/repository/scammer.repository.js +2 -2
- package/dist/features/server-errors/repository/server-errors.repository.js +2 -2
- package/dist/features/shipments/repository/shipment-items.repository.d.ts +2 -1
- package/dist/features/shipments/repository/shipment-items.repository.js +2 -2
- package/dist/features/shipments/repository/shipment-lots.repository.d.ts +2 -1
- package/dist/features/shipments/repository/shipment-lots.repository.js +2 -2
- package/dist/features/shipments/repository/shipments.repository.d.ts +3 -1
- package/dist/features/shipments/repository/shipments.repository.js +3 -3
- package/dist/features/support/repository/support.repository.js +3 -3
- package/dist/features/tester/repository/tester-checklist-item.repository.js +2 -2
- package/dist/features/tester/repository/tester-checklist-response.repository.js +2 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +61 -1
- package/dist/index.d.ts +1 -0
- package/dist/providers/db-firebase/index.d.ts +1 -1
- package/dist/providers/db-firebase/index.js +1 -1
- package/dist/providers/db-firebase/sieve.d.ts +19 -0
- package/dist/providers/db-firebase/sieve.js +11 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +3 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
|
-
import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
2
|
+
import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
3
3
|
import { NOTIFICATIONS_COLLECTION, NOTIFICATION_FIELDS, } from "../schemas";
|
|
4
4
|
import { serverLogger } from "../../../monitoring";
|
|
5
5
|
import { serverTimestamp } from "../../../contracts/field-ops";
|
|
@@ -218,6 +218,6 @@ NotificationRepository.SIEVE_FIELDS = {
|
|
|
218
218
|
priority: { canFilter: true, canSort: true },
|
|
219
219
|
isRead: { canFilter: true, canSort: false },
|
|
220
220
|
relatedType: { canFilter: true, canSort: false },
|
|
221
|
-
createdAt: { canFilter: true, canSort: true },
|
|
221
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
222
222
|
};
|
|
223
223
|
export const notificationRepository = new NotificationRepository();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Data access layer for bid documents in Firestore
|
|
5
5
|
*/
|
|
6
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
6
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
7
7
|
import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
|
|
8
8
|
import type { SieveModel, FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
9
9
|
import type { BidDocument, BidCreateInput, BidUpdateInput, BidAdminUpdateInput, BidStatus } from "../schemas";
|
|
@@ -138,10 +138,12 @@ export declare class BidRepository extends BaseRepository<BidDocument> {
|
|
|
138
138
|
bidDate: {
|
|
139
139
|
canFilter: boolean;
|
|
140
140
|
canSort: boolean;
|
|
141
|
+
parseValue: typeof parseSieveDateValue;
|
|
141
142
|
};
|
|
142
143
|
createdAt: {
|
|
143
144
|
canFilter: boolean;
|
|
144
145
|
canSort: boolean;
|
|
146
|
+
parseValue: typeof parseSieveDateValue;
|
|
145
147
|
};
|
|
146
148
|
};
|
|
147
149
|
/**
|
|
@@ -4,7 +4,7 @@ import { normalizeError } from "../../../errors/normalize";
|
|
|
4
4
|
*
|
|
5
5
|
* Data access layer for bid documents in Firestore
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
8
8
|
import { DatabaseError } from "../../../errors";
|
|
9
9
|
import { BID_COLLECTION, createBidId } from "../schemas";
|
|
10
10
|
import { encryptPiiFields, decryptPiiFields, BID_PII_FIELDS, } from "../../../security";
|
|
@@ -375,7 +375,7 @@ BidRepository.SIEVE_FIELDS = {
|
|
|
375
375
|
bidAmount: { canFilter: true, canSort: true },
|
|
376
376
|
status: { canFilter: true, canSort: true },
|
|
377
377
|
isWinning: { canFilter: true, canSort: false },
|
|
378
|
-
bidDate: { canFilter: true, canSort: true },
|
|
379
|
-
createdAt: { canFilter: true, canSort: true },
|
|
378
|
+
bidDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
379
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
380
380
|
};
|
|
381
381
|
export const bidRepository = new BidRepository();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
3
|
import { serverLogger } from "../../../monitoring";
|
|
4
|
-
import { BaseRepository, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { generateSessionId } from "../token-helpers";
|
|
6
6
|
import { SESSION_COLLECTION, SESSION_EXPIRATION_MS, SESSION_FIELDS, } from "../schemas";
|
|
7
7
|
export class SessionRepository extends BaseRepository {
|
|
@@ -273,8 +273,8 @@ SessionRepository.SIEVE_FIELDS = {
|
|
|
273
273
|
canFilter: true,
|
|
274
274
|
canSort: false,
|
|
275
275
|
},
|
|
276
|
-
lastActivity: { canFilter: true, canSort: true },
|
|
277
|
-
expiresAt: { canFilter: true, canSort: true },
|
|
278
|
-
createdAt: { canFilter: true, canSort: true },
|
|
276
|
+
lastActivity: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
277
|
+
expiresAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
278
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
279
279
|
};
|
|
280
280
|
export const sessionRepository = new SessionRepository();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
|
-
import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
3
|
+
import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
4
4
|
import { USER_PII_FIELDS, decryptPayoutDetails, decryptPiiFields, decryptShippingConfig, encryptPayoutDetails, encryptPiiFields, encryptShippingConfig, piiBlindIndex, } from "../../../security";
|
|
5
5
|
import { USER_COLLECTION, USER_FIELDS, createUserId, } from "../schemas";
|
|
6
6
|
export class UserRepository extends BaseRepository {
|
|
@@ -257,7 +257,7 @@ UserRepository.SIEVE_FIELDS = {
|
|
|
257
257
|
emailVerified: { canFilter: true, canSort: false },
|
|
258
258
|
disabled: { canFilter: true, canSort: true },
|
|
259
259
|
storeStatus: { canFilter: true, canSort: false },
|
|
260
|
-
createdAt: { canFilter: true, canSort: true },
|
|
261
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
260
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
261
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
262
262
|
};
|
|
263
263
|
export const userRepository = new UserRepository();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
|
|
2
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
2
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
3
3
|
import { type BlogPostCreateInput, type BlogPostDocument, type BlogPostUpdateInput } from "../schemas";
|
|
4
4
|
import type { BlogPostCategory } from "../types";
|
|
5
5
|
declare class BlogRepository extends BaseRepository<BlogPostDocument> {
|
|
@@ -61,10 +61,12 @@ declare class BlogRepository extends BaseRepository<BlogPostDocument> {
|
|
|
61
61
|
publishedAt: {
|
|
62
62
|
canFilter: boolean;
|
|
63
63
|
canSort: boolean;
|
|
64
|
+
parseValue: typeof parseSieveDateValue;
|
|
64
65
|
};
|
|
65
66
|
updatedAt: {
|
|
66
67
|
canFilter: boolean;
|
|
67
68
|
canSort: boolean;
|
|
69
|
+
parseValue: typeof parseSieveDateValue;
|
|
68
70
|
};
|
|
69
71
|
tags: {
|
|
70
72
|
canFilter: boolean;
|
|
@@ -73,6 +75,7 @@ declare class BlogRepository extends BaseRepository<BlogPostDocument> {
|
|
|
73
75
|
createdAt: {
|
|
74
76
|
canFilter: boolean;
|
|
75
77
|
canSort: boolean;
|
|
78
|
+
parseValue: typeof parseSieveDateValue;
|
|
76
79
|
};
|
|
77
80
|
};
|
|
78
81
|
listPublished(opts: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DatabaseError } from "../../../errors";
|
|
2
2
|
import { normalizeError } from "../../../errors/normalize";
|
|
3
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
3
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
4
4
|
import { BLOG_POST_FIELDS, BLOG_POSTS_COLLECTION, } from "../schemas";
|
|
5
5
|
import { createBlogPostId } from "../schemas";
|
|
6
6
|
import { increment } from "../../../contracts/field-ops";
|
|
@@ -171,10 +171,10 @@ BlogRepository.SIEVE_FIELDS = {
|
|
|
171
171
|
isFeatured: { canFilter: true, canSort: false },
|
|
172
172
|
readTimeMinutes: { canFilter: true, canSort: true },
|
|
173
173
|
views: { canFilter: true, canSort: true },
|
|
174
|
-
publishedAt: { canFilter: true, canSort: true },
|
|
175
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
174
|
+
publishedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
175
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
176
176
|
tags: { canFilter: true, canSort: false },
|
|
177
|
-
createdAt: { canFilter: true, canSort: true },
|
|
177
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
178
178
|
};
|
|
179
179
|
const blogRepository = new BlogRepository();
|
|
180
180
|
export { BlogRepository, blogRepository };
|
|
@@ -4,7 +4,7 @@ import { CART_FIELDS } from "../../../constants/field-names";
|
|
|
4
4
|
import { normalizeError } from "../../../errors/normalize";
|
|
5
5
|
const ERR_CART_ITEM_NOT_FOUND = "Cart item not found";
|
|
6
6
|
const ERR_CART_ITEM_LOCKED = "This item requires payment and cannot be removed or modified.";
|
|
7
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
8
8
|
import { decryptPii } from "../../../security";
|
|
9
9
|
import { CART_COLLECTION, } from "../schemas";
|
|
10
10
|
export class CartRepository extends BaseRepository {
|
|
@@ -291,7 +291,7 @@ export class CartRepository extends BaseRepository {
|
|
|
291
291
|
CartRepository.SIEVE_FIELDS = {
|
|
292
292
|
userId: { canFilter: true, canSort: false },
|
|
293
293
|
sessionId: { canFilter: true, canSort: false },
|
|
294
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
295
|
-
createdAt: { canFilter: true, canSort: true },
|
|
294
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
295
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
296
296
|
};
|
|
297
297
|
export const cartRepository = new CartRepository();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Personal Catalogue Repository (Feature B).
|
|
3
3
|
*/
|
|
4
|
-
import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
5
5
|
import { type CatalogueItemDocument, type CatalogueItemCreateInput, type CatalogueItemUpdateInput } from "../schemas/firestore";
|
|
6
6
|
export declare class CatalogueRepository extends BaseRepository<CatalogueItemDocument> {
|
|
7
7
|
constructor();
|
|
@@ -31,6 +31,7 @@ export declare class CatalogueRepository extends BaseRepository<CatalogueItemDoc
|
|
|
31
31
|
createdAt: {
|
|
32
32
|
canFilter: boolean;
|
|
33
33
|
canSort: boolean;
|
|
34
|
+
parseValue: typeof parseSieveDateValue;
|
|
34
35
|
};
|
|
35
36
|
};
|
|
36
37
|
/** Bounded scan for the staleness-reminder Function — images older than N days, not yet reminded for this update. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Personal Catalogue Repository (Feature B).
|
|
3
3
|
*/
|
|
4
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { CATALOGUE_COLLECTION, CATALOGUE_FIELDS, createCatalogueItemId, } from "../schemas/firestore";
|
|
6
6
|
export class CatalogueRepository extends BaseRepository {
|
|
7
7
|
constructor() {
|
|
@@ -63,6 +63,6 @@ CatalogueRepository.SIEVE_FIELDS = {
|
|
|
63
63
|
ownerId: { canFilter: true, canSort: false },
|
|
64
64
|
visibility: { canFilter: true, canSort: false },
|
|
65
65
|
listingStatus: { canFilter: true, canSort: false },
|
|
66
|
-
createdAt: { canFilter: true, canSort: true },
|
|
66
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
67
67
|
};
|
|
68
68
|
export const catalogueRepository = new CatalogueRepository();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { increment, arrayUnion, arrayRemove, } from "../../../contracts/field-ops";
|
|
3
3
|
import { DatabaseError } from "../../../errors";
|
|
4
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
6
6
|
import { CATEGORY_FIELDS, CATEGORIES_COLLECTION, MIN_ITEMS_FOR_FEATURED, buildCategoryTree, calculateCategoryFields, canBeFeatured, createCategoryId, isValidCategoryMove, } from "../schemas";
|
|
7
7
|
export class CategoriesRepository extends BaseRepository {
|
|
@@ -523,6 +523,6 @@ CategoriesRepository.SIEVE_FIELDS = {
|
|
|
523
523
|
},
|
|
524
524
|
id: { canFilter: true, canSort: false },
|
|
525
525
|
isLeaf: { canFilter: true, canSort: false },
|
|
526
|
-
createdAt: { canFilter: true, canSort: true },
|
|
526
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
527
527
|
};
|
|
528
528
|
export const categoriesRepository = new CategoriesRepository();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
3
|
import { serverLogger } from "../../../monitoring";
|
|
4
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { decryptPiiFields, encryptPiiFields, EVENT_ENTRY_PII_FIELDS, } from "../../../security";
|
|
6
6
|
import { EVENT_ENTRIES_COLLECTION, EVENT_ENTRY_FIELDS, } from "../schemas";
|
|
7
7
|
/** Extracted from getExportReport's poll branch to stay under the max nesting depth. */
|
|
@@ -304,7 +304,7 @@ EventEntryRepository.SIEVE_FIELDS = {
|
|
|
304
304
|
userDisplayName: { canFilter: false, canSort: false },
|
|
305
305
|
reviewStatus: { canFilter: true, canSort: false },
|
|
306
306
|
status: { canFilter: true, canSort: false },
|
|
307
|
-
submittedAt: { canFilter: true, canSort: true },
|
|
307
|
+
submittedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
308
308
|
points: { canFilter: true, canSort: true },
|
|
309
309
|
};
|
|
310
310
|
const eventEntryRepository = new EventEntryRepository();
|
|
@@ -2,7 +2,7 @@ import { DatabaseError } from "../../../errors";
|
|
|
2
2
|
import { serverLogger } from "../../../monitoring";
|
|
3
3
|
import { slugify } from "../../../utils";
|
|
4
4
|
import { normalizeError } from "../../../errors/normalize";
|
|
5
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
5
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
6
6
|
import { EVENT_FIELDS, EVENTS_COLLECTION, } from "../schemas";
|
|
7
7
|
import { increment } from "../../../contracts/field-ops";
|
|
8
8
|
class EventRepository extends BaseRepository {
|
|
@@ -143,8 +143,8 @@ EventRepository.SIEVE_FIELDS = {
|
|
|
143
143
|
status: { canFilter: true, canSort: true },
|
|
144
144
|
title: { canFilter: true, canSort: true },
|
|
145
145
|
createdBy: { canFilter: true, canSort: false },
|
|
146
|
-
startsAt: { canFilter: true, canSort: true },
|
|
147
|
-
endsAt: { canFilter: true, canSort: true },
|
|
146
|
+
startsAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
147
|
+
endsAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
148
148
|
"stats.totalEntries": {
|
|
149
149
|
path: "stats.totalEntries",
|
|
150
150
|
canFilter: true,
|
|
@@ -161,7 +161,7 @@ EventRepository.SIEVE_FIELDS = {
|
|
|
161
161
|
canSort: true,
|
|
162
162
|
},
|
|
163
163
|
id: { canFilter: true, canSort: false },
|
|
164
|
-
createdAt: { canFilter: true, canSort: true },
|
|
164
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
165
165
|
};
|
|
166
166
|
const eventRepository = new EventRepository();
|
|
167
167
|
export { EventRepository, eventRepository };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
1
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
3
|
import { increment } from "../../../contracts/field-ops";
|
|
4
4
|
import { FAQ_FIELDS } from "../../../constants/field-names";
|
|
@@ -295,6 +295,6 @@ FirebaseFAQsRepository.SIEVE_FIELDS = {
|
|
|
295
295
|
tags: { canFilter: true, canSort: false },
|
|
296
296
|
searchTokens: { canFilter: true, canSort: false },
|
|
297
297
|
"stats.helpful": { canFilter: false, canSort: true },
|
|
298
|
-
createdAt: { canFilter: true, canSort: true },
|
|
298
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
299
299
|
};
|
|
300
300
|
export const faqsRepository = new FirebaseFAQsRepository();
|
|
@@ -4,7 +4,7 @@ import { normalizeError } from "../../../errors/normalize";
|
|
|
4
4
|
*
|
|
5
5
|
* Manages homepage carousel slides with grid card system
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, getFirestoreCount, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, getFirestoreCount, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
8
8
|
import { increment } from "../../../contracts/field-ops";
|
|
9
9
|
import { CAROUSEL_SLIDES_COLLECTION, MAX_ACTIVE_SLIDES, DEFAULT_CAROUSEL_SLIDE_DATA, canActivateSlide, createCarouselId, } from "../schemas";
|
|
10
10
|
import { DatabaseError } from "../../../errors";
|
|
@@ -250,8 +250,8 @@ CarouselRepository.SIEVE_FIELDS = {
|
|
|
250
250
|
active: { canFilter: true, canSort: false },
|
|
251
251
|
order: { canFilter: true, canSort: true },
|
|
252
252
|
createdBy: { canFilter: true, canSort: false },
|
|
253
|
-
createdAt: { canFilter: true, canSort: true },
|
|
254
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
253
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
254
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
255
255
|
};
|
|
256
256
|
// Export singleton instance
|
|
257
257
|
export const carouselRepository = new CarouselRepository();
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { DatabaseError } from "../../../errors";
|
|
7
7
|
import { HOMEPAGE_SECTION_FIELDS } from "../../../constants/field-names";
|
|
8
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
8
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
9
9
|
import { normalizeError } from "../../../errors/normalize";
|
|
10
10
|
import { HOMEPAGE_SECTIONS_COLLECTION, createHomepageSectionId, } from "../schemas";
|
|
11
11
|
export class HomepageSectionsRepository extends BaseRepository {
|
|
@@ -180,7 +180,7 @@ HomepageSectionsRepository.SIEVE_FIELDS = {
|
|
|
180
180
|
type: { canFilter: true, canSort: false },
|
|
181
181
|
enabled: { canFilter: true, canSort: false },
|
|
182
182
|
order: { canFilter: true, canSort: true },
|
|
183
|
-
createdAt: { canFilter: true, canSort: true },
|
|
184
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
183
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
184
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
185
185
|
};
|
|
186
186
|
export const homepageSectionsRepository = new HomepageSectionsRepository();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
3
|
import { serverLogger } from "../../../monitoring";
|
|
4
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { decryptPiiFields, encryptPiiFields, LOTTERY_ENTRY_PII_FIELDS, } from "../../../security";
|
|
6
6
|
import { LOTTERY_ENTRIES_COLLECTION, LOTTERY_ENTRY_FIELDS, } from "../schemas/firestore";
|
|
7
7
|
class LotteryEntryRepository extends BaseRepository {
|
|
@@ -123,7 +123,7 @@ LotteryEntryRepository.SIEVE_FIELDS = {
|
|
|
123
123
|
productId: { canFilter: true, canSort: false },
|
|
124
124
|
userId: { canFilter: true, canSort: false },
|
|
125
125
|
status: { canFilter: true, canSort: false },
|
|
126
|
-
submittedAt: { canFilter: true, canSort: true },
|
|
126
|
+
submittedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
127
127
|
userLotteryNumber: { canFilter: false, canSort: true },
|
|
128
128
|
};
|
|
129
129
|
export { LotteryEntryRepository };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
|
|
2
2
|
import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
|
|
3
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
3
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
4
4
|
import { type OrderCreateInput, type OrderDocument, type OrderRefundEvent } from "../schemas";
|
|
5
5
|
import type { OrderStatus, PaymentStatus } from "../types";
|
|
6
6
|
declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
@@ -89,10 +89,12 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
|
89
89
|
orderDate: {
|
|
90
90
|
canFilter: boolean;
|
|
91
91
|
canSort: boolean;
|
|
92
|
+
parseValue: typeof parseSieveDateValue;
|
|
92
93
|
};
|
|
93
94
|
createdAt: {
|
|
94
95
|
canFilter: boolean;
|
|
95
96
|
canSort: boolean;
|
|
97
|
+
parseValue: typeof parseSieveDateValue;
|
|
96
98
|
};
|
|
97
99
|
contestable: {
|
|
98
100
|
canFilter: boolean;
|
|
@@ -157,10 +159,12 @@ declare class OrderRepository extends BaseRepository<OrderDocument> {
|
|
|
157
159
|
orderDate: {
|
|
158
160
|
canFilter: boolean;
|
|
159
161
|
canSort: boolean;
|
|
162
|
+
parseValue: typeof parseSieveDateValue;
|
|
160
163
|
};
|
|
161
164
|
createdAt: {
|
|
162
165
|
canFilter: boolean;
|
|
163
166
|
canSort: boolean;
|
|
167
|
+
parseValue: typeof parseSieveDateValue;
|
|
164
168
|
};
|
|
165
169
|
paymentBatchId: {
|
|
166
170
|
canFilter: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { DatabaseError, NotFoundError } from "../../../errors";
|
|
3
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
3
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
4
4
|
import { decryptPiiFields, encryptPiiFields, ORDER_PII_FIELDS, } from "../../../security";
|
|
5
5
|
import { serverTimestamp } from "../../../contracts/field-ops";
|
|
6
6
|
import { createOrderId, ORDER_COLLECTION, OrderStatusValues, } from "../schemas";
|
|
@@ -464,8 +464,8 @@ OrderRepository.SELLER_SIEVE_FIELDS = {
|
|
|
464
464
|
paymentStatus: { canFilter: true, canSort: true },
|
|
465
465
|
paymentMethod: { canFilter: true, canSort: true },
|
|
466
466
|
totalPrice: { canFilter: true, canSort: true },
|
|
467
|
-
orderDate: { canFilter: true, canSort: true },
|
|
468
|
-
createdAt: { canFilter: true, canSort: true },
|
|
467
|
+
orderDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
468
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
469
469
|
contestable: { canFilter: true, canSort: false },
|
|
470
470
|
};
|
|
471
471
|
OrderRepository.ADMIN_SIEVE_FIELDS = {
|
|
@@ -482,8 +482,8 @@ OrderRepository.ADMIN_SIEVE_FIELDS = {
|
|
|
482
482
|
shippingMethod: { canFilter: true, canSort: true },
|
|
483
483
|
payoutStatus: { canFilter: true, canSort: false },
|
|
484
484
|
totalPrice: { canFilter: true, canSort: true },
|
|
485
|
-
orderDate: { canFilter: true, canSort: true },
|
|
486
|
-
createdAt: { canFilter: true, canSort: true },
|
|
485
|
+
orderDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
486
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
487
487
|
// S-SBUNI-RULES 2026-05-13 — refund + batch fields
|
|
488
488
|
paymentBatchId: { canFilter: true, canSort: false },
|
|
489
489
|
contestable: { canFilter: true, canSort: false },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Data access layer for payout documents in Firestore.
|
|
5
5
|
* Handles seller payout requests and admin payout processing.
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
8
8
|
import type { DocumentReference, WriteResult } from "firebase-admin/firestore";
|
|
9
9
|
import type { PayoutDocument, PayoutCreateInput, PayoutRefundDeduction, PayoutStatus, PayoutUpdateInput } from "../schemas";
|
|
10
10
|
export declare class PayoutRepository extends BaseRepository<PayoutDocument> {
|
|
@@ -109,14 +109,17 @@ export declare class PayoutRepository extends BaseRepository<PayoutDocument> {
|
|
|
109
109
|
requestedAt: {
|
|
110
110
|
canFilter: boolean;
|
|
111
111
|
canSort: boolean;
|
|
112
|
+
parseValue: typeof parseSieveDateValue;
|
|
112
113
|
};
|
|
113
114
|
createdAt: {
|
|
114
115
|
canFilter: boolean;
|
|
115
116
|
canSort: boolean;
|
|
117
|
+
parseValue: typeof parseSieveDateValue;
|
|
116
118
|
};
|
|
117
119
|
processedAt: {
|
|
118
120
|
canFilter: boolean;
|
|
119
121
|
canSort: boolean;
|
|
122
|
+
parseValue: typeof parseSieveDateValue;
|
|
120
123
|
};
|
|
121
124
|
};
|
|
122
125
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Data access layer for payout documents in Firestore.
|
|
5
5
|
* Handles seller payout requests and admin payout processing.
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
8
8
|
import { PAYOUT_COLLECTION, PAYOUT_FIELDS, createPayoutId } from "../schemas";
|
|
9
9
|
import { encryptPiiFields, decryptPiiFields, addPiiIndices, PAYOUT_PII_FIELDS, PAYOUT_PII_INDEX_MAP, encryptPayoutBankAccount, decryptPayoutBankAccount, } from "../../../security";
|
|
10
10
|
export class PayoutRepository extends BaseRepository {
|
|
@@ -241,9 +241,9 @@ PayoutRepository.SIEVE_FIELDS = {
|
|
|
241
241
|
paymentMethod: { canFilter: true, canSort: false },
|
|
242
242
|
amount: { canFilter: true, canSort: true },
|
|
243
243
|
netAmount: { canFilter: true, canSort: true },
|
|
244
|
-
requestedAt: { canFilter: true, canSort: true },
|
|
245
|
-
createdAt: { canFilter: true, canSort: true },
|
|
246
|
-
processedAt: { canFilter: true, canSort: true },
|
|
244
|
+
requestedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
245
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
246
|
+
processedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
247
247
|
};
|
|
248
248
|
// Export singleton instance
|
|
249
249
|
export const payoutRepository = new PayoutRepository();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DocumentReference, WriteBatch } from "firebase-admin/firestore";
|
|
2
2
|
import type { DocumentSnapshot } from "../../../providers/db-firebase";
|
|
3
|
-
import { BaseRepository, type FirebaseSieveResult, type SieveFilterAliases, type SieveModel } from "../../../providers/db-firebase";
|
|
3
|
+
import { BaseRepository, parseSieveDateValue, type FirebaseSieveResult, type SieveFilterAliases, type SieveModel } from "../../../providers/db-firebase";
|
|
4
4
|
import { type ProductCreateInput, type ProductDocument, type ProductUpdateInput } from "../schemas";
|
|
5
5
|
import type { ProductStatus } from "../types";
|
|
6
6
|
export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
@@ -140,22 +140,27 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
140
140
|
createdAt: {
|
|
141
141
|
canFilter: boolean;
|
|
142
142
|
canSort: boolean;
|
|
143
|
+
parseValue: typeof parseSieveDateValue;
|
|
143
144
|
};
|
|
144
145
|
updatedAt: {
|
|
145
146
|
canFilter: boolean;
|
|
146
147
|
canSort: boolean;
|
|
148
|
+
parseValue: typeof parseSieveDateValue;
|
|
147
149
|
};
|
|
148
150
|
auctionEndDate: {
|
|
149
151
|
canFilter: boolean;
|
|
150
152
|
canSort: boolean;
|
|
153
|
+
parseValue: typeof parseSieveDateValue;
|
|
151
154
|
};
|
|
152
155
|
prizeRevealWindowStart: {
|
|
153
156
|
canFilter: boolean;
|
|
154
157
|
canSort: boolean;
|
|
158
|
+
parseValue: typeof parseSieveDateValue;
|
|
155
159
|
};
|
|
156
160
|
prizeRevealWindowEnd: {
|
|
157
161
|
canFilter: boolean;
|
|
158
162
|
canSort: boolean;
|
|
163
|
+
parseValue: typeof parseSieveDateValue;
|
|
159
164
|
};
|
|
160
165
|
prizeRevealStatus: {
|
|
161
166
|
canFilter: boolean;
|
|
@@ -184,6 +189,7 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
184
189
|
preOrderDeliveryDate: {
|
|
185
190
|
canFilter: boolean;
|
|
186
191
|
canSort: boolean;
|
|
192
|
+
parseValue: typeof parseSieveDateValue;
|
|
187
193
|
};
|
|
188
194
|
preOrderDepositPercent: {
|
|
189
195
|
canFilter: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize";
|
|
2
2
|
import { increment, serverTimestamp } from "../../../contracts/field-ops";
|
|
3
3
|
import { DatabaseError } from "../../../errors";
|
|
4
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { cacheManager } from "../../../core";
|
|
6
6
|
import { generateUniqueId, slugify, buildSearchTokens, tokenizeQuery, generateBarcodeId } from "../../../utils";
|
|
7
7
|
import { PRODUCT_COLLECTION, ProductStatusValues } from "../schemas";
|
|
@@ -500,18 +500,18 @@ ProductRepository.SIEVE_FIELDS = {
|
|
|
500
500
|
bidCount: { canFilter: true, canSort: true },
|
|
501
501
|
isSold: { canFilter: true, canSort: true },
|
|
502
502
|
isPartOfBundle: { canFilter: true, canSort: false },
|
|
503
|
-
createdAt: { canFilter: true, canSort: true },
|
|
504
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
505
|
-
auctionEndDate: { canFilter: true, canSort: true },
|
|
506
|
-
prizeRevealWindowStart: { canFilter: true, canSort: true },
|
|
507
|
-
prizeRevealWindowEnd: { canFilter: true, canSort: true },
|
|
503
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
504
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
505
|
+
auctionEndDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
506
|
+
prizeRevealWindowStart: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
507
|
+
prizeRevealWindowEnd: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
508
508
|
prizeRevealStatus: { canFilter: true, canSort: false },
|
|
509
509
|
startingBid: { canFilter: true, canSort: true },
|
|
510
510
|
buyNowPrice: { canFilter: true, canSort: true },
|
|
511
511
|
minBidIncrement: { canFilter: true, canSort: false },
|
|
512
512
|
autoExtendable: { canFilter: true, canSort: false },
|
|
513
513
|
reservePrice: { canFilter: true, canSort: true },
|
|
514
|
-
preOrderDeliveryDate: { canFilter: true, canSort: true },
|
|
514
|
+
preOrderDeliveryDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
515
515
|
preOrderDepositPercent: { canFilter: true, canSort: false },
|
|
516
516
|
preOrderDepositAmount: { canFilter: true, canSort: true },
|
|
517
517
|
preOrderMaxQuantity: { canFilter: true, canSort: true },
|
|
@@ -4,7 +4,7 @@ import type { ListingType } from "../../products/types";
|
|
|
4
4
|
*
|
|
5
5
|
* Manages coupon creation, validation, and usage tracking
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
8
8
|
import type { SieveModel, FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
9
9
|
import { CouponDocument, CouponUsageDocument, CouponCreateInput } from "../schemas";
|
|
10
10
|
import type { CouponType } from "../types";
|
|
@@ -179,11 +179,13 @@ export declare class CouponsRepository extends BaseRepository<CouponDocument> {
|
|
|
179
179
|
path: string;
|
|
180
180
|
canFilter: boolean;
|
|
181
181
|
canSort: boolean;
|
|
182
|
+
parseValue: typeof parseSieveDateValue;
|
|
182
183
|
};
|
|
183
184
|
"validity.startDate": {
|
|
184
185
|
path: string;
|
|
185
186
|
canFilter: boolean;
|
|
186
187
|
canSort: boolean;
|
|
188
|
+
parseValue: typeof parseSieveDateValue;
|
|
187
189
|
};
|
|
188
190
|
"discount.value": {
|
|
189
191
|
path: string;
|
|
@@ -203,6 +205,7 @@ export declare class CouponsRepository extends BaseRepository<CouponDocument> {
|
|
|
203
205
|
createdAt: {
|
|
204
206
|
canFilter: boolean;
|
|
205
207
|
canSort: boolean;
|
|
208
|
+
parseValue: typeof parseSieveDateValue;
|
|
206
209
|
};
|
|
207
210
|
storeId: {
|
|
208
211
|
canFilter: boolean;
|
|
@@ -5,7 +5,7 @@ import { serverLogger } from "../../../monitoring/server-logger";
|
|
|
5
5
|
*
|
|
6
6
|
* Manages coupon creation, validation, and usage tracking
|
|
7
7
|
*/
|
|
8
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
8
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
9
9
|
import { COUPONS_COLLECTION, COUPON_FIELDS, isValidCouponCode, isCouponValid, canUserUseCoupon, calculateDiscount, createCouponId, } from "../schemas";
|
|
10
10
|
import { USER_COLLECTION } from "../../auth/schemas";
|
|
11
11
|
import { DatabaseError } from "../../../errors";
|
|
@@ -500,11 +500,13 @@ CouponsRepository.SIEVE_FIELDS = {
|
|
|
500
500
|
path: "validity.endDate",
|
|
501
501
|
canFilter: true,
|
|
502
502
|
canSort: true,
|
|
503
|
+
parseValue: parseSieveDateValue,
|
|
503
504
|
},
|
|
504
505
|
"validity.startDate": {
|
|
505
506
|
path: "validity.startDate",
|
|
506
507
|
canFilter: true,
|
|
507
508
|
canSort: true,
|
|
509
|
+
parseValue: parseSieveDateValue,
|
|
508
510
|
},
|
|
509
511
|
"discount.value": {
|
|
510
512
|
path: "discount.value",
|
|
@@ -521,7 +523,7 @@ CouponsRepository.SIEVE_FIELDS = {
|
|
|
521
523
|
canFilter: true,
|
|
522
524
|
canSort: true,
|
|
523
525
|
},
|
|
524
|
-
createdAt: { canFilter: true, canSort: true },
|
|
526
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
525
527
|
storeId: { canFilter: true, canSort: false },
|
|
526
528
|
scope: { canFilter: true, canSort: false },
|
|
527
529
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FirebaseSieveResult, SieveModel } from "../../../providers/db-firebase";
|
|
2
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
2
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
3
3
|
import { type ReviewCreateInput, type ReviewDocument } from "../schemas";
|
|
4
4
|
import type { ReviewStatus } from "../types";
|
|
5
5
|
export interface ReviewRatingAggregate {
|
|
@@ -107,10 +107,12 @@ declare class ReviewRepository extends BaseRepository<ReviewDocument> {
|
|
|
107
107
|
updatedAt: {
|
|
108
108
|
canFilter: boolean;
|
|
109
109
|
canSort: boolean;
|
|
110
|
+
parseValue: typeof parseSieveDateValue;
|
|
110
111
|
};
|
|
111
112
|
createdAt: {
|
|
112
113
|
canFilter: boolean;
|
|
113
114
|
canSort: boolean;
|
|
115
|
+
parseValue: typeof parseSieveDateValue;
|
|
114
116
|
};
|
|
115
117
|
};
|
|
116
118
|
listForProduct(productId: string, model: SieveModel): Promise<FirebaseSieveResult<ReviewDocument>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
1
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
2
2
|
import { addPiiIndices, decryptPiiFields, encryptPiiFields, REVIEW_PII_FIELDS, REVIEW_PII_INDEX_MAP, } from "../../../security";
|
|
3
3
|
import { createReviewId, REVIEW_COLLECTION, } from "../schemas";
|
|
4
4
|
const REVIEW_FIELDS = {
|
|
@@ -262,8 +262,8 @@ ReviewRepository.SIEVE_FIELDS = {
|
|
|
262
262
|
helpfulCount: { canFilter: true, canSort: true },
|
|
263
263
|
featured: { canFilter: true, canSort: false },
|
|
264
264
|
reportCount: { canFilter: true, canSort: true },
|
|
265
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
266
|
-
createdAt: { canFilter: true, canSort: true },
|
|
265
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
266
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
267
267
|
};
|
|
268
268
|
const reviewRepository = new ReviewRepository();
|
|
269
269
|
export { ReviewRepository, reviewRepository };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseRepository, type FirebaseSieveResult, type SieveModel } from "../../../providers/db-firebase";
|
|
1
|
+
import { BaseRepository, parseSieveDateValue, type FirebaseSieveResult, type SieveModel } from "../../../providers/db-firebase";
|
|
2
2
|
import { type ScammerDocument, type ScammerIncidentDocument, type ScammerCommentDocument, type ScammerCreateInput, type ScammerAdminUpdateInput } from "../schemas/firestore";
|
|
3
3
|
declare class ScammerRepository extends BaseRepository<ScammerDocument> {
|
|
4
4
|
constructor();
|
|
@@ -42,6 +42,7 @@ declare class ScammerRepository extends BaseRepository<ScammerDocument> {
|
|
|
42
42
|
createdAt: {
|
|
43
43
|
canFilter: boolean;
|
|
44
44
|
canSort: boolean;
|
|
45
|
+
parseValue: typeof parseSieveDateValue;
|
|
45
46
|
};
|
|
46
47
|
updatedAt: {
|
|
47
48
|
canFilter: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DatabaseError } from "../../../errors";
|
|
2
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
2
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { serverLogger } from "../../../monitoring/server-logger";
|
|
5
5
|
import { increment } from "../../../contracts/field-ops";
|
|
@@ -190,7 +190,7 @@ ScammerRepository.SIEVE_FIELDS = {
|
|
|
190
190
|
isContested: { canFilter: true, canSort: false },
|
|
191
191
|
views: { canFilter: false, canSort: true },
|
|
192
192
|
incidentCount: { canFilter: false, canSort: true },
|
|
193
|
-
createdAt: { canFilter: true, canSort: true },
|
|
193
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
194
194
|
updatedAt: { canFilter: false, canSort: true },
|
|
195
195
|
};
|
|
196
196
|
const scammerRepository = new ScammerRepository();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseRepository } from "../../../providers/db-firebase";
|
|
2
|
-
import { applySieveToFirestore } from "../../../providers/db-firebase";
|
|
2
|
+
import { applySieveToFirestore, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
3
3
|
import { SERVER_ERRORS_COLLECTION, SERVER_ERROR_STACK_MAX_BYTES, } from "../schemas/firestore";
|
|
4
4
|
import { normalizeError } from "../../../errors/normalize";
|
|
5
5
|
/** Truncate a stack to the documented byte budget — never persist > 4 KB. */
|
|
@@ -63,7 +63,7 @@ export class ServerErrorsRepository extends BaseRepository {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
ServerErrorsRepository.SIEVE_FIELDS = {
|
|
66
|
-
occurredAt: { canFilter: true, canSort: true },
|
|
66
|
+
occurredAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
67
67
|
source: { canFilter: true, canSort: false },
|
|
68
68
|
route: { canFilter: true, canSort: false },
|
|
69
69
|
method: { canFilter: true, canSort: false },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* exactly Firestore's native batched-write limit, so bulkCreate() writes an
|
|
5
5
|
* entire import in a single WriteBatch with no chunking logic.
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
8
8
|
import { type ShipmentItem, type ShipmentItemCreateInput, type ShipmentItemUpdateInput } from "../schemas/firestore";
|
|
9
9
|
export declare class ShipmentItemsRepository extends BaseRepository<ShipmentItem> {
|
|
10
10
|
constructor();
|
|
@@ -50,6 +50,7 @@ export declare class ShipmentItemsRepository extends BaseRepository<ShipmentItem
|
|
|
50
50
|
createdAt: {
|
|
51
51
|
canFilter: boolean;
|
|
52
52
|
canSort: boolean;
|
|
53
|
+
parseValue: typeof parseSieveDateValue;
|
|
53
54
|
};
|
|
54
55
|
};
|
|
55
56
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* exactly Firestore's native batched-write limit, so bulkCreate() writes an
|
|
5
5
|
* entire import in a single WriteBatch with no chunking logic.
|
|
6
6
|
*/
|
|
7
|
-
import { BaseRepository, } from "../../../providers/db-firebase";
|
|
7
|
+
import { BaseRepository, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
8
8
|
import { SHIPMENT_ITEM_COLLECTION, SHIPMENT_ITEM_FIELDS, MAX_ITEMS_PER_LOT, } from "../schemas/firestore";
|
|
9
9
|
import { ValidationError } from "../../../errors";
|
|
10
10
|
import { sieveAnd, sieveFilter, SIEVE_OP } from "../../../utils/sieve-builder";
|
|
@@ -73,6 +73,6 @@ ShipmentItemsRepository.SIEVE_FIELDS = {
|
|
|
73
73
|
title: { canFilter: true, canSort: true },
|
|
74
74
|
price: { canFilter: true, canSort: true },
|
|
75
75
|
linkedProductId: { canFilter: true, canSort: false },
|
|
76
|
-
createdAt: { canFilter: true, canSort: true },
|
|
76
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
77
77
|
};
|
|
78
78
|
export const shipmentItemsRepository = new ShipmentItemsRepository();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shipment Lots Repository — one doc per lot, FK'd to a shipment.
|
|
3
3
|
*/
|
|
4
|
-
import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
5
5
|
import { type ShipmentLot, type ShipmentLotCreateInput, type ShipmentLotUpdateInput } from "../schemas/firestore";
|
|
6
6
|
export declare class ShipmentLotsRepository extends BaseRepository<ShipmentLot> {
|
|
7
7
|
constructor();
|
|
@@ -42,6 +42,7 @@ export declare class ShipmentLotsRepository extends BaseRepository<ShipmentLot>
|
|
|
42
42
|
createdAt: {
|
|
43
43
|
canFilter: boolean;
|
|
44
44
|
canSort: boolean;
|
|
45
|
+
parseValue: typeof parseSieveDateValue;
|
|
45
46
|
};
|
|
46
47
|
};
|
|
47
48
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shipment Lots Repository — one doc per lot, FK'd to a shipment.
|
|
3
3
|
*/
|
|
4
|
-
import { BaseRepository, } from "../../../providers/db-firebase";
|
|
4
|
+
import { BaseRepository, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
5
5
|
import { SHIPMENT_LOT_COLLECTION, SHIPMENT_LOT_FIELDS, } from "../schemas/firestore";
|
|
6
6
|
export class ShipmentLotsRepository extends BaseRepository {
|
|
7
7
|
constructor() {
|
|
@@ -50,6 +50,6 @@ ShipmentLotsRepository.SIEVE_FIELDS = {
|
|
|
50
50
|
lotName: { canFilter: true, canSort: true },
|
|
51
51
|
projectedProfit: { canFilter: true, canSort: true },
|
|
52
52
|
projectedRevenue: { canFilter: true, canSort: true },
|
|
53
|
-
createdAt: { canFilter: true, canSort: true },
|
|
53
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
54
54
|
};
|
|
55
55
|
export const shipmentLotsRepository = new ShipmentLotsRepository();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Procurement Shipments Repository — header document only.
|
|
3
3
|
* Lots/items live in ShipmentLotsRepository / ShipmentItemsRepository.
|
|
4
4
|
*/
|
|
5
|
-
import { BaseRepository, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
5
|
+
import { BaseRepository, parseSieveDateValue, type SieveModel, type FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
6
6
|
import { type ShipmentDocument, type ShipmentCreateInput, type ShipmentUpdateInput } from "../schemas/firestore";
|
|
7
7
|
export declare class ShipmentsRepository extends BaseRepository<ShipmentDocument> {
|
|
8
8
|
constructor();
|
|
@@ -39,10 +39,12 @@ export declare class ShipmentsRepository extends BaseRepository<ShipmentDocument
|
|
|
39
39
|
etaDate: {
|
|
40
40
|
canFilter: boolean;
|
|
41
41
|
canSort: boolean;
|
|
42
|
+
parseValue: typeof parseSieveDateValue;
|
|
42
43
|
};
|
|
43
44
|
createdAt: {
|
|
44
45
|
canFilter: boolean;
|
|
45
46
|
canSort: boolean;
|
|
47
|
+
parseValue: typeof parseSieveDateValue;
|
|
46
48
|
};
|
|
47
49
|
};
|
|
48
50
|
list(model: SieveModel): Promise<FirebaseSieveResult<ShipmentDocument>>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Procurement Shipments Repository — header document only.
|
|
3
3
|
* Lots/items live in ShipmentLotsRepository / ShipmentItemsRepository.
|
|
4
4
|
*/
|
|
5
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
5
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
6
6
|
import { ConflictError } from "../../../errors";
|
|
7
7
|
import { SHIPMENT_COLLECTION, SHIPMENT_FIELDS, SHIPMENT_ITEM_COLLECTION, DEFAULT_SHIPMENT_TOTALS, createShipmentId, } from "../schemas/firestore";
|
|
8
8
|
export class ShipmentsRepository extends BaseRepository {
|
|
@@ -71,7 +71,7 @@ ShipmentsRepository.SIEVE_FIELDS = {
|
|
|
71
71
|
shipmentNumber: { canFilter: true, canSort: true },
|
|
72
72
|
supplierName: { canFilter: true, canSort: true },
|
|
73
73
|
status: { canFilter: true, canSort: true },
|
|
74
|
-
etaDate: { canFilter: true, canSort: true },
|
|
75
|
-
createdAt: { canFilter: true, canSort: true },
|
|
74
|
+
etaDate: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
75
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
76
76
|
};
|
|
77
77
|
export const shipmentsRepository = new ShipmentsRepository();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { firebaseFieldOps } from "../../../providers/db-firebase";
|
|
2
|
-
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
2
|
+
import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { DatabaseError } from "../../../errors";
|
|
5
5
|
import { SUPPORT_TICKET_FIELDS } from "../../../constants/field-names";
|
|
@@ -115,7 +115,7 @@ SupportRepository.SIEVE_FIELDS = {
|
|
|
115
115
|
priority: { canFilter: true, canSort: false },
|
|
116
116
|
assignedTo: { canFilter: true, canSort: false },
|
|
117
117
|
orderId: { canFilter: true, canSort: false },
|
|
118
|
-
createdAt: { canFilter: true, canSort: true },
|
|
119
|
-
updatedAt: { canFilter: true, canSort: true },
|
|
118
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
119
|
+
updatedAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
120
120
|
};
|
|
121
121
|
export const supportRepository = new SupportRepository();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
1
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
3
|
import { TESTER_CHECKLIST_ITEM_COLLECTION, TESTER_CHECKLIST_ITEM_FIELDS, createChecklistItemId, } from "../schemas/firestore";
|
|
4
4
|
export class TesterChecklistItemRepository extends BaseRepository {
|
|
@@ -130,7 +130,7 @@ TesterChecklistItemRepository.SIEVE_FIELDS = {
|
|
|
130
130
|
phase: { canFilter: true, canSort: true },
|
|
131
131
|
isActive: { canFilter: true, canSort: false },
|
|
132
132
|
searchTokens: { canFilter: true, canSort: false },
|
|
133
|
-
createdAt: { canFilter: true, canSort: true },
|
|
133
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
134
134
|
bugConfirmed: { canFilter: true, canSort: false },
|
|
135
135
|
};
|
|
136
136
|
export const testerChecklistItemRepository = new TesterChecklistItemRepository();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseRepository } from "../../../providers/db-firebase";
|
|
1
|
+
import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
|
|
2
2
|
import { TESTER_CHECKLIST_RESPONSE_COLLECTION, TESTER_CHECKLIST_RESPONSE_FIELDS, createChecklistResponseId, } from "../schemas/firestore";
|
|
3
3
|
import { testerChecklistItemRepository } from "./tester-checklist-item.repository";
|
|
4
4
|
function escapeMd(text) {
|
|
@@ -230,6 +230,6 @@ TesterChecklistResponseRepository.SIEVE_FIELDS = {
|
|
|
230
230
|
phase: { canFilter: true, canSort: true },
|
|
231
231
|
answer: { canFilter: true, canSort: false },
|
|
232
232
|
status: { canFilter: true, canSort: false },
|
|
233
|
-
createdAt: { canFilter: true, canSort: true },
|
|
233
|
+
createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
|
|
234
234
|
};
|
|
235
235
|
export const testerChecklistResponseRepository = new TesterChecklistResponseRepository();
|
|
@@ -207,6 +207,12 @@ const rawTesterChecklistItems = [
|
|
|
207
207
|
{ key: "classified-contact-flow", label: "A classified listing shows a contact-seller flow with deliberately no checkout/buy button" },
|
|
208
208
|
{ key: "digitalcode-delivery", label: "Purchasing a digital-code listing delivers the code to the buyer post-purchase" },
|
|
209
209
|
{ key: "live-item-detail", label: "A live-item listing's detail page shows the livestream link correctly" },
|
|
210
|
+
{
|
|
211
|
+
key: "live-item-video-mandatory",
|
|
212
|
+
label: "Creating a live-item listing (species: animals/plants) without a video is rejected with a clear error; a live listing WITH a video plays correctly in the gallery's video slide and its poster thumbnail is watermarked",
|
|
213
|
+
description: "Verify the block on both the admin product editor and the seller \"List a live item\" form. Then verify playback against the tester-sandbox fixture \"Test Live Item — Golden Retriever Puppy\" (live-tester-sandbox-1) — its video is a real dog clip (previously a broken YouTube link that never played); confirm it actually plays in theater mode and the thumbnail carries the site watermark.",
|
|
214
|
+
href: "/store/live/new",
|
|
215
|
+
},
|
|
210
216
|
],
|
|
211
217
|
},
|
|
212
218
|
{
|
|
@@ -224,7 +230,11 @@ const rawTesterChecklistItems = [
|
|
|
224
230
|
{ key: "shipping-address-edit", label: "Editing an existing shipping address from checkout works" },
|
|
225
231
|
{ key: "shipping-method-selection", label: "Selecting a shipping method/provider at checkout recalculates the shipping fee correctly" },
|
|
226
232
|
{ key: "gst-breakdown-display", label: "When GST is enabled in Site Settings, checkout shows the correct CGST/SGST or IGST breakdown based on buyer vs seller state" },
|
|
227
|
-
{
|
|
233
|
+
{
|
|
234
|
+
key: "checkout-otp-highvalue",
|
|
235
|
+
label: "Carts ≥ the admin-configured high-value OTP threshold (Site Settings → Shipping → \"High-value checkout OTP threshold\", default ₹5,000) prompt a \"Verify this order\" email OTP right before payment (except COD); carts below the threshold check out directly with no OTP step at all",
|
|
236
|
+
description: "Root-caused 2026-08-20 — the OTP email send used to be fire-and-forget, so a Resend/API failure silently left the buyer stuck with no code and no error. Sending now surfaces a real error if the email genuinely fails to send, and the \"already sent, retry in N minutes\" rate-limit message is a clear sentence instead of a raw error code. Place a real order ≥ the threshold and confirm the code actually arrives by email (check spam too) — report the exact error text if it still doesn't.",
|
|
237
|
+
},
|
|
228
238
|
{ key: "payment-method-selection", label: "Choosing between COD, UPI/manual, and Razorpay (when enabled) at checkout works" },
|
|
229
239
|
{ key: "payment-window-countdown", label: "Manual-payment orders show a 15-minute countdown timer on the payment page" },
|
|
230
240
|
{ key: "payment-proof-upload", label: "Uploading manual payment proof (screenshot, UTR, mark-as-paid + agreement checkboxes) works" },
|
|
@@ -334,6 +344,17 @@ const rawTesterChecklistItems = [
|
|
|
334
344
|
label: "The mobile bottom navigation bar shows a Cart tab with a live item-count badge; Wishlist is no longer a bottom-tab slot but is still reachable from the header",
|
|
335
345
|
description: "Wishlist was intentionally removed only from the bottom tab bar, not from the header/app-bar or main navigation — confirm the header wishlist icon is unaffected.",
|
|
336
346
|
},
|
|
347
|
+
{
|
|
348
|
+
key: "cart-badge-matches-cart-page",
|
|
349
|
+
label: "After adding an item to the cart, the item actually appears when you open the cart page — not just an updated badge count with an empty cart",
|
|
350
|
+
description: "Root-caused 2026-08-20 — a background sync manager that replays queued cart/wishlist writes to the server was built but never mounted anywhere in the app, so the cart badge (read from a local queue) updated instantly while the server-backed cart page (read from Firestore) never received the write and showed empty. Add an item, then navigate to /cart (not just glance at the badge) and confirm it's really there — check on both a fresh session and after a page reload.",
|
|
351
|
+
href: "/cart",
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
key: "cart-mobile-no-overflow",
|
|
355
|
+
label: "On mobile, cart item cards stay fully within the screen width per seller group — no horizontal overflow/clipping — and a floating checkout bar (styled like the bulk-action bar, sitting above the bottom nav) shows the total and a Checkout button, with enough bottom margin that the last cart item is never hidden behind it",
|
|
356
|
+
href: "/cart",
|
|
357
|
+
},
|
|
337
358
|
],
|
|
338
359
|
},
|
|
339
360
|
{
|
|
@@ -398,6 +419,12 @@ const rawTesterChecklistItems = [
|
|
|
398
419
|
{ key: "browser-back-forward", label: "Browser back/forward buttons move correctly between dashboard sub-pages without breaking the layout" },
|
|
399
420
|
{ key: "logged-out-redirect", label: "Opening a user dashboard URL while logged out redirects to login, then returns to the originally requested page after signing in" },
|
|
400
421
|
{ key: "breadcrumbs-accurate", label: "Breadcrumbs (where present) on nested user dashboard pages accurately reflect the current location" },
|
|
422
|
+
{
|
|
423
|
+
key: "sidebar-logout-button",
|
|
424
|
+
label: "The user dashboard sidebar itself (not just the header profile dropdown) has a visible \"Log out\" action at the bottom, and clicking it signs out and redirects to login",
|
|
425
|
+
description: "Previously the only logout affordance while on a dashboard page was the header profile dropdown — the sidebar had none. Check on both mobile (drawer) and desktop (persistent rail).",
|
|
426
|
+
href: "/user",
|
|
427
|
+
},
|
|
401
428
|
],
|
|
402
429
|
},
|
|
403
430
|
]),
|
|
@@ -536,6 +563,11 @@ const rawTesterChecklistItems = [
|
|
|
536
563
|
{ key: "store-browser-back-forward", label: "Browser back/forward buttons move correctly between store dashboard sub-pages without breaking the layout" },
|
|
537
564
|
{ key: "store-logged-out-redirect", label: "Opening a store dashboard URL while logged out (or as a non-seller) redirects appropriately instead of erroring" },
|
|
538
565
|
{ key: "store-nav-listing-type-groups", label: "The seller nav correctly groups/labels each listing-type management area (standard, auctions, pre-orders, prize draws, bundles, classifieds, digital codes, live, art, stickers)" },
|
|
566
|
+
{
|
|
567
|
+
key: "store-sidebar-logout-button",
|
|
568
|
+
label: "The store dashboard sidebar has a visible \"Log out\" action at the bottom (not just the header profile dropdown), and clicking it signs out and redirects to login",
|
|
569
|
+
href: "/store",
|
|
570
|
+
},
|
|
539
571
|
],
|
|
540
572
|
},
|
|
541
573
|
]),
|
|
@@ -669,6 +701,11 @@ const rawTesterChecklistItems = [
|
|
|
669
701
|
{ key: "card-row-heights-aligned", label: "Product/auction/pre-order/event cards in the same row are the same height, with their action button (Reserve now, Place bid, View details, etc.) aligned along a common bottom edge even when one card's description is longer than another's" },
|
|
670
702
|
{ key: "image-watermark-subtle", label: "Product and listing images show a small, subtle watermark (not oversized or fully opaque, doesn't obscure the image)" },
|
|
671
703
|
{ key: "section-cta-buttons-visible", label: "Homepage section \"View all →\" / \"Go to…\" buttons use a solid primary-colored fill so they're clearly identifiable as clickable CTAs, not a plain white/outline box that blends into the page" },
|
|
704
|
+
{
|
|
705
|
+
key: "mobile-search-bar-proportions",
|
|
706
|
+
label: "On mobile, the global header search bar's resource-type dropdown (Products/Auctions/etc.) is a compact, capped width and the text input takes up most of the remaining space — not an oversized dropdown squeezing the input down to a sliver",
|
|
707
|
+
href: "/",
|
|
708
|
+
},
|
|
672
709
|
],
|
|
673
710
|
},
|
|
674
711
|
{
|
|
@@ -752,6 +789,12 @@ const rawTesterChecklistItems = [
|
|
|
752
789
|
description: "Verify \"Beyblade Burst\" shows 2 items (matching the 2 real products inside it when browsed), not \"0 items\". If any category shows 0 despite having published products, run `npm run categories:backfill-metrics` to recompute the counters.",
|
|
753
790
|
href: "/categories",
|
|
754
791
|
},
|
|
792
|
+
{
|
|
793
|
+
key: "parent-category-includes-children",
|
|
794
|
+
label: "Visiting a PARENT category's page shows products filed under its child categories too (not just products tagged with the parent's own id), and its \"N products\" count is the sum of its own products plus every child category's",
|
|
795
|
+
description: "Verify against \"Spinning Tops\" (category-spinning-tops, the root over Original/Metal Fight/Burst/X) — its product listing and count should include every Beyblade product across all 4 generations, not show empty or only the root's own (normally zero) direct products. Note: this rollup applies to page 1 / default sort only — changing sort or paging within the tab may still narrow back to the parent's own products (a known follow-up, not yet fixed).",
|
|
796
|
+
href: "/categories",
|
|
797
|
+
},
|
|
755
798
|
{
|
|
756
799
|
key: "category-brand-related-sections",
|
|
757
800
|
label: "A category detail page shows a \"Related Categories\" section (other categories sharing the same root) and a brand detail page shows a \"Related Brands\" section, each with real items",
|
|
@@ -937,6 +980,12 @@ const rawTesterChecklistItems = [
|
|
|
937
980
|
pageLabel: "Users & Trust",
|
|
938
981
|
cases: [
|
|
939
982
|
{ key: "users-role-change", label: "Admin can change a user's role and toggle isTester/canTestAdmin", href: "/admin/users" },
|
|
983
|
+
{
|
|
984
|
+
key: "admin-delete-user-complete",
|
|
985
|
+
label: "Deleting a user from the admin Users editor removes their Firestore profile, their active sessions, AND their Firebase Auth record — not just the Firestore doc",
|
|
986
|
+
description: "Verify the deleted account can no longer log in at all (Auth record gone), not just that it's missing from the admin list.",
|
|
987
|
+
href: "/admin/users",
|
|
988
|
+
},
|
|
940
989
|
{ key: "roles-crud", label: "Admin can create and edit custom roles", href: "/admin/roles" },
|
|
941
990
|
{ key: "sessions-revoke", label: "Admin can revoke a user's active sessions", href: "/admin/sessions" },
|
|
942
991
|
{ key: "scammers-registry-admin", label: "Admin can manage the scammer registry", href: "/admin/scammers" },
|
|
@@ -976,6 +1025,17 @@ const rawTesterChecklistItems = [
|
|
|
976
1025
|
{ key: "guide-pages-admin", label: "The 8 admin guide pages all load correctly", href: "/admin/guide" },
|
|
977
1026
|
{ key: "tester-checklist-crud-admin", label: "Admin can create, edit, and toggle adminOnly on tester checklist items", href: "/admin/tester-checklist" },
|
|
978
1027
|
{ key: "tester-feedback-report-export", label: "Admin tester-feedback report shows Yes/No analytics grouped correctly and the Download Report export works", href: "/admin/tester-feedback" },
|
|
1028
|
+
{
|
|
1029
|
+
key: "admin-sidebar-logout-button",
|
|
1030
|
+
label: "The admin dashboard sidebar has a visible \"Log out\" action at the bottom (not just the header profile dropdown), and clicking it signs out and redirects to login",
|
|
1031
|
+
href: "/admin/dashboard",
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
key: "dashboard-tables-colors-avatars-icons",
|
|
1035
|
+
label: "Admin/store/user dashboard tables and list/grid cards show color-coded status badges (green/amber/red/blue by meaning, not one flat color for every status) and a circular avatar/icon per row (a real photo when the item has one — user avatar, store logo, product image — otherwise a resource-type icon), instead of plain black-on-white text rows",
|
|
1036
|
+
description: "Spot-check a few different dashboards — Users (avatar photos), Stores (logos), Products (thumbnails), and a resource with no natural photo like Orders/Payouts/Coupons (should still show a resource icon, not a blank row).",
|
|
1037
|
+
href: "/admin/users",
|
|
1038
|
+
},
|
|
979
1039
|
],
|
|
980
1040
|
},
|
|
981
1041
|
{
|
package/dist/index.d.ts
CHANGED
|
@@ -1838,6 +1838,7 @@ export type { FeedbackConfig } from "./features/events/index";
|
|
|
1838
1838
|
export type { FormFieldType } from "./features/events/index";
|
|
1839
1839
|
export type { LeaderboardEntry } from "./features/events/index";
|
|
1840
1840
|
export type { PollResultEntry } from "./features/events/index";
|
|
1841
|
+
export type { SpinResultEntry } from "./features/events/index";
|
|
1841
1842
|
export type { OfferConfig } from "./features/events/index";
|
|
1842
1843
|
export type { PollConfig } from "./features/events/index";
|
|
1843
1844
|
export type { PollResultsVisibility } from "./features/events/index";
|
|
@@ -27,4 +27,4 @@ export declare const firebaseDbProvider: IDbProvider;
|
|
|
27
27
|
export { buildFirebaseClientConfig, normalizeFirebaseConfigValue, } from "./client-config";
|
|
28
28
|
export type { FirebaseClientConfig } from "./client-config";
|
|
29
29
|
export type { SieveModel, SieveFields, SieveFieldConfig, SieveOptions, SieveResult, SieveFilterAlias, SieveFilterAliases, FirebaseSieveFieldConfig, FirebaseSieveFields, FirebaseSieveOptions, FirebaseSieveResult, } from "./sieve";
|
|
30
|
-
export { applySieveToFirestore, expandFilterAliases } from "./sieve";
|
|
30
|
+
export { applySieveToFirestore, expandFilterAliases, parseSieveDateValue } from "./sieve";
|
|
@@ -38,4 +38,4 @@ export const firebaseDbProvider = {
|
|
|
38
38
|
};
|
|
39
39
|
// Firebase client config builder (for consumer app bundles)
|
|
40
40
|
export { buildFirebaseClientConfig, normalizeFirebaseConfigValue, } from "./client-config";
|
|
41
|
-
export { applySieveToFirestore, expandFilterAliases } from "./sieve";
|
|
41
|
+
export { applySieveToFirestore, expandFilterAliases, parseSieveDateValue } from "./sieve";
|
|
@@ -75,8 +75,27 @@ export type SieveFieldConfig = {
|
|
|
75
75
|
path?: string;
|
|
76
76
|
canFilter?: boolean;
|
|
77
77
|
canSort?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Convert the raw string filter value before it reaches the Firestore
|
|
80
|
+
* adapter. Required for any field stored as a Firestore Timestamp — without
|
|
81
|
+
* it, sievejs's default `convertValue` (`processor.js`) leaves date-like
|
|
82
|
+
* filter values as plain strings, and a Firestore inequality (`>`, `<`,
|
|
83
|
+
* `>=`, `<=`) comparing a Timestamp field against a string value matches
|
|
84
|
+
* ZERO documents (Firestore requires the query value's type to match the
|
|
85
|
+
* field's stored type for range comparisons) — silently, with no error.
|
|
86
|
+
* Use `parseSieveDateValue` below for Timestamp fields.
|
|
87
|
+
*/
|
|
88
|
+
parseValue?: (raw: string) => unknown;
|
|
78
89
|
};
|
|
79
90
|
export type SieveFields = Record<string, SieveFieldConfig>;
|
|
91
|
+
/**
|
|
92
|
+
* `SieveFieldConfig.parseValue` for any field stored as a Firestore
|
|
93
|
+
* Timestamp. Converts an ISO date string (or any `Date`-parseable value) into
|
|
94
|
+
* a real `Date`, which the Admin SDK compares correctly against a Timestamp
|
|
95
|
+
* field. Falls back to the raw string on an unparseable value rather than
|
|
96
|
+
* throwing, so a malformed filter degrades to "no match" instead of a 500.
|
|
97
|
+
*/
|
|
98
|
+
export declare function parseSieveDateValue(raw: string): Date | string;
|
|
80
99
|
export interface SieveModel {
|
|
81
100
|
/** Comma-delimited filter expressions, e.g. `status==published,price>=100` */
|
|
82
101
|
filters?: string;
|
|
@@ -101,6 +101,17 @@ import { expandFilterAliases as _expandFilterAliases, } from "./filter-aliases";
|
|
|
101
101
|
// should prefer importing from `./filter-aliases` directly — that path has
|
|
102
102
|
// zero runtime deps and is client-safe.
|
|
103
103
|
export { _expandFilterAliases as expandFilterAliases, };
|
|
104
|
+
/**
|
|
105
|
+
* `SieveFieldConfig.parseValue` for any field stored as a Firestore
|
|
106
|
+
* Timestamp. Converts an ISO date string (or any `Date`-parseable value) into
|
|
107
|
+
* a real `Date`, which the Admin SDK compares correctly against a Timestamp
|
|
108
|
+
* field. Falls back to the raw string on an unparseable value rather than
|
|
109
|
+
* throwing, so a malformed filter degrades to "no match" instead of a 500.
|
|
110
|
+
*/
|
|
111
|
+
export function parseSieveDateValue(raw) {
|
|
112
|
+
const parsed = new Date(raw);
|
|
113
|
+
return Number.isNaN(parsed.getTime()) ? raw : parsed;
|
|
114
|
+
}
|
|
104
115
|
const SIEVE_DEFAULTS = {
|
|
105
116
|
caseSensitive: false,
|
|
106
117
|
defaultPageSize: 20,
|
package/dist/server.d.ts
CHANGED
|
@@ -272,6 +272,8 @@ export { eventIdGET } from "./features/events/server";
|
|
|
272
272
|
export { eventsGET } from "./features/events/server";
|
|
273
273
|
export { getEventLeaderboard } from "./features/events/server";
|
|
274
274
|
export { getEventPollResults } from "./features/events/server";
|
|
275
|
+
export type { PollResultEntry, SpinResultEntry } from "./features/events/index";
|
|
276
|
+
export { getEventSpinResults } from "./features/events/server";
|
|
275
277
|
export { getBugHunterLeaderboard } from "./features/tester/server";
|
|
276
278
|
export { getPublicEventById } from "./features/events/server";
|
|
277
279
|
export { listPublicEvents } from "./features/events/server";
|
package/dist/server.js
CHANGED
|
@@ -779,6 +779,9 @@ export { getEventLeaderboard } from "./features/events/server";
|
|
|
779
779
|
// getEventPollResults - Helper for get event poll results (per-option vote tally).
|
|
780
780
|
export { getEventPollResults } from "./features/events/server";
|
|
781
781
|
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|
|
782
|
+
// getEventSpinResults - Last N winning spins for a spin_wheel event, most recent first.
|
|
783
|
+
export { getEventSpinResults } from "./features/events/server";
|
|
784
|
+
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|
|
782
785
|
// getBugHunterLeaderboard - Ranks testers by confirmed-bug count.
|
|
783
786
|
export { getBugHunterLeaderboard } from "./features/tester/server";
|
|
784
787
|
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|