@mohasinac/appkit 2.8.5 → 2.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/features/checkout/actions.js +2 -1
- package/dist/_internal/server/features/grouped/data.js +9 -8
- package/dist/_internal/server/features/products/data.js +2 -1
- package/dist/_internal/server/features/refunds/actions.js +2 -1
- package/dist/_internal/server/features/seo/sitemap.js +2 -1
- package/dist/_internal/server/jobs/core/countersReconcile.js +4 -4
- package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +8 -8
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/field-names.d.ts +3 -0
- package/dist/constants/field-names.js +3 -0
- package/dist/features/admin/repository/chat.repository.js +7 -6
- package/dist/features/auctions/components/AuctionBottomActions.js +3 -4
- package/dist/features/auctions/repository/bid.repository.js +12 -12
- package/dist/features/blog/actions/blog-actions.d.ts +2 -2
- package/dist/features/blog/schemas/index.d.ts +1 -1
- package/dist/features/cart/repository/cart.repository.js +2 -1
- package/dist/features/categories/repository/categories.repository.js +26 -25
- package/dist/features/categories/schemas/firestore.d.ts +1 -0
- package/dist/features/categories/schemas/firestore.js +1 -0
- package/dist/features/categories/schemas/index.d.ts +10 -10
- package/dist/features/events/components/EventParticipateView.js +2 -1
- package/dist/features/events/schemas/index.d.ts +3 -3
- package/dist/features/faq/actions/faq-actions.d.ts +6 -6
- package/dist/features/faq/repository/faqs.repository.js +21 -20
- package/dist/features/faq/schemas/index.d.ts +2 -2
- package/dist/features/homepage/repository/carousel.repository.js +8 -7
- package/dist/features/homepage/repository/carousels.repository.js +2 -1
- package/dist/features/homepage/repository/homepage-sections.repository.js +6 -5
- package/dist/features/messages/repository/conversations.repository.js +5 -4
- package/dist/features/orders/repository/orders.repository.js +13 -13
- package/dist/features/products/repository/product-features.repository.js +7 -6
- package/dist/features/products/repository/product-templates.repository.js +4 -3
- package/dist/features/products/repository/products.repository.js +4 -4
- package/dist/features/promotions/repository/claimed-coupons.repository.js +2 -1
- package/dist/features/promotions/repository/coupons.repository.js +4 -4
- package/dist/features/reviews/repository/reviews.repository.js +2 -2
- package/dist/features/scams/repository/scammer.repository.js +3 -3
- package/dist/features/search/components/Search.js +11 -0
- package/dist/features/support/repository/support.repository.js +8 -7
- package/dist/seed/products-auctions-seed-data.js +107 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* concerns; the action only does transactional order placement.
|
|
8
8
|
*/
|
|
9
9
|
import { ApiError, ValidationError, NotFoundError, ERROR_MESSAGES } from "../../../../errors";
|
|
10
|
+
import { ORDER_FIELDS } from "../../../../constants/field-names";
|
|
10
11
|
import { serverLogger } from "../../../../monitoring";
|
|
11
12
|
import { unitOfWork, siteSettingsRepository, userRepository, storeRepository, couponsRepository, notificationRepository, claimedCouponsRepository, } from "../../../../repositories";
|
|
12
13
|
import { failedCheckoutRepository } from "../../../../features/checkout/repository/failed-checkout.repository";
|
|
@@ -38,7 +39,7 @@ async function claimDigitalCodeForOrder(db, productId, orderId, userId, opts) {
|
|
|
38
39
|
.collection(PRODUCT_COLLECTION)
|
|
39
40
|
.doc(productId)
|
|
40
41
|
.collection(PRODUCT_CODES_SUBCOLLECTION);
|
|
41
|
-
const snap = await codesRef.where(
|
|
42
|
+
const snap = await codesRef.where(ORDER_FIELDS.STATUS, "==", "available").limit(1).get();
|
|
42
43
|
if (snap.empty) {
|
|
43
44
|
serverLogger.warn("claimDigitalCode: code pool exhausted", { productId, orderId });
|
|
44
45
|
return;
|
|
@@ -2,6 +2,7 @@ import { cache } from "react";
|
|
|
2
2
|
import { getAdminDb } from "../../../../providers/db-firebase";
|
|
3
3
|
import { GROUPED_LISTINGS_COLLECTION, } from "../../../../features/grouped/schemas/firestore";
|
|
4
4
|
import { productRepository } from "../../../../repositories";
|
|
5
|
+
import { PRODUCT_FIELDS, CATEGORY_FIELDS } from "../../../../constants/field-names";
|
|
5
6
|
import { GROUPED_LISTINGS_FEATURED_LIMIT, GROUPED_LISTINGS_PAGE_SIZE, GROUPED_LISTINGS_SITEMAP_LIMIT, } from "../../../shared/features/grouped/config";
|
|
6
7
|
function mapDoc(doc) {
|
|
7
8
|
const data = doc.data();
|
|
@@ -43,7 +44,7 @@ export const getGroupedListingForDetail = cache(async (slug) => {
|
|
|
43
44
|
const db = getAdminDb();
|
|
44
45
|
const snap = await db
|
|
45
46
|
.collection(GROUPED_LISTINGS_COLLECTION)
|
|
46
|
-
.where(
|
|
47
|
+
.where(CATEGORY_FIELDS.SLUG, "==", slug)
|
|
47
48
|
.limit(1)
|
|
48
49
|
.get();
|
|
49
50
|
if (snap.empty) {
|
|
@@ -77,17 +78,17 @@ export async function listGroupedListings(params = {}) {
|
|
|
77
78
|
const db = getAdminDb();
|
|
78
79
|
let q = db
|
|
79
80
|
.collection(GROUPED_LISTINGS_COLLECTION)
|
|
80
|
-
.where(
|
|
81
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true);
|
|
81
82
|
if (params.featuredOnly)
|
|
82
|
-
q = q.where(
|
|
83
|
+
q = q.where(CATEGORY_FIELDS.IS_FEATURED, "==", true);
|
|
83
84
|
if (params.storeId)
|
|
84
|
-
q = q.where(
|
|
85
|
+
q = q.where(PRODUCT_FIELDS.STORE_ID, "==", params.storeId);
|
|
85
86
|
if (params.brandSlug)
|
|
86
|
-
q = q.where(
|
|
87
|
+
q = q.where(PRODUCT_FIELDS.BRAND_SLUG, "==", params.brandSlug);
|
|
87
88
|
if (params.categorySlug)
|
|
88
|
-
q = q.where(
|
|
89
|
+
q = q.where(PRODUCT_FIELDS.CATEGORY_SLUG, "==", params.categorySlug);
|
|
89
90
|
const snap = await q
|
|
90
|
-
.orderBy(
|
|
91
|
+
.orderBy(PRODUCT_FIELDS.CREATED_AT, "desc")
|
|
91
92
|
.limit(params.limit ?? GROUPED_LISTINGS_PAGE_SIZE)
|
|
92
93
|
.get();
|
|
93
94
|
return snap.docs.map(mapDoc);
|
|
@@ -105,7 +106,7 @@ export async function listSitemapGroupedListings() {
|
|
|
105
106
|
const db = getAdminDb();
|
|
106
107
|
const snap = await db
|
|
107
108
|
.collection(GROUPED_LISTINGS_COLLECTION)
|
|
108
|
-
.where(
|
|
109
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
109
110
|
.select("slug", "updatedAt")
|
|
110
111
|
.limit(GROUPED_LISTINGS_SITEMAP_LIMIT)
|
|
111
112
|
.get();
|
|
@@ -4,6 +4,7 @@ import { reviewRepository } from "../../../../repositories";
|
|
|
4
4
|
import { getAdminDb } from "../../../../providers/db-firebase";
|
|
5
5
|
import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
|
|
6
6
|
import { PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
|
|
7
|
+
import { PRODUCT_FIELDS } from "../../../../constants/field-names";
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// Request-scoped cache — each function is deduplicated per RSC render tree.
|
|
9
10
|
// Both generateMetadata() and the page component get the same promise.
|
|
@@ -22,7 +23,7 @@ export async function listSitemapProducts() {
|
|
|
22
23
|
const db = getAdminDb();
|
|
23
24
|
const snapshot = await db
|
|
24
25
|
.collection(PRODUCT_COLLECTION)
|
|
25
|
-
.where(
|
|
26
|
+
.where(PRODUCT_FIELDS.STATUS, "==", "published")
|
|
26
27
|
.select("slug", "id", "updatedAt", "listingType")
|
|
27
28
|
.limit(PRODUCTS_SITEMAP_LIMIT)
|
|
28
29
|
.get();
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { randomUUID } from "crypto";
|
|
17
17
|
import { getProviders } from "../../../../contracts/registry";
|
|
18
18
|
import { orderRepository } from "../../../..";
|
|
19
|
+
import { ORDER_FIELDS } from "../../../../constants/field-names";
|
|
19
20
|
import { NotFoundError, ValidationError } from "../../../../errors";
|
|
20
21
|
import { serverLogger } from "../../../../monitoring";
|
|
21
22
|
import { applyRefundDeductionAction } from "../payouts/actions";
|
|
@@ -93,7 +94,7 @@ export async function processRefundAction(input) {
|
|
|
93
94
|
.doc(item.productId)
|
|
94
95
|
.collection(PRODUCT_CODES_SUBCOLLECTION)
|
|
95
96
|
.where("orderId", "==", input.orderId)
|
|
96
|
-
.where(
|
|
97
|
+
.where(ORDER_FIELDS.STATUS, "==", "available")
|
|
97
98
|
.limit(1)
|
|
98
99
|
.get()
|
|
99
100
|
.then((snap) => {
|
|
@@ -7,6 +7,7 @@ import { CATEGORIES_COLLECTION, CATEGORY_FIELDS } from "../../../../features/cat
|
|
|
7
7
|
import { STORE_COLLECTION, STORE_FIELDS } from "../../../../features/stores";
|
|
8
8
|
import { SCAMMER_COLLECTION } from "../../../../features/scams/schemas/firestore";
|
|
9
9
|
import { serverLogger } from "../../../../monitoring/server-logger";
|
|
10
|
+
import { PRODUCT_FIELDS } from "../../../../constants/field-names";
|
|
10
11
|
// Product field strings — matches consumer field-names.ts
|
|
11
12
|
const PRODUCT_STATUS = "status";
|
|
12
13
|
const PRODUCT_STATUS_PUBLISHED = "published";
|
|
@@ -211,7 +212,7 @@ async function fetchScammerUrls(baseUrl) {
|
|
|
211
212
|
const db = getAdminDb();
|
|
212
213
|
const snap = await db
|
|
213
214
|
.collection(SCAMMER_COLLECTION)
|
|
214
|
-
.where(
|
|
215
|
+
.where(PRODUCT_FIELDS.STATUS, "==", "verified")
|
|
215
216
|
.select("seo", "updatedAt")
|
|
216
217
|
.limit(2000)
|
|
217
218
|
.get();
|
|
@@ -7,7 +7,7 @@ const ORDERS_COLLECTION = "orders";
|
|
|
7
7
|
async function reconcileCategories(ctx) {
|
|
8
8
|
const snap = await ctx.db
|
|
9
9
|
.collection(PRODUCT_COLLECTION)
|
|
10
|
-
.where(
|
|
10
|
+
.where(ORDER_FIELDS.STATUS, "==", ProductStatusValues.PUBLISHED)
|
|
11
11
|
.limit(QUERY_LIMIT)
|
|
12
12
|
.get();
|
|
13
13
|
ctx.logger.info(`[categories] ${snap.size} published products found`);
|
|
@@ -63,13 +63,13 @@ async function reconcileStores(ctx) {
|
|
|
63
63
|
const [productsSnap, ordersSnap, reviewStats] = await Promise.all([
|
|
64
64
|
ctx.db
|
|
65
65
|
.collection(PRODUCT_COLLECTION)
|
|
66
|
-
.where(
|
|
67
|
-
.where(
|
|
66
|
+
.where(ORDER_FIELDS.SELLER_ID, "==", sellerId)
|
|
67
|
+
.where(ORDER_FIELDS.STATUS, "==", ProductStatusValues.PUBLISHED)
|
|
68
68
|
.limit(QUERY_LIMIT)
|
|
69
69
|
.get(),
|
|
70
70
|
ctx.db
|
|
71
71
|
.collection(ORDERS_COLLECTION)
|
|
72
|
-
.where(
|
|
72
|
+
.where(ORDER_FIELDS.SELLER_ID, "==", sellerId)
|
|
73
73
|
.where(ORDER_FIELDS.STATUS, "==", ORDER_FIELDS.STATUS_VALUES.DELIVERED)
|
|
74
74
|
.limit(QUERY_LIMIT)
|
|
75
75
|
.get(),
|
|
@@ -21,8 +21,8 @@ export declare const blogPostInputSchema: z.ZodObject<{
|
|
|
21
21
|
content: string;
|
|
22
22
|
isFeatured: boolean;
|
|
23
23
|
youtubeId?: string | undefined;
|
|
24
|
-
coverImage?: string | undefined;
|
|
25
24
|
excerpt?: string | undefined;
|
|
25
|
+
coverImage?: string | undefined;
|
|
26
26
|
metaTitle?: string | undefined;
|
|
27
27
|
metaDescription?: string | undefined;
|
|
28
28
|
}, {
|
|
@@ -34,8 +34,8 @@ export declare const blogPostInputSchema: z.ZodObject<{
|
|
|
34
34
|
youtubeId?: string | undefined;
|
|
35
35
|
tags?: string[] | undefined;
|
|
36
36
|
isFeatured?: boolean | undefined;
|
|
37
|
-
coverImage?: string | undefined;
|
|
38
37
|
excerpt?: string | undefined;
|
|
38
|
+
coverImage?: string | undefined;
|
|
39
39
|
metaTitle?: string | undefined;
|
|
40
40
|
metaDescription?: string | undefined;
|
|
41
41
|
}>;
|
|
@@ -60,8 +60,8 @@ export declare const blogPostUpdateSchema: z.ZodObject<Omit<{
|
|
|
60
60
|
tags?: string[] | undefined;
|
|
61
61
|
content?: string | undefined;
|
|
62
62
|
isFeatured?: boolean | undefined;
|
|
63
|
-
coverImage?: string | undefined;
|
|
64
63
|
excerpt?: string | undefined;
|
|
64
|
+
coverImage?: string | undefined;
|
|
65
65
|
metaTitle?: string | undefined;
|
|
66
66
|
metaDescription?: string | undefined;
|
|
67
67
|
}, {
|
|
@@ -72,8 +72,8 @@ export declare const blogPostUpdateSchema: z.ZodObject<Omit<{
|
|
|
72
72
|
tags?: string[] | undefined;
|
|
73
73
|
content?: string | undefined;
|
|
74
74
|
isFeatured?: boolean | undefined;
|
|
75
|
-
coverImage?: string | undefined;
|
|
76
75
|
excerpt?: string | undefined;
|
|
76
|
+
coverImage?: string | undefined;
|
|
77
77
|
metaTitle?: string | undefined;
|
|
78
78
|
metaDescription?: string | undefined;
|
|
79
79
|
}>;
|
|
@@ -314,6 +314,10 @@ export declare const bundleUpdateSchema: z.ZodObject<{
|
|
|
314
314
|
isActive?: boolean | undefined;
|
|
315
315
|
bundleProductIds?: string[] | undefined;
|
|
316
316
|
bundleKind?: "brand" | "special" | undefined;
|
|
317
|
+
bundleItemDetails?: {
|
|
318
|
+
productId: string;
|
|
319
|
+
drawCount?: number | undefined;
|
|
320
|
+
}[] | undefined;
|
|
317
321
|
bundlePriceInPaise?: number | undefined;
|
|
318
322
|
bundleQueryRule?: {
|
|
319
323
|
type: "static";
|
|
@@ -329,10 +333,6 @@ export declare const bundleUpdateSchema: z.ZodObject<{
|
|
|
329
333
|
limit: number;
|
|
330
334
|
orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
|
|
331
335
|
} | undefined;
|
|
332
|
-
bundleItemDetails?: {
|
|
333
|
-
productId: string;
|
|
334
|
-
drawCount?: number | undefined;
|
|
335
|
-
}[] | undefined;
|
|
336
336
|
}, {
|
|
337
337
|
description?: string | undefined;
|
|
338
338
|
name?: string | undefined;
|
|
@@ -344,6 +344,10 @@ export declare const bundleUpdateSchema: z.ZodObject<{
|
|
|
344
344
|
isActive?: boolean | undefined;
|
|
345
345
|
bundleProductIds?: string[] | undefined;
|
|
346
346
|
bundleKind?: "brand" | "special" | undefined;
|
|
347
|
+
bundleItemDetails?: {
|
|
348
|
+
productId: string;
|
|
349
|
+
drawCount?: number | undefined;
|
|
350
|
+
}[] | undefined;
|
|
347
351
|
bundlePriceInPaise?: number | undefined;
|
|
348
352
|
bundleQueryRule?: {
|
|
349
353
|
type: "static";
|
|
@@ -359,10 +363,6 @@ export declare const bundleUpdateSchema: z.ZodObject<{
|
|
|
359
363
|
limit: number;
|
|
360
364
|
orderBy?: "price-asc" | "price-desc" | "createdAt-desc" | undefined;
|
|
361
365
|
} | undefined;
|
|
362
|
-
bundleItemDetails?: {
|
|
363
|
-
productId: string;
|
|
364
|
-
drawCount?: number | undefined;
|
|
365
|
-
}[] | undefined;
|
|
366
366
|
}>;
|
|
367
367
|
export type BundleCreateBody = z.infer<typeof bundleCreateSchema>;
|
|
368
368
|
export type BundleUpdateBody = z.infer<typeof bundleUpdateSchema>;
|
package/dist/client.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export { registerClientRealtimeProvider, getClientRealtimeProvider, type IClient
|
|
|
3
3
|
export { registerClientSessionAdapter, getClientSessionAdapter, type IClientSessionAdapter, type AdapterAuthUser, type AuthUnsubscribe, } from "./contracts/client-session";
|
|
4
4
|
export { ConfirmDeleteModal } from "./ui/components/ConfirmDeleteModal";
|
|
5
5
|
export { EmptyState } from "./ui/components/EmptyState";
|
|
6
|
+
export { Skeleton } from "./ui/components/Skeleton";
|
|
7
|
+
export type { SkeletonProps } from "./ui/components/Skeleton";
|
|
6
8
|
export { Divider } from "./ui/components/Divider";
|
|
7
9
|
export { TabStrip } from "./ui/components/TabStrip";
|
|
8
10
|
export { SellerProductsView } from "./features/seller/components/SellerProductsView";
|
package/dist/client.js
CHANGED
|
@@ -11,6 +11,7 @@ export { registerClientSessionAdapter, getClientSessionAdapter, } from "./contra
|
|
|
11
11
|
export { ConfirmDeleteModal } from "./ui/components/ConfirmDeleteModal";
|
|
12
12
|
// S-STORE — pure-UI primitives + Seller view consumed by new dashboard pages
|
|
13
13
|
export { EmptyState } from "./ui/components/EmptyState";
|
|
14
|
+
export { Skeleton } from "./ui/components/Skeleton";
|
|
14
15
|
export { Divider } from "./ui/components/Divider";
|
|
15
16
|
export { TabStrip } from "./ui/components/TabStrip";
|
|
16
17
|
export { SellerProductsView } from "./features/seller/components/SellerProductsView";
|
|
@@ -410,6 +410,7 @@ export declare const CATEGORY_FIELDS: {
|
|
|
410
410
|
readonly IS_FEATURED: "isFeatured";
|
|
411
411
|
readonly SHOW_ON_HOMEPAGE: "showOnHomepage";
|
|
412
412
|
readonly ICON: "icon";
|
|
413
|
+
readonly ORDER: "order";
|
|
413
414
|
readonly DISPLAY_ORDER: "displayOrder";
|
|
414
415
|
readonly IS_ACTIVE: "isActive";
|
|
415
416
|
readonly CATEGORY_TYPE: "categoryType";
|
|
@@ -599,6 +600,7 @@ export declare const SUPPORT_TICKET_FIELDS: {
|
|
|
599
600
|
readonly USER_ID: "userId";
|
|
600
601
|
readonly STORE_ID: "storeId";
|
|
601
602
|
readonly SUBJECT: "subject";
|
|
603
|
+
readonly CATEGORY: "category";
|
|
602
604
|
readonly STATUS: "status";
|
|
603
605
|
readonly PRIORITY: "priority";
|
|
604
606
|
readonly CREATED_AT: "createdAt";
|
|
@@ -620,6 +622,7 @@ export declare const SUPPORT_TICKET_FIELDS: {
|
|
|
620
622
|
};
|
|
621
623
|
export declare const CAROUSEL_FIELDS: {
|
|
622
624
|
readonly ID: "id";
|
|
625
|
+
readonly NAME: "name";
|
|
623
626
|
readonly TITLE: "title";
|
|
624
627
|
readonly ORDER: "order";
|
|
625
628
|
readonly ACTIVE: "active";
|
|
@@ -449,6 +449,7 @@ export const CATEGORY_FIELDS = {
|
|
|
449
449
|
IS_FEATURED: "isFeatured",
|
|
450
450
|
SHOW_ON_HOMEPAGE: "showOnHomepage",
|
|
451
451
|
ICON: "icon",
|
|
452
|
+
ORDER: "order",
|
|
452
453
|
DISPLAY_ORDER: "displayOrder",
|
|
453
454
|
IS_ACTIVE: "isActive",
|
|
454
455
|
// SB-UNI — categoryType discriminator (category/brand/sublisting/bundle)
|
|
@@ -679,6 +680,7 @@ export const SUPPORT_TICKET_FIELDS = {
|
|
|
679
680
|
USER_ID: "userId",
|
|
680
681
|
STORE_ID: "storeId",
|
|
681
682
|
SUBJECT: "subject",
|
|
683
|
+
CATEGORY: "category",
|
|
682
684
|
STATUS: "status",
|
|
683
685
|
PRIORITY: "priority",
|
|
684
686
|
CREATED_AT: "createdAt",
|
|
@@ -703,6 +705,7 @@ export const SUPPORT_TICKET_FIELDS = {
|
|
|
703
705
|
// ============================================================================
|
|
704
706
|
export const CAROUSEL_FIELDS = {
|
|
705
707
|
ID: "id",
|
|
708
|
+
NAME: "name",
|
|
706
709
|
TITLE: "title",
|
|
707
710
|
ORDER: "order",
|
|
708
711
|
ACTIVE: "active",
|
|
@@ -22,6 +22,7 @@ import { getAdminRealtimeDb } from "../../../providers/db-firebase";
|
|
|
22
22
|
import { BaseRepository } from "../../../providers/db-firebase";
|
|
23
23
|
import { prepareForFirestore } from "../../../providers/db-firebase";
|
|
24
24
|
import { DatabaseError } from "../../../errors";
|
|
25
|
+
import { CONVERSATION_FIELDS, STORE_FIELDS } from "../../../constants/field-names";
|
|
25
26
|
import { CHAT_ROOM_COLLECTION } from "../schemas";
|
|
26
27
|
import { encryptPiiFields, decryptPiiFields, CHAT_PII_FIELDS, } from "../../../security";
|
|
27
28
|
export { CHAT_ROOM_COLLECTION } from "../schemas";
|
|
@@ -72,11 +73,11 @@ class ChatRepository extends BaseRepository {
|
|
|
72
73
|
try {
|
|
73
74
|
const [asBuyer, asSeller] = await Promise.all([
|
|
74
75
|
this.getCollection()
|
|
75
|
-
.where(
|
|
76
|
+
.where(CONVERSATION_FIELDS.BUYER_ID, "==", userId)
|
|
76
77
|
.where("adminDeleted", "==", false)
|
|
77
78
|
.get(),
|
|
78
79
|
this.getCollection()
|
|
79
|
-
.where(
|
|
80
|
+
.where(STORE_FIELDS.OWNER_ID, "==", userId)
|
|
80
81
|
.where("adminDeleted", "==", false)
|
|
81
82
|
.get(),
|
|
82
83
|
]);
|
|
@@ -101,14 +102,14 @@ class ChatRepository extends BaseRepository {
|
|
|
101
102
|
try {
|
|
102
103
|
const [asBuyer, asSeller] = await Promise.all([
|
|
103
104
|
this.getCollection()
|
|
104
|
-
.where(
|
|
105
|
+
.where(CONVERSATION_FIELDS.BUYER_ID, "==", userId)
|
|
105
106
|
.where("adminDeleted", "==", false)
|
|
106
|
-
.orderBy(
|
|
107
|
+
.orderBy(CONVERSATION_FIELDS.UPDATED_AT, "desc")
|
|
107
108
|
.get(),
|
|
108
109
|
this.getCollection()
|
|
109
|
-
.where(
|
|
110
|
+
.where(STORE_FIELDS.OWNER_ID, "==", userId)
|
|
110
111
|
.where("adminDeleted", "==", false)
|
|
111
|
-
.orderBy(
|
|
112
|
+
.orderBy(CONVERSATION_FIELDS.UPDATED_AT, "desc")
|
|
112
113
|
.get(),
|
|
113
114
|
]);
|
|
114
115
|
const seen = new Set();
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useBottomActions } from "../../layout/hooks/useBottomActions";
|
|
3
3
|
import { formatCurrency } from "../../../utils/number.formatter";
|
|
4
|
-
import { ACTION_ID } from "../../products/constants/action-defs";
|
|
4
|
+
import { ACTION_ID, ACTION_META } from "../../products/constants/action-defs";
|
|
5
5
|
export function AuctionBottomActions({ currentBid, currency, bidCount, isEnded, }) {
|
|
6
|
+
const placeBidMeta = ACTION_META[ACTION_ID.PLACE_BID];
|
|
6
7
|
useBottomActions(isEnded
|
|
7
8
|
? {}
|
|
8
9
|
: {
|
|
9
10
|
actions: [
|
|
10
11
|
{
|
|
11
|
-
|
|
12
|
-
label: "Place Bid",
|
|
13
|
-
variant: "primary",
|
|
12
|
+
...placeBidMeta,
|
|
14
13
|
onClick: () => {
|
|
15
14
|
document
|
|
16
15
|
.getElementById("auction-bid-form")
|
|
@@ -63,7 +63,7 @@ export class BidRepository extends BaseRepository {
|
|
|
63
63
|
let query = this.db
|
|
64
64
|
.collection(this.collection)
|
|
65
65
|
.where(BID_FIELDS.USER_ID, "==", userId)
|
|
66
|
-
.orderBy(
|
|
66
|
+
.orderBy(BID_FIELDS.CREATED_AT, "desc")
|
|
67
67
|
.limit(pageSize + 1);
|
|
68
68
|
if (startAfterDoc)
|
|
69
69
|
query = query.startAfter(startAfterDoc);
|
|
@@ -86,7 +86,7 @@ export class BidRepository extends BaseRepository {
|
|
|
86
86
|
.collection(this.collection)
|
|
87
87
|
.where(BID_FIELDS.PRODUCT_ID, "==", productId)
|
|
88
88
|
.where(BID_FIELDS.STATUS, "==", "active")
|
|
89
|
-
.orderBy(
|
|
89
|
+
.orderBy(BID_FIELDS.BID_AMOUNT, "desc")
|
|
90
90
|
.get();
|
|
91
91
|
return snapshot.docs.map((doc) => ({
|
|
92
92
|
id: doc.id,
|
|
@@ -169,9 +169,9 @@ export class BidRepository extends BaseRepository {
|
|
|
169
169
|
try {
|
|
170
170
|
const snapshot = await this.db
|
|
171
171
|
.collection(this.collection)
|
|
172
|
-
.where(
|
|
173
|
-
.where(
|
|
174
|
-
.orderBy(
|
|
172
|
+
.where(BID_FIELDS.PRODUCT_ID, "==", productId)
|
|
173
|
+
.where(BID_FIELDS.STATUS, "==", "active")
|
|
174
|
+
.orderBy(BID_FIELDS.BID_AMOUNT, "desc")
|
|
175
175
|
.limit(1)
|
|
176
176
|
.get();
|
|
177
177
|
if (snapshot.empty) {
|
|
@@ -192,8 +192,8 @@ export class BidRepository extends BaseRepository {
|
|
|
192
192
|
try {
|
|
193
193
|
const snapshot = await this.db
|
|
194
194
|
.collection(this.collection)
|
|
195
|
-
.where(
|
|
196
|
-
.orderBy(
|
|
195
|
+
.where(BID_FIELDS.PRODUCT_ID, "==", productId)
|
|
196
|
+
.orderBy(BID_FIELDS.BID_AMOUNT, "desc")
|
|
197
197
|
.get();
|
|
198
198
|
return snapshot.docs.map((doc) => this.mapDoc(doc));
|
|
199
199
|
}
|
|
@@ -230,7 +230,7 @@ export class BidRepository extends BaseRepository {
|
|
|
230
230
|
// Set all bids for this product to not winning
|
|
231
231
|
const allBidsSnapshot = await this.db
|
|
232
232
|
.collection(this.collection)
|
|
233
|
-
.where(
|
|
233
|
+
.where(BID_FIELDS.PRODUCT_ID, "==", productId)
|
|
234
234
|
.get();
|
|
235
235
|
allBidsSnapshot.docs.forEach((doc) => {
|
|
236
236
|
batch.update(doc.ref, {
|
|
@@ -260,7 +260,7 @@ export class BidRepository extends BaseRepository {
|
|
|
260
260
|
const batch = this.db.batch();
|
|
261
261
|
const bidsSnapshot = await this.db
|
|
262
262
|
.collection(this.collection)
|
|
263
|
-
.where(
|
|
263
|
+
.where(BID_FIELDS.PRODUCT_ID, "==", productId)
|
|
264
264
|
.get();
|
|
265
265
|
bidsSnapshot.docs.forEach((doc) => {
|
|
266
266
|
const bid = doc.data();
|
|
@@ -283,7 +283,7 @@ export class BidRepository extends BaseRepository {
|
|
|
283
283
|
const batch = this.db.batch();
|
|
284
284
|
const bidsSnapshot = await this.db
|
|
285
285
|
.collection(this.collection)
|
|
286
|
-
.where(
|
|
286
|
+
.where(BID_FIELDS.PRODUCT_ID, "==", productId)
|
|
287
287
|
.get();
|
|
288
288
|
bidsSnapshot.docs.forEach((doc) => {
|
|
289
289
|
batch.update(doc.ref, {
|
|
@@ -303,7 +303,7 @@ export class BidRepository extends BaseRepository {
|
|
|
303
303
|
*/
|
|
304
304
|
async countByProduct(productId) {
|
|
305
305
|
try {
|
|
306
|
-
return await getFirestoreCount(this.db.collection(this.collection).where(
|
|
306
|
+
return await getFirestoreCount(this.db.collection(this.collection).where(BID_FIELDS.PRODUCT_ID, "==", productId));
|
|
307
307
|
}
|
|
308
308
|
catch (error) {
|
|
309
309
|
throw new DatabaseError(`Failed to count bids for product: ${productId}`, error);
|
|
@@ -314,7 +314,7 @@ export class BidRepository extends BaseRepository {
|
|
|
314
314
|
*/
|
|
315
315
|
async countByUser(userId) {
|
|
316
316
|
try {
|
|
317
|
-
return await getFirestoreCount(this.db.collection(this.collection).where(
|
|
317
|
+
return await getFirestoreCount(this.db.collection(this.collection).where(BID_FIELDS.USER_ID, "==", userId));
|
|
318
318
|
}
|
|
319
319
|
catch (error) {
|
|
320
320
|
throw new DatabaseError(`Failed to count bids for user: ${userId}`, error);
|
|
@@ -216,6 +216,7 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
216
216
|
content?: string | undefined;
|
|
217
217
|
isFeatured?: boolean | undefined;
|
|
218
218
|
publishedAt?: string | undefined;
|
|
219
|
+
excerpt?: string | undefined;
|
|
219
220
|
coverImage?: {
|
|
220
221
|
url: string;
|
|
221
222
|
type: "image" | "video" | "file";
|
|
@@ -225,7 +226,6 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
225
226
|
url: string;
|
|
226
227
|
type: "image";
|
|
227
228
|
} | null | undefined;
|
|
228
|
-
excerpt?: string | undefined;
|
|
229
229
|
authorId?: string | undefined;
|
|
230
230
|
authorName?: string | undefined;
|
|
231
231
|
readTimeMinutes?: number | undefined;
|
|
@@ -253,13 +253,13 @@ export declare const updateBlogPostSchema: z.ZodObject<{
|
|
|
253
253
|
content?: string | undefined;
|
|
254
254
|
isFeatured?: boolean | undefined;
|
|
255
255
|
publishedAt?: string | undefined;
|
|
256
|
+
excerpt?: string | undefined;
|
|
256
257
|
coverImage?: string | {
|
|
257
258
|
url: string;
|
|
258
259
|
type: "image" | "video" | "file";
|
|
259
260
|
alt?: string | undefined;
|
|
260
261
|
thumbnailUrl?: string | undefined;
|
|
261
262
|
} | null | undefined;
|
|
262
|
-
excerpt?: string | undefined;
|
|
263
263
|
authorId?: string | undefined;
|
|
264
264
|
authorName?: string | undefined;
|
|
265
265
|
readTimeMinutes?: number | undefined;
|
|
@@ -180,6 +180,7 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
180
180
|
content?: string | undefined;
|
|
181
181
|
isFeatured?: boolean | undefined;
|
|
182
182
|
publishedAt?: string | undefined;
|
|
183
|
+
excerpt?: string | undefined;
|
|
183
184
|
coverImage?: string | {
|
|
184
185
|
url: string;
|
|
185
186
|
type: "image" | "video" | "file";
|
|
@@ -188,7 +189,6 @@ export declare const blogPostSchema: z.ZodObject<{
|
|
|
188
189
|
source?: "upload" | "youtube" | "external" | undefined;
|
|
189
190
|
youtubeId?: string | undefined;
|
|
190
191
|
} | null | undefined;
|
|
191
|
-
excerpt?: string | undefined;
|
|
192
192
|
authorId?: string | undefined;
|
|
193
193
|
authorName?: string | undefined;
|
|
194
194
|
readTimeMinutes?: number | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { DatabaseError, NotFoundError, ValidationError } from "../../../errors";
|
|
3
|
+
import { CART_FIELDS } from "../../../constants/field-names";
|
|
3
4
|
const ERR_CART_ITEM_NOT_FOUND = "Cart item not found";
|
|
4
5
|
const ERR_CART_ITEM_LOCKED = "This item requires payment and cannot be removed or modified.";
|
|
5
6
|
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
@@ -277,7 +278,7 @@ export class CartRepository extends BaseRepository {
|
|
|
277
278
|
cutoff.setDate(cutoff.getDate() - ttlDays);
|
|
278
279
|
const snap = await this.db
|
|
279
280
|
.collection(this.collection)
|
|
280
|
-
.where(
|
|
281
|
+
.where(CART_FIELDS.UPDATED_AT, "<", cutoff)
|
|
281
282
|
.limit(500)
|
|
282
283
|
.get();
|
|
283
284
|
return snap.docs.map((d) => d.ref);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { increment, arrayUnion, arrayRemove, } from "../../../contracts/field-ops";
|
|
2
2
|
import { DatabaseError } from "../../../errors";
|
|
3
3
|
import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
|
|
4
|
+
import { PRODUCT_FIELDS } from "../../../constants/field-names";
|
|
4
5
|
import { CATEGORY_FIELDS, CATEGORIES_COLLECTION, MIN_ITEMS_FOR_FEATURED, buildCategoryTree, calculateCategoryFields, canBeFeatured, createCategoryId, isValidCategoryMove, } from "../schemas";
|
|
5
6
|
export class CategoriesRepository extends BaseRepository {
|
|
6
7
|
constructor() {
|
|
@@ -80,9 +81,9 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
80
81
|
try {
|
|
81
82
|
const snapshot = await this.db
|
|
82
83
|
.collection(this.collection)
|
|
83
|
-
.where(
|
|
84
|
-
.where(
|
|
85
|
-
.orderBy(
|
|
84
|
+
.where(CATEGORY_FIELDS.TIER, "==", 0)
|
|
85
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
86
|
+
.orderBy(CATEGORY_FIELDS.ORDER, "asc")
|
|
86
87
|
.get();
|
|
87
88
|
return snapshot.docs
|
|
88
89
|
.map((doc) => this.mapDoc(doc))
|
|
@@ -96,8 +97,8 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
96
97
|
try {
|
|
97
98
|
const snapshot = await this.db
|
|
98
99
|
.collection(this.collection)
|
|
99
|
-
.where(
|
|
100
|
-
.where(
|
|
100
|
+
.where(CATEGORY_FIELDS.IS_LEAF, "==", true)
|
|
101
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
101
102
|
.limit(500)
|
|
102
103
|
.get();
|
|
103
104
|
return snapshot.docs.map((doc) => this.mapDoc(doc));
|
|
@@ -110,9 +111,9 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
110
111
|
try {
|
|
111
112
|
const snapshot = await this.db
|
|
112
113
|
.collection(this.collection)
|
|
113
|
-
.where(
|
|
114
|
-
.where(
|
|
115
|
-
.orderBy(
|
|
114
|
+
.where(CATEGORY_FIELDS.TIER, "==", tier)
|
|
115
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
116
|
+
.orderBy(CATEGORY_FIELDS.ORDER, "asc")
|
|
116
117
|
.get();
|
|
117
118
|
return snapshot.docs
|
|
118
119
|
.map((doc) => this.mapDoc(doc))
|
|
@@ -126,9 +127,9 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
126
127
|
try {
|
|
127
128
|
const snapshot = await this.db
|
|
128
129
|
.collection(this.collection)
|
|
129
|
-
.where(
|
|
130
|
-
.orderBy(
|
|
131
|
-
.orderBy(
|
|
130
|
+
.where(CATEGORY_FIELDS.ROOT_ID, "==", rootId)
|
|
131
|
+
.orderBy(CATEGORY_FIELDS.TIER, "asc")
|
|
132
|
+
.orderBy(CATEGORY_FIELDS.ORDER, "asc")
|
|
132
133
|
.get();
|
|
133
134
|
return snapshot.docs.map((doc) => this.mapDoc(doc));
|
|
134
135
|
}
|
|
@@ -141,7 +142,7 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
141
142
|
const snapshot = await this.db
|
|
142
143
|
.collection(this.collection)
|
|
143
144
|
.where("parentIds", "array-contains", parentId)
|
|
144
|
-
.orderBy(
|
|
145
|
+
.orderBy(CATEGORY_FIELDS.ORDER, "asc")
|
|
145
146
|
.limit(100)
|
|
146
147
|
.get();
|
|
147
148
|
return snapshot.docs
|
|
@@ -156,8 +157,8 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
156
157
|
try {
|
|
157
158
|
const snapshot = await this.db
|
|
158
159
|
.collection(this.collection)
|
|
159
|
-
.where(
|
|
160
|
-
.where(
|
|
160
|
+
.where(CATEGORY_FIELDS.IS_FEATURED, "==", true)
|
|
161
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
161
162
|
.orderBy("featuredPriority", "asc")
|
|
162
163
|
.get();
|
|
163
164
|
return snapshot.docs
|
|
@@ -173,8 +174,8 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
173
174
|
const snapshot = await this.db
|
|
174
175
|
.collection(this.collection)
|
|
175
176
|
.where("isBrand", "==", true)
|
|
176
|
-
.where(
|
|
177
|
-
.orderBy(
|
|
177
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
178
|
+
.orderBy(CATEGORY_FIELDS.ORDER, "asc")
|
|
178
179
|
.get();
|
|
179
180
|
const brands = snapshot.docs.map((doc) => this.mapDoc(doc));
|
|
180
181
|
return limit > 0 ? brands.slice(0, limit) : brands;
|
|
@@ -368,9 +369,9 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
368
369
|
async listByType(type, opts = {}) {
|
|
369
370
|
let q = this.db
|
|
370
371
|
.collection(this.collection)
|
|
371
|
-
.where(
|
|
372
|
+
.where(CATEGORY_FIELDS.CATEGORY_TYPE, "==", type);
|
|
372
373
|
if (opts.activeOnly)
|
|
373
|
-
q = q.where(
|
|
374
|
+
q = q.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true);
|
|
374
375
|
if (opts.limit)
|
|
375
376
|
q = q.limit(opts.limit);
|
|
376
377
|
const snap = await q.get();
|
|
@@ -380,10 +381,10 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
380
381
|
async findBySlugAndType(slug, type) {
|
|
381
382
|
let q = this.db
|
|
382
383
|
.collection(this.collection)
|
|
383
|
-
.where(
|
|
384
|
+
.where(CATEGORY_FIELDS.SLUG, "==", slug)
|
|
384
385
|
.limit(1);
|
|
385
386
|
if (type)
|
|
386
|
-
q = q.where(
|
|
387
|
+
q = q.where(CATEGORY_FIELDS.CATEGORY_TYPE, "==", type);
|
|
387
388
|
const snap = await q.get();
|
|
388
389
|
if (snap.empty)
|
|
389
390
|
return null;
|
|
@@ -398,8 +399,8 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
398
399
|
const snap = await this.db
|
|
399
400
|
.collection("products")
|
|
400
401
|
.where("sublistingCategoryId", "==", sublistingId)
|
|
401
|
-
.where(
|
|
402
|
-
.orderBy(
|
|
402
|
+
.where(PRODUCT_FIELDS.STATUS, "==", "published")
|
|
403
|
+
.orderBy(PRODUCT_FIELDS.PRICE, "asc")
|
|
403
404
|
.limit(limit)
|
|
404
405
|
.get();
|
|
405
406
|
return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
|
|
@@ -428,9 +429,9 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
428
429
|
async findActiveBrands() {
|
|
429
430
|
const snap = await this.db
|
|
430
431
|
.collection(this.collection)
|
|
431
|
-
.where(
|
|
432
|
-
.where(
|
|
433
|
-
.orderBy(
|
|
432
|
+
.where(CATEGORY_FIELDS.CATEGORY_TYPE, "==", "brand")
|
|
433
|
+
.where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
|
|
434
|
+
.orderBy(CATEGORY_FIELDS.ORDER, "asc")
|
|
434
435
|
.get();
|
|
435
436
|
return snap.docs.map((d) => this.mapDoc(d));
|
|
436
437
|
}
|