@mohasinac/appkit 2.7.45 → 2.7.47
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/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +1 -0
- package/dist/core/unit-of-work.js +3 -4
- package/dist/features/account/components/AddressForm.js +23 -1
- package/dist/features/admin/components/AdminBlogEditorView.js +18 -11
- package/dist/features/admin/components/AdminBrandEditorView.js +18 -11
- package/dist/features/admin/components/AdminCarouselEditorView.js +18 -8
- package/dist/features/admin/components/AdminCategoryEditorView.js +18 -11
- package/dist/features/admin/components/AdminCouponEditorView.js +18 -11
- package/dist/features/admin/components/AdminFaqEditorView.js +18 -11
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +18 -11
- package/dist/features/auth/actions/profile-actions.js +4 -1
- package/dist/features/auth/repository/user.repository.d.ts +1 -0
- package/dist/features/auth/repository/user.repository.js +3 -0
- package/dist/features/blog/api/route.js +3 -0
- package/dist/features/blog/components/BlogIndexListing.js +2 -0
- package/dist/features/blog/components/BlogPostView.js +2 -1
- package/dist/features/categories/api/route.js +1 -1
- package/dist/features/categories/components/BundleDetailView.js +5 -3
- package/dist/features/categories/components/CategoryBundlesListing.js +77 -41
- package/dist/features/categories/components/CategoryDetailPageView.js +12 -9
- package/dist/features/categories/components/CategoryProductsListing.js +3 -5
- package/dist/features/classified/components/ClassifiedIndexListing.js +3 -4
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +3 -4
- package/dist/features/events/actions/event-actions.d.ts +2 -3
- package/dist/features/events/actions/event-actions.js +3 -5
- package/dist/features/events/api/route.js +7 -7
- package/dist/features/events/components/AdminEventEditorView.js +80 -6
- package/dist/features/events/components/AdminEventEntriesView.js +33 -4
- package/dist/features/events/components/EventFilters.d.ts +3 -0
- package/dist/features/events/components/EventFilters.js +1 -0
- package/dist/features/events/components/EventLeaderboard.js +1 -1
- package/dist/features/events/components/EventsIndexListing.js +10 -7
- package/dist/features/events/repository/event-entry.repository.d.ts +2 -2
- package/dist/features/events/repository/event-entry.repository.js +32 -7
- package/dist/features/events/types/index.d.ts +3 -1
- package/dist/features/homepage/components/BrandsSection.js +2 -2
- package/dist/features/homepage/components/CustomCardsSection.js +1 -1
- package/dist/features/homepage/components/CustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/live/components/LiveItemsIndexListing.js +3 -4
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +3 -4
- package/dist/features/products/components/AuctionsIndexListing.js +3 -4
- package/dist/features/products/components/PrizeDrawsIndexListing.js +3 -8
- package/dist/features/products/components/ProductDetailPageView.js +17 -2
- package/dist/features/products/components/ProductsIndexListing.js +10 -6
- package/dist/features/products/schemas/firestore.d.ts +2 -0
- package/dist/features/reviews/actions/review-actions.js +2 -0
- package/dist/features/reviews/components/ReviewDetailShell.js +3 -2
- package/dist/features/reviews/components/ReviewFilters.js +1 -1
- package/dist/features/reviews/components/ReviewsIndexListing.js +9 -2
- package/dist/features/reviews/hooks/useReviews.js +2 -0
- package/dist/features/reviews/schemas/firestore.d.ts +4 -0
- package/dist/features/reviews/types/index.d.ts +3 -0
- package/dist/features/scams/components/ScamRegistryView.js +41 -8
- package/dist/features/seller/components/SellerProductShell.js +27 -3
- package/dist/features/shell/StepForm.d.ts +6 -2
- package/dist/features/shell/StepForm.js +13 -10
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +3 -4
- package/dist/features/stores/components/StoreDetailLayoutView.js +13 -5
- package/dist/features/stores/components/StorePreOrdersListing.js +3 -4
- package/dist/features/stores/components/StoreProductsListing.js +3 -4
- package/dist/index.d.ts +1 -0
- package/dist/providers/db-firebase/helpers.d.ts +4 -6
- package/dist/providers/db-firebase/helpers.js +18 -5
- package/dist/providers/db-firebase/sieve.js +26 -1
- package/dist/react/hooks/usePendingFilters.d.ts +2 -0
- package/dist/react/hooks/usePendingFilters.js +9 -0
- package/dist/react/hooks/usePendingTable.d.ts +5 -0
- package/dist/react/hooks/usePendingTable.js +1 -0
- package/dist/seed/homepage-sections-seed-data.js +2 -2
- package/dist/seed/products-standard-seed-data.js +1 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Drawer.style.css +13 -0
- package/dist/ui/components/FilterDrawer.d.ts +4 -2
- package/dist/ui/components/FilterDrawer.js +2 -2
- package/dist/ui/components/Toast.d.ts +4 -1
- package/dist/ui/components/Toast.js +3 -3
- package/dist/ui/forms/FormShell.d.ts +19 -1
- package/dist/ui/forms/FormShell.js +92 -0
- package/dist/ui/forms/index.d.ts +2 -2
- package/dist/ui/forms/index.js +1 -1
- package/package.json +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cache } from "react";
|
|
3
|
-
import { storeRepository, productRepository, categoriesRepository, siteSettingsRepository } from "../../../repositories";
|
|
3
|
+
import { storeRepository, productRepository, categoriesRepository, siteSettingsRepository, reviewRepository, couponsRepository } from "../../../repositories";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
5
|
import { Container, Main, Section, Text } from "../../../ui";
|
|
6
6
|
import { STORE_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
@@ -27,7 +27,7 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
|
|
|
27
27
|
}
|
|
28
28
|
const storeId = store?.id;
|
|
29
29
|
const settings = await siteSettingsRepository.findById("global").catch(() => null);
|
|
30
|
-
const [productsCount, auctionsCount, preOrdersCount, prizeDrawsCount, bundlesCount] = storeId
|
|
30
|
+
const [productsCount, auctionsCount, preOrdersCount, prizeDrawsCount, bundlesCount, couponsCount, reviewsCount] = storeId
|
|
31
31
|
? await Promise.all([
|
|
32
32
|
productRepository
|
|
33
33
|
.list({ filters: `storeId==${storeId},status==published,listingType==standard`, page: 1, pageSize: 1 })
|
|
@@ -51,8 +51,16 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
|
|
|
51
51
|
.listByType("bundle", { activeOnly: true, limit: 100 })
|
|
52
52
|
.then((rows) => rows.filter((c) => c.createdByStoreId === storeId).length)
|
|
53
53
|
.catch(() => 0),
|
|
54
|
+
couponsRepository
|
|
55
|
+
.list({ filters: `sellerId==${storeId},validity.isActive==true`, page: 1, pageSize: 1 })
|
|
56
|
+
.then((r) => r.total)
|
|
57
|
+
.catch(() => 0),
|
|
58
|
+
reviewRepository
|
|
59
|
+
.listAll({ filters: `storeId==${storeId},status==approved`, page: 1, pageSize: 1 })
|
|
60
|
+
.then((r) => r.total)
|
|
61
|
+
.catch(() => 0),
|
|
54
62
|
])
|
|
55
|
-
: [0, 0, 0, 0, 0];
|
|
63
|
+
: [0, 0, 0, 0, 0, 0, 0];
|
|
56
64
|
const listingCounts = {
|
|
57
65
|
products: productsCount,
|
|
58
66
|
auctions: auctionsCount,
|
|
@@ -78,8 +86,8 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
|
|
|
78
86
|
label: tabLabel(tab.label, listingCounts[tab.id]),
|
|
79
87
|
href: STORE_LISTING_HREF[tab.id](storeSlug),
|
|
80
88
|
})),
|
|
81
|
-
{ value: "coupons", label: "Coupons", href: String(ROUTES.PUBLIC.STORE_COUPONS(storeSlug)) },
|
|
82
|
-
{ value: "reviews", label: "Reviews", href: String(ROUTES.PUBLIC.STORE_REVIEWS(storeSlug)) },
|
|
89
|
+
{ value: "coupons", label: tabLabel("Coupons", couponsCount), href: String(ROUTES.PUBLIC.STORE_COUPONS(storeSlug)) },
|
|
90
|
+
{ value: "reviews", label: tabLabel("Reviews", reviewsCount), href: String(ROUTES.PUBLIC.STORE_REVIEWS(storeSlug)) },
|
|
83
91
|
{ value: "about", label: "About", href: String(ROUTES.PUBLIC.STORE_ABOUT(storeSlug)) },
|
|
84
92
|
];
|
|
85
93
|
return (_jsxs(Main, { children: [_jsx(StoreHeader, { store: store }), _jsxs(Container, { size: "xl", className: "mt-6", children: [_jsx(StoreNavTabs, { tabs: tabs, activeValue: activeTab }), _jsx(Section, { className: "pt-6", children: children })] })] }));
|
|
@@ -24,7 +24,7 @@ export function StorePreOrdersListing({ storeId, initialData }) {
|
|
|
24
24
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
25
25
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
26
26
|
const [view, setView] = useState(table.get("view") || "grid");
|
|
27
|
-
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
27
|
+
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onResetAll, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
28
28
|
const openFilters = useCallback(() => {
|
|
29
29
|
onFilterReset();
|
|
30
30
|
setFilterOpen(true);
|
|
@@ -34,10 +34,9 @@ export function StorePreOrdersListing({ storeId, initialData }) {
|
|
|
34
34
|
setFilterOpen(false);
|
|
35
35
|
}, [onFilterApply]);
|
|
36
36
|
const resetAll = useCallback(() => {
|
|
37
|
-
|
|
38
|
-
onFilterClear();
|
|
37
|
+
onResetAll({ q: "", sort: "" });
|
|
39
38
|
setSearchInput("");
|
|
40
|
-
}, [
|
|
39
|
+
}, [onResetAll]);
|
|
41
40
|
const hasActiveState = !!table.get("q") ||
|
|
42
41
|
table.get("sort") !== DEFAULT_SORT ||
|
|
43
42
|
filterActiveCount > 0;
|
|
@@ -24,7 +24,7 @@ export function StoreProductsListing({ storeId, initialData }) {
|
|
|
24
24
|
const localCart = useGuestCart();
|
|
25
25
|
const localWishlist = useGuestWishlist();
|
|
26
26
|
const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "product").map((i) => i.itemId));
|
|
27
|
-
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
27
|
+
const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onResetAll, onFilterReset } = usePendingTable(table, FILTER_KEYS);
|
|
28
28
|
const openFilters = useCallback(() => {
|
|
29
29
|
onFilterReset();
|
|
30
30
|
setFilterOpen(true);
|
|
@@ -34,10 +34,9 @@ export function StoreProductsListing({ storeId, initialData }) {
|
|
|
34
34
|
setFilterOpen(false);
|
|
35
35
|
}, [onFilterApply]);
|
|
36
36
|
const resetAll = useCallback(() => {
|
|
37
|
-
|
|
38
|
-
onFilterClear();
|
|
37
|
+
onResetAll({ q: "", sort: "" });
|
|
39
38
|
setSearchInput("");
|
|
40
|
-
}, [
|
|
39
|
+
}, [onResetAll]);
|
|
41
40
|
const hasActiveState = !!table.get("q") ||
|
|
42
41
|
table.get("sort") !== "-createdAt" ||
|
|
43
42
|
filterActiveCount > 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1761,6 +1761,7 @@ export { EventRaffleWinnerView } from "./features/events/index";
|
|
|
1761
1761
|
export type { EventRaffleWinnerViewProps } from "./features/events/index";
|
|
1762
1762
|
export { SpinWheelView } from "./features/events/index";
|
|
1763
1763
|
export type { SpinWheelViewProps } from "./features/events/index";
|
|
1764
|
+
export type { SpinPrize } from "./features/events/index";
|
|
1764
1765
|
export { EventsListView } from "./features/events/index";
|
|
1765
1766
|
export { EventsIndexListing } from "./features/events/index";
|
|
1766
1767
|
export type { EventsIndexListingProps } from "./features/events/index";
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import type { CollectionReference, DocumentData, Query } from "firebase-admin/firestore";
|
|
2
|
-
/**
|
|
3
|
-
* Firestore serialisation helpers.
|
|
4
|
-
*
|
|
5
|
-
* These utilities are the single adapter boundary between raw Firestore
|
|
6
|
-
* documents and application-level objects.
|
|
7
|
-
*/
|
|
8
2
|
/**
|
|
9
3
|
* Recursively remove `undefined` values from an object.
|
|
10
4
|
* Firestore rejects writes that contain `undefined` fields.
|
|
5
|
+
*
|
|
6
|
+
* Non-plain values (Date, FieldValue sentinels, class instances, Arrays)
|
|
7
|
+
* are passed through as-is so that Firestore transform operations
|
|
8
|
+
* (increment, arrayUnion, deleteField, serverTimestamp) survive the strip.
|
|
11
9
|
*/
|
|
12
10
|
export declare function removeUndefined<T extends Record<string, unknown>>(obj: T): Partial<T>;
|
|
13
11
|
/**
|
|
@@ -5,9 +5,26 @@
|
|
|
5
5
|
* documents and application-level objects.
|
|
6
6
|
*/
|
|
7
7
|
// --- Undefined removal --------------------------------------------------------
|
|
8
|
+
/**
|
|
9
|
+
* Returns true only for plain objects ({}) — NOT class instances, Dates,
|
|
10
|
+
* Arrays, FieldValue sentinels, or Timestamps. Only plain objects are safe
|
|
11
|
+
* to recurse into; everything else must pass through unchanged so Firestore
|
|
12
|
+
* FieldValue sentinels (increment, arrayUnion, deleteField, etc.) are not
|
|
13
|
+
* stripped.
|
|
14
|
+
*/
|
|
15
|
+
function isPlainObject(v) {
|
|
16
|
+
if (v === null || typeof v !== "object" || Array.isArray(v))
|
|
17
|
+
return false;
|
|
18
|
+
const proto = Object.getPrototypeOf(v);
|
|
19
|
+
return proto === Object.prototype || proto === null;
|
|
20
|
+
}
|
|
8
21
|
/**
|
|
9
22
|
* Recursively remove `undefined` values from an object.
|
|
10
23
|
* Firestore rejects writes that contain `undefined` fields.
|
|
24
|
+
*
|
|
25
|
+
* Non-plain values (Date, FieldValue sentinels, class instances, Arrays)
|
|
26
|
+
* are passed through as-is so that Firestore transform operations
|
|
27
|
+
* (increment, arrayUnion, deleteField, serverTimestamp) survive the strip.
|
|
11
28
|
*/
|
|
12
29
|
export function removeUndefined(obj) {
|
|
13
30
|
const result = {};
|
|
@@ -15,11 +32,7 @@ export function removeUndefined(obj) {
|
|
|
15
32
|
const value = obj[key];
|
|
16
33
|
if (value === undefined)
|
|
17
34
|
continue;
|
|
18
|
-
if (value
|
|
19
|
-
typeof value === "object" &&
|
|
20
|
-
!Array.isArray(value) &&
|
|
21
|
-
!(value instanceof Date) &&
|
|
22
|
-
!value.constructor?.name?.includes("Timestamp")) {
|
|
35
|
+
if (isPlainObject(value)) {
|
|
23
36
|
const cleaned = removeUndefined(value);
|
|
24
37
|
if (Object.keys(cleaned).length > 0) {
|
|
25
38
|
result[key] = cleaned;
|
|
@@ -19,6 +19,31 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { SieveProcessorBase } from "@mohasinac/sievejs/services";
|
|
21
21
|
import { createFirebaseAdapter } from "@mohasinac/sievejs/adapters/firebase";
|
|
22
|
+
/**
|
|
23
|
+
* Wraps the stock Firebase adapter and upgrades OR groups where all conditions
|
|
24
|
+
* are equality checks on the same field into a Firestore `in` query.
|
|
25
|
+
*
|
|
26
|
+
* Background: Sieve parses `field==v1|v2` as a single term with two values
|
|
27
|
+
* (OR semantics). The stock adapter tries `whereOr` which Firestore Admin SDK
|
|
28
|
+
* doesn't support. Firestore's `.where(field, "in", [v1, v2])` is the correct
|
|
29
|
+
* equivalent and supports up to 30 values.
|
|
30
|
+
*/
|
|
31
|
+
function createEnhancedFirebaseAdapter() {
|
|
32
|
+
const base = createFirebaseAdapter();
|
|
33
|
+
return {
|
|
34
|
+
...base,
|
|
35
|
+
applyFilterGroup(query, group) {
|
|
36
|
+
if (group.length > 1 &&
|
|
37
|
+
group.every((c) => c.parsedOperator === "equals" &&
|
|
38
|
+
!c.operatorIsNegated &&
|
|
39
|
+
!c.operatorIsCaseInsensitive &&
|
|
40
|
+
c.field === group[0].field)) {
|
|
41
|
+
return query.where(group[0].field, "in", group.map((c) => c.value));
|
|
42
|
+
}
|
|
43
|
+
return base.applyFilterGroup(query, group);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
22
47
|
import { FirebaseRepository } from "./base";
|
|
23
48
|
import { deserializeTimestamps, getFirestoreCount } from "./helpers";
|
|
24
49
|
import { expandFilterAliases as _expandFilterAliases, } from "./filter-aliases";
|
|
@@ -90,7 +115,7 @@ export class FirebaseSieveRepository extends FirebaseRepository {
|
|
|
90
115
|
const base = baseQuery ?? this.getCollection();
|
|
91
116
|
const effective = withAliasesExpanded(model, aliases);
|
|
92
117
|
const processor = new SieveProcessorBase({
|
|
93
|
-
adapter:
|
|
118
|
+
adapter: createEnhancedFirebaseAdapter(),
|
|
94
119
|
autoLoadConfig: false,
|
|
95
120
|
options: merged,
|
|
96
121
|
fields,
|
|
@@ -25,6 +25,8 @@ export interface UsePendingFiltersReturn {
|
|
|
25
25
|
reset: () => void;
|
|
26
26
|
/** Clear all keys in both pending state and the URL */
|
|
27
27
|
clear: () => void;
|
|
28
|
+
/** Clear filter keys + extra URL keys in a single router.replace — avoids the double-replace race */
|
|
29
|
+
clearAll: (extras?: Record<string, string>) => void;
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
30
32
|
* usePendingFilters
|
|
@@ -65,6 +65,14 @@ export function usePendingFilters({ table, keys, }) {
|
|
|
65
65
|
updates[k] = "";
|
|
66
66
|
table.setMany(updates);
|
|
67
67
|
}, [keys, table]);
|
|
68
|
+
const clearAll = useCallback((extras) => {
|
|
69
|
+
const empty = Object.fromEntries(keys.map((k) => [k, []]));
|
|
70
|
+
setPending(empty);
|
|
71
|
+
const updates = { page: "1", ...extras };
|
|
72
|
+
for (const k of keys)
|
|
73
|
+
updates[k] = "";
|
|
74
|
+
table.setMany(updates);
|
|
75
|
+
}, [keys, table]);
|
|
68
76
|
return {
|
|
69
77
|
pending,
|
|
70
78
|
applied,
|
|
@@ -75,5 +83,6 @@ export function usePendingFilters({ table, keys, }) {
|
|
|
75
83
|
apply,
|
|
76
84
|
reset,
|
|
77
85
|
clear,
|
|
86
|
+
clearAll,
|
|
78
87
|
};
|
|
79
88
|
}
|
|
@@ -17,6 +17,11 @@ export interface UsePendingTableReturn {
|
|
|
17
17
|
onFilterApply: () => void;
|
|
18
18
|
/** Clear all filter keys from pending state and URL */
|
|
19
19
|
onFilterClear: () => void;
|
|
20
|
+
/**
|
|
21
|
+
* Clear filter keys + extra URL keys in a SINGLE router.replace.
|
|
22
|
+
* Use this in resetAll handlers to avoid the double-replace race condition.
|
|
23
|
+
*/
|
|
24
|
+
onResetAll: (extras?: Record<string, string>) => void;
|
|
20
25
|
/**
|
|
21
26
|
* Revert pending state to the current URL values without clearing them.
|
|
22
27
|
* Call this when opening a filter drawer to ensure it shows the last
|
|
@@ -327,9 +327,9 @@ export const homepageSectionsSeedData = [
|
|
|
327
327
|
/** Which tabs to surface in display order (leave empty [] to auto-derive from categories) */
|
|
328
328
|
visibleTabs: ["general", "orders_payment", "shipping_delivery", "returns_refunds"],
|
|
329
329
|
/** Allow multiple accordion panels open at once */
|
|
330
|
-
allowMultipleOpen:
|
|
330
|
+
allowMultipleOpen: true,
|
|
331
331
|
/** Number of items expanded on first render (0 = all closed) */
|
|
332
|
-
defaultOpenCount:
|
|
332
|
+
defaultOpenCount: 100,
|
|
333
333
|
categories: ["general", "orders_payment", "shipping_delivery", "returns_refunds", "product_information"],
|
|
334
334
|
},
|
|
335
335
|
createdAt: daysAgo(90),
|
|
@@ -4134,6 +4134,7 @@ export const productsStandardSeedData = [
|
|
|
4134
4134
|
..._rawProductsStandardSeedData.map((p) => ({
|
|
4135
4135
|
...p,
|
|
4136
4136
|
listingType: "standard",
|
|
4137
|
+
isPartOfBundle: Array.isArray(p.partOfBundleIds) && p.partOfBundleIds.length > 0,
|
|
4137
4138
|
})),
|
|
4138
4139
|
// ── SB-UNI-M classified seed (OLX-style meetup) ──────────────────────────
|
|
4139
4140
|
{
|