@mohasinac/appkit 2.7.12 → 2.7.13

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.
@@ -14,31 +14,24 @@ const UNAVAILABLE = new Set([
14
14
  PRODUCT_FIELDS.STATUS_VALUES.OUT_OF_STOCK,
15
15
  PRODUCT_FIELDS.STATUS_VALUES.DISCONTINUED,
16
16
  ]);
17
- const MIN_ACTIVE_DEFAULT = 1;
18
- async function computeBundleStockStatus(productIds, minActive, ctx) {
17
+ async function computeBundleStockStatus(productIds, ctx) {
19
18
  if (productIds.length === 0)
20
19
  return "out_of_stock";
21
- let unavailable = 0;
22
20
  for (let i = 0; i < productIds.length; i += 30) {
23
21
  const chunk = productIds.slice(i, i + 30);
24
22
  const snap = await ctx.db
25
23
  .collection(PRODUCT_COLLECTION)
26
24
  .where("__name__", "in", chunk)
27
25
  .get();
26
+ if (snap.size < chunk.length)
27
+ return "out_of_stock";
28
28
  for (const doc of snap.docs) {
29
29
  const status = doc.data().status;
30
30
  if (!status || UNAVAILABLE.has(status))
31
- unavailable++;
31
+ return "out_of_stock";
32
32
  }
33
- if (snap.size < chunk.length)
34
- unavailable += chunk.length - snap.size;
35
33
  }
36
- const active = productIds.length - unavailable;
37
- if (unavailable === 0)
38
- return "in_stock";
39
- if (active < minActive)
40
- return "out_of_stock";
41
- return "partial";
34
+ return "in_stock";
42
35
  }
43
36
  export async function runBundleStockSync(ctx) {
44
37
  ctx.logger.info("Bundle stock sync starting (SB-UNI-V categories)");
@@ -58,8 +51,7 @@ export async function runBundleStockSync(ctx) {
58
51
  const productIds = data.bundleProductIds ?? [];
59
52
  if (productIds.length === 0)
60
53
  continue;
61
- const minActive = data.minActiveMembers ?? MIN_ACTIVE_DEFAULT;
62
- const nextStatus = await computeBundleStockStatus(productIds, minActive, ctx);
54
+ const nextStatus = await computeBundleStockStatus(productIds, ctx);
63
55
  if (nextStatus !== data.bundleStockStatus) {
64
56
  await bundleDoc.ref.update({
65
57
  [CATEGORY_FIELDS.BUNDLE_STOCK_STATUS]: nextStatus,
@@ -25,30 +25,24 @@ export function isAvailable(status) {
25
25
  return false;
26
26
  return !UNAVAILABLE_STATUSES.has(status);
27
27
  }
28
- async function recomputeBundleStatus(productIds, minActive, ctx) {
28
+ async function recomputeBundleStatus(productIds, ctx) {
29
29
  if (productIds.length === 0)
30
30
  return "out_of_stock";
31
- let unavailable = 0;
32
31
  for (let i = 0; i < productIds.length; i += 30) {
33
32
  const chunk = productIds.slice(i, i + 30);
34
33
  const snap = await ctx.db
35
34
  .collection(PRODUCT_COLLECTION)
36
35
  .where("__name__", "in", chunk)
37
36
  .get();
37
+ if (snap.size < chunk.length)
38
+ return "out_of_stock";
38
39
  for (const doc of snap.docs) {
39
40
  const status = doc.data().status;
40
41
  if (!isAvailable(status))
41
- unavailable++;
42
+ return "out_of_stock";
42
43
  }
43
- if (snap.size < chunk.length)
44
- unavailable += chunk.length - snap.size;
45
44
  }
46
- const active = productIds.length - unavailable;
47
- if (unavailable === 0)
48
- return "in_stock";
49
- if (active < minActive)
50
- return "out_of_stock";
51
- return "partial";
45
+ return "in_stock";
52
46
  }
53
47
  export async function syncBundlesForProduct(productId, ctx) {
54
48
  const snap = await ctx.db
@@ -60,8 +54,7 @@ export async function syncBundlesForProduct(productId, ctx) {
60
54
  for (const bundleDoc of snap.docs) {
61
55
  const data = bundleDoc.data();
62
56
  const ids = data.bundleProductIds ?? [];
63
- const minActive = data.minActiveMembers ?? 1;
64
- const next = await recomputeBundleStatus(ids, minActive, ctx);
57
+ const next = await recomputeBundleStatus(ids, ctx);
65
58
  if (next !== data.bundleStockStatus) {
66
59
  await bundleDoc.ref.update({
67
60
  [CATEGORY_FIELDS.BUNDLE_STOCK_STATUS]: next,
@@ -108,16 +108,13 @@ export declare const BUNDLE_COPY: {
108
108
  };
109
109
  readonly stockBadge: {
110
110
  readonly in_stock: "In stock";
111
- readonly partial: "Some items unavailable";
112
- readonly out_of_stock: "Currently out of stock";
111
+ readonly out_of_stock: "Not active";
113
112
  readonly listVariantInStock: "In stock";
114
- readonly listVariantPartial: "Partial";
115
- readonly listVariantOutOfStock: "Out of stock";
113
+ readonly listVariantOutOfStock: "Not active";
116
114
  };
117
115
  };
118
116
  /** Stock-status variant mapping (kept here so consumers can drive Badge variant prop). */
119
117
  export declare const BUNDLE_STOCK_VARIANT: {
120
118
  readonly in_stock: "success";
121
- readonly partial: "warning";
122
- readonly out_of_stock: "danger";
119
+ readonly out_of_stock: "warning";
123
120
  };
@@ -119,16 +119,13 @@ export const BUNDLE_COPY = {
119
119
  // Stock badges (shared between list + featured + detail)
120
120
  stockBadge: {
121
121
  in_stock: "In stock",
122
- partial: "Some items unavailable",
123
- out_of_stock: "Currently out of stock",
122
+ out_of_stock: "Not active",
124
123
  listVariantInStock: "In stock",
125
- listVariantPartial: "Partial",
126
- listVariantOutOfStock: "Out of stock",
124
+ listVariantOutOfStock: "Not active",
127
125
  },
128
126
  };
129
127
  /** Stock-status variant mapping (kept here so consumers can drive Badge variant prop). */
130
128
  export const BUNDLE_STOCK_VARIANT = {
131
129
  in_stock: "success",
132
- partial: "warning",
133
- out_of_stock: "danger",
130
+ out_of_stock: "warning",
134
131
  };
@@ -363,11 +363,14 @@ export declare const STORE_FIELDS: {
363
363
  readonly OWNER_ID: "ownerId";
364
364
  readonly STORE_NAME: "storeName";
365
365
  readonly SLUG: "slug";
366
+ readonly STORE_SLUG: "storeSlug";
367
+ readonly STORE_CATEGORY: "storeCategory";
366
368
  readonly DESCRIPTION: "storeDescription";
367
369
  readonly LOGO_URL: "storeLogoURL";
368
370
  readonly BANNER_URL: "storeBannerURL";
369
371
  readonly STATUS: "status";
370
372
  readonly IS_VERIFIED: "isVerified";
373
+ readonly IS_PUBLIC: "isPublic";
371
374
  readonly SHIPPING_CONFIG: "shippingConfig";
372
375
  readonly PAYOUT_DETAILS: "payoutDetails";
373
376
  readonly STATS: "stats";
@@ -379,6 +382,13 @@ export declare const STORE_FIELDS: {
379
382
  readonly SUSPENDED: "suspended";
380
383
  readonly REJECTED: "rejected";
381
384
  };
385
+ readonly STATS_FIELDS: {
386
+ readonly ITEMS_SOLD: "stats.itemsSold";
387
+ readonly AVERAGE_RATING: "stats.averageRating";
388
+ readonly TOTAL_REVIEWS: "stats.totalReviews";
389
+ readonly TOTAL_ORDERS: "stats.totalOrders";
390
+ readonly TOTAL_PRODUCTS: "stats.totalProducts";
391
+ };
382
392
  };
383
393
  export declare const CATEGORY_FIELDS: {
384
394
  readonly ID: "id";
@@ -633,6 +643,7 @@ export declare const COUPON_FIELDS: {
633
643
  readonly TYPE: "type";
634
644
  readonly SCOPE: "scope";
635
645
  readonly SELLER_ID: "sellerId";
646
+ readonly STORE_ID: "storeId";
636
647
  readonly STORE_SLUG: "storeSlug";
637
648
  readonly APPLICABLE_TO_AUCTIONS: "applicableToAuctions";
638
649
  readonly DISCOUNT: "discount";
@@ -400,11 +400,14 @@ export const STORE_FIELDS = {
400
400
  OWNER_ID: "ownerId",
401
401
  STORE_NAME: "storeName",
402
402
  SLUG: "slug",
403
+ STORE_SLUG: "storeSlug",
404
+ STORE_CATEGORY: "storeCategory",
403
405
  DESCRIPTION: "storeDescription",
404
406
  LOGO_URL: "storeLogoURL",
405
407
  BANNER_URL: "storeBannerURL",
406
408
  STATUS: "status",
407
409
  IS_VERIFIED: "isVerified",
410
+ IS_PUBLIC: "isPublic",
408
411
  SHIPPING_CONFIG: "shippingConfig",
409
412
  PAYOUT_DETAILS: "payoutDetails",
410
413
  STATS: "stats",
@@ -416,6 +419,13 @@ export const STORE_FIELDS = {
416
419
  SUSPENDED: "suspended",
417
420
  REJECTED: "rejected",
418
421
  },
422
+ STATS_FIELDS: {
423
+ ITEMS_SOLD: "stats.itemsSold",
424
+ AVERAGE_RATING: "stats.averageRating",
425
+ TOTAL_REVIEWS: "stats.totalReviews",
426
+ TOTAL_ORDERS: "stats.totalOrders",
427
+ TOTAL_PRODUCTS: "stats.totalProducts",
428
+ },
419
429
  };
420
430
  // ============================================================================
421
431
  // CATEGORY FIELDS
@@ -720,6 +730,7 @@ export const COUPON_FIELDS = {
720
730
  TYPE: "type",
721
731
  SCOPE: "scope",
722
732
  SELLER_ID: "sellerId",
733
+ STORE_ID: "storeId",
723
734
  STORE_SLUG: "storeSlug",
724
735
  APPLICABLE_TO_AUCTIONS: "applicableToAuctions",
725
736
  DISCOUNT: "discount",
@@ -17,7 +17,6 @@ import { Badge, Button, Container, Div, Heading, Row, Section, Stack, Text, } fr
17
17
  import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy";
18
18
  const STOCK_LIST_LABEL = {
19
19
  in_stock: BUNDLE_COPY.stockBadge.listVariantInStock,
20
- partial: BUNDLE_COPY.stockBadge.listVariantPartial,
21
20
  out_of_stock: BUNDLE_COPY.stockBadge.listVariantOutOfStock,
22
21
  };
23
22
  function formatPrice(paise) {
@@ -7,7 +7,6 @@ import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/fe
7
7
  import { BundleBuyNowCta } from "./BundleBuyNowCta";
8
8
  const STOCK_BADGE_TEXT = {
9
9
  in_stock: BUNDLE_COPY.stockBadge.in_stock,
10
- partial: BUNDLE_COPY.stockBadge.partial,
11
10
  out_of_stock: BUNDLE_COPY.stockBadge.out_of_stock,
12
11
  };
13
12
  const PLACEHOLDER_EMOJI = "📦";
@@ -19,13 +19,11 @@ const SORT_OPTIONS = [
19
19
  ];
20
20
  const STOCK_BADGE_TEXT = {
21
21
  in_stock: "",
22
- partial: "Partial",
23
- out_of_stock: "Out of stock",
22
+ out_of_stock: "Not active",
24
23
  };
25
24
  const STOCK_BADGE_VARIANT = {
26
25
  in_stock: "success",
27
- partial: "warning",
28
- out_of_stock: "danger",
26
+ out_of_stock: "warning",
29
27
  };
30
28
  const PLACEHOLDER_EMOJI = "📦";
31
29
  const COPY = {
@@ -105,7 +105,7 @@ export interface CategoryDocument {
105
105
  /** Rule resolving the bundle's member products — static list or live query. */
106
106
  bundleQueryRule?: BundleQueryRule;
107
107
  /** Snapshot stock state — recomputed by onProductStockChange. */
108
- bundleStockStatus?: "in_stock" | "partial" | "out_of_stock";
108
+ bundleStockStatus?: "in_stock" | "out_of_stock";
109
109
  /** Timestamp of the last dynamic-rule resolution. */
110
110
  bundleQueryResolvedAt?: Date;
111
111
  /** Hand-picked products list (mirror of bundleQueryRule for static rules); kept for index-friendly queries. */
@@ -18,7 +18,5 @@ function FeaturedBundleCard({ bundle }) {
18
18
  // eslint-disable-next-line @next/next/no-img-element, lir/no-raw-media-elements
19
19
  _jsx("img", { src: cover, alt: bundle.name, className: "h-full w-full object-cover transition-transform group-hover:scale-105", loading: "lazy" })) : (_jsx(Div, { className: "flex h-full w-full items-center justify-center text-3xl", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2 text-sm font-semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1 flex-wrap", children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
20
20
  ? formatCurrency(bundle.bundlePriceInPaise / 100, "INR")
21
- : BUNDLE_COPY.featured.priceFallback }), _jsxs(Text, { size: "xs", color: "muted", children: ["\u00B7 ", BUNDLE_COPY.featured.itemCount(memberCount)] }), stock !== "in_stock" && (_jsx(Badge, { variant: BUNDLE_STOCK_VARIANT[stock], children: stock === "partial"
22
- ? BUNDLE_COPY.stockBadge.listVariantPartial
23
- : BUNDLE_COPY.stockBadge.listVariantOutOfStock }))] })] }));
21
+ : BUNDLE_COPY.featured.priceFallback }), _jsxs(Text, { size: "xs", color: "muted", children: ["\u00B7 ", BUNDLE_COPY.featured.itemCount(memberCount)] }), stock !== "in_stock" && (_jsx(Badge, { variant: BUNDLE_STOCK_VARIANT[stock], children: BUNDLE_COPY.stockBadge.listVariantOutOfStock }))] })] }));
24
22
  }
@@ -1,19 +1,23 @@
1
+ import { PRODUCT_FIELDS } from "../../../constants/field-names";
2
+ const { LISTING_TYPE, STATUS, FEATURED, CREATED_AT } = PRODUCT_FIELDS;
3
+ const { PRE_ORDER } = PRODUCT_FIELDS.LISTING_TYPE_VALUES;
4
+ const { PUBLISHED } = PRODUCT_FIELDS.STATUS_VALUES;
1
5
  export class PreordersRepository {
2
6
  constructor(repo) {
3
7
  this.repo = repo;
4
8
  }
5
9
  async findPreorders() {
6
10
  const result = await this.repo.findAll({
7
- filters: "isPreorder==true,active==true",
8
- sort: "createdAt",
11
+ filters: `${LISTING_TYPE}==${PRE_ORDER},${STATUS}==${PUBLISHED}`,
12
+ sort: CREATED_AT,
9
13
  order: "desc",
10
14
  });
11
15
  return result.data;
12
16
  }
13
17
  async findFeaturedPreorders() {
14
18
  const result = await this.repo.findAll({
15
- filters: "isPreorder==true,active==true,isFeatured==true",
16
- sort: "createdAt",
19
+ filters: `${LISTING_TYPE}==${PRE_ORDER},${STATUS}==${PUBLISHED},${FEATURED}==true`,
20
+ sort: CREATED_AT,
17
21
  order: "desc",
18
22
  });
19
23
  return result.data;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
3
+ import { useState, useCallback, useMemo } from "react";
4
4
  import { Search, SlidersHorizontal, X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { Pagination, SortDropdown, Div, Text, Heading } from "../../../ui";
@@ -13,34 +13,55 @@ const DEFAULT_SORT = sortBy(COUPON_FIELDS.CREATED_AT);
13
13
  const COUPON_SORT_OPTIONS = [
14
14
  { value: sortBy(COUPON_FIELDS.NAME, "ASC"), label: "Name A–Z" },
15
15
  { value: sortBy(COUPON_FIELDS.NAME), label: "Name Z–A" },
16
- { value: "-validity.endDate", label: "Expiring Soon" },
16
+ { value: sortBy(COUPON_FIELDS.VALIDITY_FIELDS.END_DATE, "ASC"), label: "Expiring Soon" },
17
17
  { value: sortBy(COUPON_FIELDS.CREATED_AT), label: "Newest First" },
18
18
  { value: sortBy(COUPON_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
19
19
  ];
20
20
  const COUPON_TYPES = [
21
- { value: "percentage", label: "% Off" },
22
- { value: "fixed", label: "Fixed Amount" },
23
- { value: "free_shipping", label: "Free Shipping" },
24
- { value: "buy_x_get_y", label: "Buy X Get Y" },
21
+ { value: COUPON_FIELDS.TYPE_VALUES.PERCENTAGE, label: "% Off" },
22
+ { value: COUPON_FIELDS.TYPE_VALUES.FIXED, label: "Fixed Amount" },
23
+ { value: COUPON_FIELDS.TYPE_VALUES.FREE_SHIPPING, label: "Free Shipping" },
24
+ { value: COUPON_FIELDS.TYPE_VALUES.BUY_X_GET_Y, label: "Buy X Get Y" },
25
25
  ];
26
+ const FILTER_KEYS = [TABLE_KEYS.TYPE, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
26
27
  export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
27
28
  const table = useUrlTable({ defaults: { pageSize: "12", sort: DEFAULT_SORT } });
28
29
  const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
29
30
  const [filterOpen, setFilterOpen] = useState(false);
30
- // Build Sieve filter string
31
+ // Pending filter state — buffered until "Apply Filters" clicked
32
+ const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
33
+ const pendingTable = useMemo(() => ({
34
+ get: (key) => pendingFilters[key] ?? "",
35
+ set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
36
+ }), [pendingFilters]);
37
+ const openFilters = useCallback(() => {
38
+ setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
39
+ setFilterOpen(true);
40
+ }, [table]);
41
+ const applyFilters = useCallback(() => {
42
+ const updates = { [TABLE_KEYS.PAGE]: "1" };
43
+ for (const k of FILTER_KEYS)
44
+ updates[k] = pendingFilters[k] ?? "";
45
+ table.setMany(updates);
46
+ setFilterOpen(false);
47
+ }, [pendingFilters, table]);
48
+ const clearPending = useCallback(() => {
49
+ setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
50
+ }, []);
51
+ // Build Sieve filter string from committed URL table values
31
52
  const buildFilters = () => {
32
- const parts = ["validity.isActive==true"];
33
- const typeFilter = table.get("type");
53
+ const parts = [`${COUPON_FIELDS.VALIDITY_FIELDS.IS_ACTIVE}==true`];
54
+ const typeFilter = table.get(TABLE_KEYS.TYPE);
34
55
  if (typeFilter)
35
- parts.push(`type==${typeFilter}`);
56
+ parts.push(`${COUPON_FIELDS.TYPE}==${typeFilter}`);
36
57
  const dateFrom = table.get(TABLE_KEYS.DATE_FROM);
37
58
  if (dateFrom)
38
- parts.push(`validity.startDate>=${dateFrom}`);
59
+ parts.push(`${COUPON_FIELDS.VALIDITY_FIELDS.START_DATE}>=${dateFrom}`);
39
60
  const dateTo = table.get(TABLE_KEYS.DATE_TO);
40
61
  if (dateTo)
41
- parts.push(`validity.endDate<=${dateTo}`);
62
+ parts.push(`${COUPON_FIELDS.VALIDITY_FIELDS.END_DATE}<=${dateTo}`);
42
63
  if (storeId)
43
- parts.push(`storeId==${storeId}`);
64
+ parts.push(`${COUPON_FIELDS.STORE_ID}==${storeId}`);
44
65
  return parts.join(",");
45
66
  };
46
67
  const { promotions: coupons, total, totalPages, isLoading } = usePromotions({
@@ -52,7 +73,7 @@ export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
52
73
  // Use initial data on first load if available and no search/filter active
53
74
  const displayCoupons = !isLoading && coupons.length > 0
54
75
  ? coupons
55
- : !isLoading && initialCoupons && !table.get(TABLE_KEYS.QUERY) && !table.get("type")
76
+ : !isLoading && initialCoupons && !table.get(TABLE_KEYS.QUERY) && !table.get(TABLE_KEYS.TYPE)
56
77
  ? initialCoupons
57
78
  : coupons;
58
79
  const commitSearch = useCallback(() => {
@@ -62,19 +83,19 @@ export function CouponsIndexListing({ initialCoupons, storeSlug, storeId, }) {
62
83
  if (e.key === "Enter")
63
84
  commitSearch();
64
85
  };
65
- const activeType = table.get("type");
86
+ const activeType = table.get(TABLE_KEYS.TYPE);
66
87
  const hasActiveFilters = !!activeType || !!table.get(TABLE_KEYS.DATE_FROM) || !!table.get(TABLE_KEYS.DATE_TO);
67
88
  const clearFilters = () => {
68
89
  table.setMany({ type: "", [TABLE_KEYS.DATE_FROM]: "", [TABLE_KEYS.DATE_TO]: "" });
69
90
  };
70
- return (_jsxs("div", { className: "min-h-[40vh]", children: [_jsxs("div", { className: "sticky top-[var(--header-height,0px)] z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2.5 px-4", children: [_jsxs("div", { className: "flex items-center gap-2.5 max-w-full", children: [_jsxs("button", { type: "button", onClick: () => setFilterOpen(true), className: `flex shrink-0 items-center gap-2 rounded-lg border px-3.5 py-2 text-sm font-medium transition-colors ${hasActiveFilters
91
+ return (_jsxs("div", { className: "min-h-[40vh]", children: [_jsxs("div", { className: "sticky top-[var(--header-height,0px)] z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2.5 px-4", children: [_jsxs("div", { className: "flex items-center gap-2.5 max-w-full", children: [_jsxs("button", { type: "button", onClick: openFilters, className: `flex shrink-0 items-center gap-2 rounded-lg border px-3.5 py-2 text-sm font-medium transition-colors ${hasActiveFilters
71
92
  ? "border-primary bg-primary-50 text-primary-700 dark:bg-primary-900/30 dark:text-primary-300"
72
- : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsxs("span", { className: "hidden sm:inline", children: ["Filters", hasActiveFilters ? " •" : ""] })] }), _jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900", children: [_jsx("input", { type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleKeyDown, placeholder: "Search by name or description\u2026", className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), searchInput && (_jsx("button", { type: "button", onClick: () => { setSearchInput(""); table.set(TABLE_KEYS.QUERY, ""); }, className: "p-2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300", "aria-label": "Clear search", children: _jsx(X, { className: "h-3.5 w-3.5" }) })), _jsx("button", { type: "button", onClick: commitSearch, className: "flex shrink-0 items-center justify-center px-3 py-2 text-zinc-400 hover:text-primary dark:hover:text-primary-400 transition-colors", "aria-label": "Search", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, onChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, options: COUPON_SORT_OPTIONS })] })] }), hasActiveFilters && (_jsxs("div", { className: "flex flex-wrap items-center gap-2 mt-2", children: [activeType && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-medium px-2.5 py-1", children: [COUPON_TYPES.find((t) => t.value === activeType)?.label ?? activeType, _jsx("button", { type: "button", onClick: () => { table.set("type", ""); }, "aria-label": "Remove type filter", children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_FROM) && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-medium px-2.5 py-1", children: ["From: ", table.get(TABLE_KEYS.DATE_FROM), _jsx("button", { type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_FROM, ""); }, "aria-label": "Remove from-date filter", children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_TO) && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-medium px-2.5 py-1", children: ["To: ", table.get(TABLE_KEYS.DATE_TO), _jsx("button", { type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_TO, ""); }, "aria-label": "Remove to-date filter", children: _jsx(X, { className: "h-3 w-3" }) })] })), _jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300 underline", children: "Clear all" })] }))] }), _jsxs("div", { className: "py-6 px-4", children: [isLoading ? (_jsx("div", { className: "grid gap-3 md:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx("div", { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : displayCoupons.length === 0 ? (_jsx("div", { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No coupons match your search." }) })) : (_jsx("div", { className: "grid gap-3 md:grid-cols-2 lg:grid-cols-3", children: displayCoupons.map((coupon) => (_jsx(CouponCard, { coupon: coupon, labels: {
93
+ : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsxs("span", { className: "hidden sm:inline", children: ["Filters", hasActiveFilters ? " •" : ""] })] }), _jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900", children: [_jsx("input", { type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleKeyDown, placeholder: "Search by name or description\u2026", className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), searchInput && (_jsx("button", { type: "button", onClick: () => { setSearchInput(""); table.set(TABLE_KEYS.QUERY, ""); }, className: "p-2 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300", "aria-label": "Clear search", children: _jsx(X, { className: "h-3.5 w-3.5" }) })), _jsx("button", { type: "button", onClick: commitSearch, className: "flex shrink-0 items-center justify-center px-3 py-2 text-zinc-400 hover:text-primary dark:hover:text-primary-400 transition-colors", "aria-label": "Search", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, onChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, options: COUPON_SORT_OPTIONS })] })] }), hasActiveFilters && (_jsxs("div", { className: "flex flex-wrap items-center gap-2 mt-2", children: [activeType && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-medium px-2.5 py-1", children: [COUPON_TYPES.find((t) => t.value === activeType)?.label ?? activeType, _jsx("button", { type: "button", onClick: () => { table.set(TABLE_KEYS.TYPE, ""); }, "aria-label": "Remove type filter", children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_FROM) && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-medium px-2.5 py-1", children: ["From: ", table.get(TABLE_KEYS.DATE_FROM), _jsx("button", { type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_FROM, ""); }, "aria-label": "Remove from-date filter", children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_TO) && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-xs font-medium px-2.5 py-1", children: ["To: ", table.get(TABLE_KEYS.DATE_TO), _jsx("button", { type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_TO, ""); }, "aria-label": "Remove to-date filter", children: _jsx(X, { className: "h-3 w-3" }) })] })), _jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300 underline", children: "Clear all" })] }))] }), _jsxs("div", { className: "py-6 px-4", children: [isLoading ? (_jsx("div", { className: "grid gap-3 md:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border-2 border-zinc-100 dark:border-slate-700 p-4 animate-pulse space-y-3", children: [_jsx("div", { className: "h-6 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : displayCoupons.length === 0 ? (_jsx("div", { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No coupons match your search." }) })) : (_jsx("div", { className: "grid gap-3 md:grid-cols-2 lg:grid-cols-3", children: displayCoupons.map((coupon) => (_jsx(CouponCard, { coupon: coupon, labels: {
73
94
  copy: "Copy",
74
95
  copied: "Copied!",
75
96
  expires: "Expires",
76
97
  minOrder: "Min. order",
77
98
  off: "OFF",
78
99
  freeShipping: "Free Shipping",
79
- } }, coupon.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), !isLoading && total > 0 && (_jsx(Div, { className: "mt-4 text-center", children: _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: [total, " coupon", total !== 1 ? "s" : "", " available"] }) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-6", children: [_jsxs("div", { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Discount Type" }), _jsxs("div", { className: "space-y-2", children: [COUPON_TYPES.map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "coupon-type", value: t.value, checked: table.get("type") === t.value, onChange: () => { table.set("type", t.value); }, className: "accent-primary" }), t.label] }, t.value))), table.get("type") && (_jsx("button", { type: "button", onClick: () => { table.set("type", ""); }, className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline", children: "Clear type" }))] })] }), _jsxs("div", { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Valid Date Range" }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "From date" }), _jsx("input", { type: "date", value: table.get(TABLE_KEYS.DATE_FROM) || "", onChange: (e) => { table.set(TABLE_KEYS.DATE_FROM, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "To date" }), _jsx("input", { type: "date", value: table.get(TABLE_KEYS.DATE_TO) || "", onChange: (e) => { table.set(TABLE_KEYS.DATE_TO, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] })] })] })] }), _jsxs("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5 flex gap-2", children: [_jsx("button", { type: "button", onClick: clearFilters, className: "flex-1 rounded-lg border border-zinc-300 dark:border-slate-600 py-2.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: "Clear all" }), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), className: "flex-1 rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply" })] })] })] }))] }));
100
+ } }, coupon.id))) })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: table.getNumber("page", 1), totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), !isLoading && total > 0 && (_jsx(Div, { className: "mt-4 text-center", children: _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: [total, " coupon", total !== 1 ? "s" : "", " available"] }) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-6", children: [_jsxs("div", { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Discount Type" }), _jsxs("div", { className: "space-y-2", children: [COUPON_TYPES.map((t) => (_jsxs("label", { className: "flex items-center gap-2 cursor-pointer text-sm text-zinc-700 dark:text-zinc-300", children: [_jsx("input", { type: "radio", name: "coupon-type", value: t.value, checked: pendingTable.get(TABLE_KEYS.TYPE) === t.value, onChange: () => { pendingTable.set(TABLE_KEYS.TYPE, t.value); }, className: "accent-primary" }), t.label] }, t.value))), pendingTable.get(TABLE_KEYS.TYPE) && (_jsx("button", { type: "button", onClick: () => { pendingTable.set(TABLE_KEYS.TYPE, ""); }, className: "text-xs text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 underline", children: "Clear type" }))] })] }), _jsxs("div", { children: [_jsx(Heading, { level: 6, className: "text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 mb-3", children: "Valid Date Range" }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "From date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_FROM) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_FROM, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs text-zinc-500 dark:text-zinc-400 mb-1", children: "To date" }), _jsx("input", { type: "date", value: pendingTable.get(TABLE_KEYS.DATE_TO) || "", onChange: (e) => { pendingTable.set(TABLE_KEYS.DATE_TO, e.target.value); }, className: "w-full rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 outline-none focus:ring-2 focus:ring-primary" })] })] })] })] }), _jsxs("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5 flex gap-2", children: [_jsx("button", { type: "button", onClick: clearPending, className: "flex-1 rounded-lg border border-zinc-300 dark:border-slate-600 py-2.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: "Clear all" }), _jsx("button", { type: "button", onClick: applyFilters, className: "flex-1 rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply Filters" })] })] })] }))] }));
80
101
  }
@@ -102,6 +102,7 @@ export declare const sellerStoreSchema: z.ZodObject<{
102
102
  } | undefined;
103
103
  returnPolicy?: string | undefined;
104
104
  website?: string | undefined;
105
+ storeCategory?: string | undefined;
105
106
  storeDescription?: string | undefined;
106
107
  storeLogoURL?: string | undefined;
107
108
  storeBannerURL?: string | undefined;
@@ -112,7 +113,6 @@ export declare const sellerStoreSchema: z.ZodObject<{
112
113
  twitter?: string | undefined;
113
114
  linkedin?: string | undefined;
114
115
  } | undefined;
115
- storeCategory?: string | undefined;
116
116
  shippingPolicy?: string | undefined;
117
117
  isVacationMode?: boolean | undefined;
118
118
  vacationMessage?: string | undefined;
@@ -134,6 +134,7 @@ export declare const sellerStoreSchema: z.ZodObject<{
134
134
  } | undefined;
135
135
  returnPolicy?: string | undefined;
136
136
  website?: string | undefined;
137
+ storeCategory?: string | undefined;
137
138
  storeDescription?: string | undefined;
138
139
  storeLogoURL?: string | undefined;
139
140
  storeBannerURL?: string | undefined;
@@ -144,7 +145,6 @@ export declare const sellerStoreSchema: z.ZodObject<{
144
145
  twitter?: string | undefined;
145
146
  linkedin?: string | undefined;
146
147
  } | undefined;
147
- storeCategory?: string | undefined;
148
148
  shippingPolicy?: string | undefined;
149
149
  isVacationMode?: boolean | undefined;
150
150
  vacationMessage?: string | undefined;
@@ -1,3 +1,6 @@
1
+ import { STORE_FIELDS } from "../../../constants/field-names";
2
+ const { STATUS, IS_PUBLIC, STORE_SLUG, STORE_CATEGORY, STATS_FIELDS } = STORE_FIELDS;
3
+ const { ACTIVE } = STORE_FIELDS.STATUS_VALUES;
1
4
  export class StoresRepository {
2
5
  constructor(repo) {
3
6
  this.repo = repo;
@@ -10,23 +13,23 @@ export class StoresRepository {
10
13
  }
11
14
  async findBySlug(storeSlug) {
12
15
  const result = await this.repo.findAll({
13
- filters: `storeSlug==${storeSlug},status==active`,
16
+ filters: `${STORE_SLUG}==${storeSlug},${STATUS}==${ACTIVE}`,
14
17
  perPage: 1,
15
18
  });
16
19
  return result.data[0] ?? null;
17
20
  }
18
21
  async findActive(page = 1, perPage = 20) {
19
22
  return this.repo.findAll({
20
- filters: "status==active,isPublic==true",
21
- sort: "-itemsSold",
23
+ filters: `${STATUS}==${ACTIVE},${IS_PUBLIC}==true`,
24
+ sort: `-${STATS_FIELDS.ITEMS_SOLD}`,
22
25
  page,
23
26
  perPage,
24
27
  });
25
28
  }
26
29
  async findByCategory(category, page = 1, perPage = 20) {
27
30
  return this.repo.findAll({
28
- filters: `storeCategory==${category},status==active`,
29
- sort: "-averageRating",
31
+ filters: `${STORE_CATEGORY}==${category},${STATUS}==${ACTIVE}`,
32
+ sort: `-${STATS_FIELDS.AVERAGE_RATING}`,
30
33
  page,
31
34
  perPage,
32
35
  });
@@ -40,10 +40,10 @@ export declare const storeListItemSchema: z.ZodObject<{
40
40
  ownerId: string;
41
41
  createdAt?: string | undefined;
42
42
  itemsSold?: number | undefined;
43
+ storeCategory?: string | undefined;
43
44
  storeDescription?: string | undefined;
44
45
  storeLogoURL?: string | undefined;
45
46
  storeBannerURL?: string | undefined;
46
- storeCategory?: string | undefined;
47
47
  totalProducts?: number | undefined;
48
48
  totalReviews?: number | undefined;
49
49
  averageRating?: number | undefined;
@@ -56,10 +56,10 @@ export declare const storeListItemSchema: z.ZodObject<{
56
56
  ownerId: string;
57
57
  createdAt?: string | undefined;
58
58
  itemsSold?: number | undefined;
59
+ storeCategory?: string | undefined;
59
60
  storeDescription?: string | undefined;
60
61
  storeLogoURL?: string | undefined;
61
62
  storeBannerURL?: string | undefined;
62
- storeCategory?: string | undefined;
63
63
  totalProducts?: number | undefined;
64
64
  totalReviews?: number | undefined;
65
65
  averageRating?: number | undefined;
@@ -1,8 +1,15 @@
1
+ import { type Auth } from "firebase/auth";
1
2
  import type { IClientAuthProvider } from "../../contracts/client-auth";
2
3
  /**
3
4
  * Firebase Auth client SDK implementation of IClientAuthProvider.
5
+ *
6
+ * Accepts the already-initialized Auth instance explicitly to avoid relying on
7
+ * the global Firebase app registry — which breaks when Turbopack resolves
8
+ * firebase/auth to a different module copy than the one that called initializeApp().
4
9
  */
5
10
  export declare class FirebaseClientAuthProvider implements IClientAuthProvider {
11
+ private readonly _auth;
12
+ constructor(_auth: Auth);
6
13
  signInWithEmailAndPassword(email: string, password: string): Promise<void>;
7
14
  applyActionCode(code: string): Promise<void>;
8
15
  sendPasswordResetEmail(email: string): Promise<void>;
@@ -1,22 +1,29 @@
1
- import { applyActionCode as fbApplyActionCode, confirmPasswordReset as fbConfirmPasswordReset, EmailAuthProvider, getAuth, reauthenticateWithCredential, sendPasswordResetEmail as fbSendPasswordResetEmail, signInWithEmailAndPassword as fbSignInWithEmailAndPassword, updatePassword, verifyBeforeUpdateEmail, } from "firebase/auth";
1
+ import { applyActionCode as fbApplyActionCode, confirmPasswordReset as fbConfirmPasswordReset, EmailAuthProvider, reauthenticateWithCredential, sendPasswordResetEmail as fbSendPasswordResetEmail, signInWithEmailAndPassword as fbSignInWithEmailAndPassword, updatePassword, verifyBeforeUpdateEmail, } from "firebase/auth";
2
2
  /**
3
3
  * Firebase Auth client SDK implementation of IClientAuthProvider.
4
+ *
5
+ * Accepts the already-initialized Auth instance explicitly to avoid relying on
6
+ * the global Firebase app registry — which breaks when Turbopack resolves
7
+ * firebase/auth to a different module copy than the one that called initializeApp().
4
8
  */
5
9
  export class FirebaseClientAuthProvider {
10
+ constructor(_auth) {
11
+ this._auth = _auth;
12
+ }
6
13
  async signInWithEmailAndPassword(email, password) {
7
- await fbSignInWithEmailAndPassword(getAuth(), email, password);
14
+ await fbSignInWithEmailAndPassword(this._auth, email, password);
8
15
  }
9
16
  async applyActionCode(code) {
10
- await fbApplyActionCode(getAuth(), code);
17
+ await fbApplyActionCode(this._auth, code);
11
18
  }
12
19
  async sendPasswordResetEmail(email) {
13
- await fbSendPasswordResetEmail(getAuth(), email);
20
+ await fbSendPasswordResetEmail(this._auth, email);
14
21
  }
15
22
  async confirmPasswordReset(code, newPassword) {
16
- await fbConfirmPasswordReset(getAuth(), code, newPassword);
23
+ await fbConfirmPasswordReset(this._auth, code, newPassword);
17
24
  }
18
25
  async reauthenticateAndChangePassword(currentPassword, newPassword) {
19
- const user = getAuth().currentUser;
26
+ const user = this._auth.currentUser;
20
27
  if (!user?.email)
21
28
  throw new Error("No authenticated user.");
22
29
  const credential = EmailAuthProvider.credential(user.email, currentPassword);
@@ -24,7 +31,7 @@ export class FirebaseClientAuthProvider {
24
31
  await updatePassword(user, newPassword);
25
32
  }
26
33
  async reauthenticateAndSendEmailUpdateVerification(currentPassword, newEmail) {
27
- const user = getAuth().currentUser;
34
+ const user = this._auth.currentUser;
28
35
  if (!user?.email)
29
36
  throw new Error("No authenticated user.");
30
37
  const credential = EmailAuthProvider.credential(user.email, currentPassword);
@@ -32,6 +39,6 @@ export class FirebaseClientAuthProvider {
32
39
  await verifyBeforeUpdateEmail(user, newEmail);
33
40
  }
34
41
  async reloadCurrentUser() {
35
- await getAuth().currentUser?.reload();
42
+ await this._auth.currentUser?.reload();
36
43
  }
37
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "2.7.12",
3
+ "version": "2.7.13",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -147,7 +147,7 @@
147
147
  "watch:css": "tailwindcss -i src/tailwind-input.css -o dist/tailwind-utilities.css --watch",
148
148
  "audit": "node scripts/audit-violations.mjs",
149
149
  "check:types": "tsc --noEmit",
150
- "check:audits": "node scripts/audit-violations.mjs && node scripts/verify-entries.mjs && node scripts/verify-css-build.mjs && node scripts/audit-use-client.mjs && node scripts/audit-double-navigation.mjs",
150
+ "check:audits": "node scripts/audit-violations.mjs && node scripts/verify-entries.mjs && node scripts/verify-css-build.mjs && node scripts/audit-use-client.mjs && node scripts/audit-double-navigation.mjs && node scripts/audit-repository-fields.mjs",
151
151
  "check": "npm run check:types && npm run check:audits"
152
152
  },
153
153
  "dependencies": {