@mohasinac/appkit 3.2.5 → 3.2.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.
Files changed (134) hide show
  1. package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +1 -1
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +3 -3
  3. package/dist/_internal/server/features/products/data.d.ts +2 -1
  4. package/dist/_internal/server/jobs/core/countersReconcile.js +2 -1
  5. package/dist/_internal/shared/actions/action-registry.js +62 -0
  6. package/dist/_internal/shared/features/products/types.d.ts +3 -2
  7. package/dist/client.d.ts +2 -0
  8. package/dist/client.js +1 -0
  9. package/dist/constants/api-endpoints.d.ts +12 -0
  10. package/dist/constants/api-endpoints.js +4 -0
  11. package/dist/constants/field-names.d.ts +2 -0
  12. package/dist/constants/field-names.js +2 -0
  13. package/dist/features/about/components/PublicProfileView.js +8 -5
  14. package/dist/features/account/components/UserOffersPanel.js +0 -1
  15. package/dist/features/addresses/repository/addresses.repository.d.ts +14 -1
  16. package/dist/features/addresses/repository/addresses.repository.js +122 -1
  17. package/dist/features/addresses/schemas/firestore.d.ts +22 -3
  18. package/dist/features/addresses/schemas/firestore.js +25 -0
  19. package/dist/features/addresses/schemas/index.d.ts +2 -2
  20. package/dist/features/admin/components/AdminAddressClustersView.d.ts +5 -0
  21. package/dist/features/admin/components/AdminAddressClustersView.js +38 -0
  22. package/dist/features/admin/components/AdminAddressesView.d.ts +5 -0
  23. package/dist/features/admin/components/AdminAddressesView.js +137 -0
  24. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -4
  25. package/dist/features/admin/components/AdminFulfillmentView.js +2 -3
  26. package/dist/features/admin/components/AdminOrderEditorView.js +1 -3
  27. package/dist/features/admin/components/AdminPaymentClustersView.d.ts +5 -0
  28. package/dist/features/admin/components/AdminPaymentClustersView.js +37 -0
  29. package/dist/features/admin/components/AdminPaymentMethodsView.d.ts +5 -0
  30. package/dist/features/admin/components/AdminPaymentMethodsView.js +130 -0
  31. package/dist/features/admin/components/AdminSiteSettingsView.js +2 -2
  32. package/dist/features/admin/components/index.d.ts +8 -0
  33. package/dist/features/admin/components/index.js +4 -0
  34. package/dist/features/admin/schemas/firestore.d.ts +3 -5
  35. package/dist/features/admin/types/product.types.d.ts +2 -1
  36. package/dist/features/auth/actions/profile-actions.d.ts +3 -1
  37. package/dist/features/auth/actions/profile-actions.js +29 -2
  38. package/dist/features/auth/schemas/firestore.d.ts +8 -10
  39. package/dist/features/cart/schemas/index.d.ts +6 -6
  40. package/dist/features/categories/components/CategoryBundlesListing.js +1 -3
  41. package/dist/features/checkout/schemas/firestore.d.ts +4 -4
  42. package/dist/features/copilot/components/AdminCopilotView.js +4 -4
  43. package/dist/features/events/schemas/firestore.d.ts +2 -2
  44. package/dist/features/grouped/schemas/firestore.d.ts +2 -4
  45. package/dist/features/history/hooks/useHistory.js +3 -2
  46. package/dist/features/homepage/actions/homepage-section-actions.d.ts +6 -6
  47. package/dist/features/homepage/actions/homepage-section-actions.js +2 -2
  48. package/dist/features/homepage/components/PromoGrid.js +1 -1
  49. package/dist/features/homepage/schemas/firestore.d.ts +15 -14
  50. package/dist/features/layout/AppLayoutShell.js +3 -18
  51. package/dist/features/layout/background-seed-defaults.d.ts +19 -0
  52. package/dist/features/layout/background-seed-defaults.js +14 -0
  53. package/dist/features/lottery/schemas/firestore.d.ts +2 -2
  54. package/dist/features/messages/hooks/useConversation.js +5 -13
  55. package/dist/features/messages/hooks/useConversations.js +0 -1
  56. package/dist/features/messages/schemas/firestore.d.ts +2 -2
  57. package/dist/features/orders/components/RefundHistoryTable.js +1 -3
  58. package/dist/features/orders/schemas/firestore.d.ts +2 -1
  59. package/dist/features/orders/schemas/index.d.ts +11 -7
  60. package/dist/features/orders/schemas/index.js +1 -1
  61. package/dist/features/payments/repository/saved-payment-methods.repository.d.ts +48 -0
  62. package/dist/features/payments/repository/saved-payment-methods.repository.js +236 -0
  63. package/dist/features/payments/schemas/index.d.ts +1 -0
  64. package/dist/features/payments/schemas/index.js +1 -0
  65. package/dist/features/payments/schemas/saved-methods-firestore.d.ts +63 -0
  66. package/dist/features/payments/schemas/saved-methods-firestore.js +60 -0
  67. package/dist/features/payments/server.d.ts +2 -0
  68. package/dist/features/payments/server.js +2 -0
  69. package/dist/features/products/actions/product-actions.d.ts +2 -1
  70. package/dist/features/products/api/[id]/route.js +1 -1
  71. package/dist/features/products/api/route.js +1 -1
  72. package/dist/features/products/components/CompareOverlay.d.ts +18 -1
  73. package/dist/features/products/components/CompareOverlay.js +0 -16
  74. package/dist/features/products/components/ProductDetailActions.js +2 -1
  75. package/dist/features/products/components/ShowGroupSection.js +1 -3
  76. package/dist/features/products/repository/product-features.repository.js +1 -1
  77. package/dist/features/products/schemas/firestore.d.ts +3 -6
  78. package/dist/features/promotions/actions/coupon-actions.d.ts +2 -1
  79. package/dist/features/promotions/hooks/useCouponValidate.d.ts +2 -1
  80. package/dist/features/promotions/repository/coupons.repository.d.ts +2 -1
  81. package/dist/features/promotions/schemas/firestore.d.ts +4 -4
  82. package/dist/features/promotions/schemas/index.d.ts +6 -6
  83. package/dist/features/reviews/repository/reviews.repository.d.ts +12 -0
  84. package/dist/features/reviews/repository/reviews.repository.js +24 -0
  85. package/dist/features/reviews/schemas/firestore.d.ts +5 -1
  86. package/dist/features/reviews/schemas/firestore.js +2 -0
  87. package/dist/features/reviews/schemas/index.d.ts +6 -6
  88. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  89. package/dist/features/scams/schemas/firestore.d.ts +6 -6
  90. package/dist/features/search/types/index.d.ts +3 -2
  91. package/dist/features/search/types/index.js +0 -1
  92. package/dist/features/seller/components/FulfillmentView.js +2 -3
  93. package/dist/features/seller/components/PrintCenterView.js +1 -3
  94. package/dist/features/seller/components/SellerAddressesView.js +7 -4
  95. package/dist/features/seller/components/SellerOffersPanel.js +0 -1
  96. package/dist/features/seller/components/SellerReviewsView.js +0 -1
  97. package/dist/features/seller/schemas/firestore.d.ts +2 -4
  98. package/dist/features/server-errors/schemas/firestore.d.ts +2 -2
  99. package/dist/features/store-extensions/schemas/firestore.d.ts +2 -2
  100. package/dist/features/stores/components/StoreScopedSearch.js +2 -4
  101. package/dist/features/stores/repository/store.repository.js +3 -1
  102. package/dist/features/stores/types/index.d.ts +2 -1
  103. package/dist/features/stores/types/index.js +0 -1
  104. package/dist/features/support/schemas/firestore.d.ts +2 -2
  105. package/dist/features/support/schemas/index.d.ts +8 -8
  106. package/dist/features/wishlist/schemas/index.d.ts +2 -2
  107. package/dist/features/wishlist/types/index.d.ts +2 -1
  108. package/dist/index.d.ts +9 -0
  109. package/dist/index.js +10 -0
  110. package/dist/next/routing/route-map.d.ts +8 -0
  111. package/dist/next/routing/route-map.js +4 -0
  112. package/dist/providers/auth-firebase/provider.js +3 -1
  113. package/dist/providers/auth-firebase/session.js +3 -1
  114. package/dist/repositories/index.d.ts +1 -0
  115. package/dist/repositories/index.js +1 -0
  116. package/dist/schemas/registry.d.ts +66 -66
  117. package/dist/schemas/webhooks/razorpay.d.ts +50 -50
  118. package/dist/security/index.d.ts +1 -1
  119. package/dist/security/index.js +1 -1
  120. package/dist/security/pii-schemas.d.ts +2 -0
  121. package/dist/security/pii-schemas.js +2 -0
  122. package/dist/seo/json-ld.d.ts +2 -1
  123. package/dist/styles.css +1 -1
  124. package/dist/tailwind-utilities.css +1 -1
  125. package/dist/ui/components/Form.d.ts +3 -1
  126. package/dist/ui/components/Form.js +2 -1
  127. package/dist/ui/components/Radio.d.ts +7 -0
  128. package/dist/ui/components/Radio.js +7 -0
  129. package/dist/ui/components/surface-tokens.d.ts +4 -0
  130. package/dist/ui/components/surface-tokens.js +24 -0
  131. package/dist/ui/index.d.ts +2 -2
  132. package/dist/ui/index.js +1 -1
  133. package/dist/ui/rich-text/RichText.js +0 -1
  134. package/package.json +5 -2
@@ -231,7 +231,7 @@ export declare function meetsMinPriority(priority: NotificationPriority, minPrio
231
231
  * `defaultLightThemeId` and `defaultDarkThemeId` decide which record is
232
232
  * applied when the user's mode preference resolves to that mode.
233
233
  */
234
- export interface SiteSettingsThemeRecord {
234
+ export type SiteSettingsThemeRecord = {
235
235
  id: string;
236
236
  name: string;
237
237
  mode: "light" | "dark";
@@ -241,7 +241,7 @@ export interface SiteSettingsThemeRecord {
241
241
  tokens: Record<string, string>;
242
242
  /** Full gradient palette; values are complete CSS gradient strings. */
243
243
  gradients: Record<string, string>;
244
- }
244
+ };
245
245
  export interface SiteSettingsTheme {
246
246
  /** All available theme records — built-ins + admin-created. */
247
247
  themes?: SiteSettingsThemeRecord[];
@@ -250,7 +250,7 @@ export interface SiteSettingsTheme {
250
250
  /** Theme id applied when the user's resolved mode is `"dark"`. */
251
251
  defaultDarkThemeId?: string;
252
252
  }
253
- export interface SiteSettingsDocument {
253
+ export interface SiteSettingsDocument extends BaseDocument {
254
254
  id: "global";
255
255
  siteName: string;
256
256
  motto: string;
@@ -479,8 +479,6 @@ export interface SiteSettingsDocument {
479
479
  * opt-in and require the corresponding credentials to be set.
480
480
  */
481
481
  notificationChannels?: NotificationChannelConfig;
482
- createdAt: Date;
483
- updatedAt: Date;
484
482
  }
485
483
  export type FeatureFlagKey = keyof SiteSettingsDocument["featureFlags"];
486
484
  export interface FeatureFlagMeta {
@@ -4,6 +4,7 @@
4
4
  * These types extend/complement the core ProductItem with admin-only fields
5
5
  * for managing auctions, pre-orders, stock, pricing, and lifecycle.
6
6
  */
7
+ import type { ListingType } from "../../products/types";
7
8
  export type AdminProductStatus = "draft" | "published" | "in_review" | "archived";
8
9
  export type AdminProductDrawerMode = "create" | "edit" | "delete" | null;
9
10
  /**
@@ -48,7 +49,7 @@ export interface AdminProduct {
48
49
  insuranceCost?: number;
49
50
  shippingPaidBy?: "seller" | "buyer";
50
51
  /** Canonical listing-kind discriminator (SB1-G Phase 4 — booleans removed). */
51
- listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "classified" | "digital-code" | "live";
52
+ listingType?: ListingType;
52
53
  auctionEndDate?: string;
53
54
  startingBid?: number;
54
55
  currentBid?: number;
@@ -27,5 +27,7 @@ export declare function getPublicUserProfile(userId: string): Promise<Pick<UserD
27
27
  /** Fetch approved reviews for a store. storeId === storeSlug in this project. */
28
28
  export declare function getSellerReviews(storeId: string): Promise<Review[]>;
29
29
  export declare function getProfileStoreProducts(storeId: string): Promise<import("../../products").ProductDocument[]>;
30
- /** Approved reviews AUTHORED by this user (not reviews of their store). */
30
+ /** Approved product reviews written by this user as a buyer. Excludes seller→buyer ratings. */
31
31
  export declare function getReviewsAuthoredBy(userId: string): Promise<Review[]>;
32
+ /** Approved seller→buyer reviews received by this buyer. */
33
+ export declare function getReviewsReceivedBy(userId: string): Promise<Review[]>;
@@ -70,9 +70,36 @@ export async function getProfileStoreProducts(storeId) {
70
70
  const products = await productRepository.findByStore(storeId);
71
71
  return products.filter((p) => p.status === ProductStatusValues.PUBLISHED);
72
72
  }
73
- /** Approved reviews AUTHORED by this user (not reviews of their store). */
73
+ /** Approved product reviews written by this user as a buyer. Excludes seller→buyer ratings. */
74
74
  export async function getReviewsAuthoredBy(userId) {
75
- const snapshot = await reviewRepository.findApprovedByUser(userId).catch(() => []);
75
+ const all = await reviewRepository.findApprovedByUser(userId).catch(() => []);
76
+ // reviewerRole:"seller" means this user rated a buyer — not a product review
77
+ const snapshot = all.filter((r) => r.reviewerRole !== "seller");
78
+ return snapshot.map((r) => ({
79
+ id: r.id,
80
+ productId: r.productId,
81
+ productTitle: r.productTitle,
82
+ userId: r.userId,
83
+ userName: maskPublicReview(r).userName,
84
+ userAvatar: r.userAvatar,
85
+ rating: r.rating,
86
+ title: r.title,
87
+ comment: r.comment,
88
+ images: r.images?.map((url) => ({ url })),
89
+ status: r.status,
90
+ helpfulCount: r.helpfulCount,
91
+ reportCount: r.reportCount,
92
+ verified: r.verified,
93
+ featured: r.featured,
94
+ createdAt: r.createdAt instanceof Date ? r.createdAt.toISOString() : undefined,
95
+ updatedAt: r.updatedAt instanceof Date ? r.updatedAt.toISOString() : undefined,
96
+ storeSlug: r.storeId,
97
+ storeName: r.storeName,
98
+ }));
99
+ }
100
+ /** Approved seller→buyer reviews received by this buyer. */
101
+ export async function getReviewsReceivedBy(userId) {
102
+ const snapshot = await reviewRepository.findByReviewee(userId).catch(() => []);
76
103
  return snapshot.map((r) => ({
77
104
  id: r.id,
78
105
  productId: r.productId,
@@ -7,6 +7,7 @@
7
7
  import type { UserRole } from "../types";
8
8
  import type { BannedAction, EmployeeGroup, Permission } from "../permissions/constants";
9
9
  import { type GenerateUserIdInput } from "../../../utils/id-generators";
10
+ import type { BaseDocument } from "../../../_internal/shared/types/base-document";
10
11
  /**
11
12
  * Generic schema defaults for user-related data.
12
13
  * Consumer-specific values (admin email, currency) belong in SiteConfig, not here.
@@ -31,8 +32,7 @@ export interface AvatarMetadata {
31
32
  };
32
33
  zoom: number;
33
34
  }
34
- export interface UserDocument {
35
- id?: string;
35
+ export interface UserDocument extends BaseDocument {
36
36
  uid: string;
37
37
  email: string | null;
38
38
  phoneNumber: string | null;
@@ -44,8 +44,6 @@ export interface UserDocument {
44
44
  passwordHash?: string;
45
45
  emailVerified: boolean;
46
46
  disabled: boolean;
47
- createdAt: Date;
48
- updatedAt: Date;
49
47
  storeId?: string;
50
48
  storeSlug?: string;
51
49
  storeStatus?: "pending" | "approved" | "rejected";
@@ -242,15 +240,15 @@ export declare const USER_FIELDS: {
242
240
  readonly LOGIN_COUNT: "metadata.loginCount";
243
241
  };
244
242
  };
245
- export interface EmailVerificationTokenDocument {
243
+ export type EmailVerificationTokenDocument = {
246
244
  id?: string;
247
245
  userId: string;
248
246
  email: string;
249
247
  token: string;
250
248
  expiresAt: Date;
251
249
  createdAt: Date;
252
- }
253
- export interface PasswordResetTokenDocument {
250
+ };
251
+ export type PasswordResetTokenDocument = {
254
252
  id?: string;
255
253
  userId: string;
256
254
  email: string;
@@ -259,7 +257,7 @@ export interface PasswordResetTokenDocument {
259
257
  createdAt: Date;
260
258
  used: boolean;
261
259
  usedAt?: Date;
262
- }
260
+ };
263
261
  export declare const EMAIL_VERIFICATION_COLLECTION: "emailVerificationTokens";
264
262
  export declare const PASSWORD_RESET_COLLECTION: "passwordResetTokens";
265
263
  export declare const TOKEN_INDEXED_FIELDS: readonly ["userId", "email", "expiresAt", "used"];
@@ -287,7 +285,7 @@ export declare const TOKEN_FIELDS: {
287
285
  readonly USED: "used";
288
286
  readonly USED_AT: "usedAt";
289
287
  };
290
- export interface SessionDocument {
288
+ export type SessionDocument = {
291
289
  id: string;
292
290
  userId: string;
293
291
  deviceInfo?: {
@@ -307,7 +305,7 @@ export interface SessionDocument {
307
305
  isActive: boolean;
308
306
  revokedAt?: Date;
309
307
  revokedBy?: string;
310
- }
308
+ };
311
309
  export declare const SESSION_COLLECTION: "sessions";
312
310
  export declare const SESSION_INDEXED_FIELDS: readonly ["userId", "isActive", "expiresAt", "createdAt", "lastActivity"];
313
311
  export declare const DEFAULT_SESSION_DATA: Partial<SessionDocument>;
@@ -66,8 +66,8 @@ export declare const cartItemSchema: z.ZodObject<{
66
66
  slug?: string | undefined;
67
67
  attributes?: Record<string, string> | undefined;
68
68
  } | undefined;
69
- sessionId?: string | undefined;
70
69
  userId?: string | undefined;
70
+ sessionId?: string | undefined;
71
71
  addedAt?: string | undefined;
72
72
  }, {
73
73
  id: string;
@@ -79,9 +79,9 @@ export declare const cartItemSchema: z.ZodObject<{
79
79
  slug?: string | undefined;
80
80
  attributes?: Record<string, string> | undefined;
81
81
  } | undefined;
82
+ userId?: string | undefined;
82
83
  sessionId?: string | undefined;
83
84
  quantity?: number | undefined;
84
- userId?: string | undefined;
85
85
  addedAt?: string | undefined;
86
86
  }>;
87
87
  export declare const cartSummarySchema: z.ZodObject<{
@@ -122,8 +122,8 @@ export declare const cartSummarySchema: z.ZodObject<{
122
122
  slug?: string | undefined;
123
123
  attributes?: Record<string, string> | undefined;
124
124
  } | undefined;
125
- sessionId?: string | undefined;
126
125
  userId?: string | undefined;
126
+ sessionId?: string | undefined;
127
127
  addedAt?: string | undefined;
128
128
  }, {
129
129
  id: string;
@@ -135,9 +135,9 @@ export declare const cartSummarySchema: z.ZodObject<{
135
135
  slug?: string | undefined;
136
136
  attributes?: Record<string, string> | undefined;
137
137
  } | undefined;
138
+ userId?: string | undefined;
138
139
  sessionId?: string | undefined;
139
140
  quantity?: number | undefined;
140
- userId?: string | undefined;
141
141
  addedAt?: string | undefined;
142
142
  }>, "many">;
143
143
  subtotal: z.ZodNumber;
@@ -156,8 +156,8 @@ export declare const cartSummarySchema: z.ZodObject<{
156
156
  slug?: string | undefined;
157
157
  attributes?: Record<string, string> | undefined;
158
158
  } | undefined;
159
- sessionId?: string | undefined;
160
159
  userId?: string | undefined;
160
+ sessionId?: string | undefined;
161
161
  addedAt?: string | undefined;
162
162
  }[];
163
163
  subtotal: number;
@@ -173,9 +173,9 @@ export declare const cartSummarySchema: z.ZodObject<{
173
173
  slug?: string | undefined;
174
174
  attributes?: Record<string, string> | undefined;
175
175
  } | undefined;
176
+ userId?: string | undefined;
176
177
  sessionId?: string | undefined;
177
178
  quantity?: number | undefined;
178
- userId?: string | undefined;
179
179
  addedAt?: string | undefined;
180
180
  }[];
181
181
  subtotal: number;
@@ -87,7 +87,5 @@ export function CategoryBundlesListing({ initialBundles, brandName, onBuyNow, })
87
87
  return (_jsx(Div, { className: "border-dashed text-center", padding: "y-4xl", rounded: "2xl", border: "default", children: _jsxs(Text, { color: "muted", children: ["No bundles available", brandName ? ` for ${brandName}` : "", " yet."] }) }));
88
88
  }
89
89
  return (_jsxs(Div, { className: "min-h-[40vh]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search bundles...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
90
- commitSearch(); }, sortValue: sort, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(Row, { border: "bottom", className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 backdrop-blur-sm", surface: "default", padding: "toolbar", justify: "center", children: _jsx(Pagination, { currentPage: safePage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx(Div, { padding: "y-lg", children: pageItems.length === 0 ? (_jsxs(Text, { paddingY: "4xl", color: "muted", size: "sm", align: "center", children: ["No bundles match your search", brandName ? ` for ${brandName}` : "", "."] })) : (_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: pageItems.map((bundle) => (_jsxs(Stack, { children: [_jsx(MarketplaceBundleCard, { bundle: bundle }), onBuyNow && (
91
- // audit-inline-style-ok: asymmetric border-radius (bottom corners only); no appkit rounded= variant covers partial corners
92
- _jsx(Div, { border: "default", surface: "default", className: "-mt-px border-t border-t-0 border-[var(--appkit-color-border)] pt-[0.5rem] pb-[0.75rem]", padding: "x-sm", style: { borderBottomLeftRadius: "0.75rem", borderBottomRightRadius: "0.75rem" }, children: _jsx(BundleBuyNowCta, { bundleSlug: bundle.slug, outOfStock: bundle.bundleStockStatus === "out_of_stock", onBuyNow: onBuyNow, compact: true }) }))] }, bundle.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: () => setPendingShowOutOfStock(false), activeCount: filterActiveCount, hideTrigger: true, children: _jsx(Stack, { className: `${__P.p4}`, gap: "md", children: _jsxs(Label, { layout: "flex", gap: "lg", className: "justify-[space-between] cursor-pointer", children: [_jsx(Span, { size: "sm", weight: "medium", color: "muted", children: "Show out-of-stock bundles" }), _jsx("button", { type: "button", role: "switch", "aria-checked": pendingShowOutOfStock, onClick: () => setPendingShowOutOfStock((v) => !v), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${pendingShowOutOfStock ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx(Span, { className: `inline-block h-3.5 w-3.5 transform transition-transform duration-200 ${pendingShowOutOfStock ? "translate-x-[19px]" : "translate-x-[3px]"}`, rounded: "full", surface: "default", shadow: "sm" }) })] }) }) })] }));
90
+ commitSearch(); }, sortValue: sort, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(Row, { border: "bottom", className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 backdrop-blur-sm", surface: "default", padding: "toolbar", justify: "center", children: _jsx(Pagination, { currentPage: safePage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx(Div, { padding: "y-lg", children: pageItems.length === 0 ? (_jsxs(Text, { paddingY: "4xl", color: "muted", size: "sm", align: "center", children: ["No bundles match your search", brandName ? ` for ${brandName}` : "", "."] })) : (_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: pageItems.map((bundle) => (_jsxs(Stack, { children: [_jsx(MarketplaceBundleCard, { bundle: bundle }), onBuyNow && (_jsx(Div, { border: "default", surface: "default", className: "-mt-px border-t border-t-0 border-[var(--appkit-color-border)] pt-[0.5rem] pb-[0.75rem]", padding: "x-sm", roundedBottom: "lg", children: _jsx(BundleBuyNowCta, { bundleSlug: bundle.slug, outOfStock: bundle.bundleStockStatus === "out_of_stock", onBuyNow: onBuyNow, compact: true }) }))] }, bundle.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: () => setPendingShowOutOfStock(false), activeCount: filterActiveCount, hideTrigger: true, children: _jsx(Stack, { className: `${__P.p4}`, gap: "md", children: _jsxs(Label, { layout: "flex", gap: "lg", className: "justify-[space-between] cursor-pointer", children: [_jsx(Span, { size: "sm", weight: "medium", color: "muted", children: "Show out-of-stock bundles" }), _jsx("button", { type: "button", role: "switch", "aria-checked": pendingShowOutOfStock, onClick: () => setPendingShowOutOfStock((v) => !v), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${pendingShowOutOfStock ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx(Span, { className: `inline-block h-3.5 w-3.5 transform transition-transform duration-200 ${pendingShowOutOfStock ? "translate-x-[19px]" : "translate-x-[3px]"}`, rounded: "full", surface: "default", shadow: "sm" }) })] }) }) })] }));
93
91
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export type FailedCheckoutReason = "otp_not_verified" | "consent_expired" | "stock_failed" | "address_not_found" | "cart_empty" | "validation_error" | "unknown";
6
6
  export type FailedPaymentReason = "signature_mismatch" | "payment_cancelled" | "amount_mismatch" | "otp_not_verified" | "consent_expired" | "product_unavailable" | "stock_insufficient" | "unknown";
7
- export interface FailedCheckoutDocument {
7
+ export type FailedCheckoutDocument = {
8
8
  id: string;
9
9
  uid: string;
10
10
  addressId?: string;
@@ -15,8 +15,8 @@ export interface FailedCheckoutDocument {
15
15
  /** Raw error message for ops — not shown to buyers. */
16
16
  detail?: string;
17
17
  createdAt: Date;
18
- }
19
- export interface FailedPaymentDocument {
18
+ };
19
+ export type FailedPaymentDocument = {
20
20
  id: string;
21
21
  uid: string;
22
22
  gatewayOrderId?: string;
@@ -28,6 +28,6 @@ export interface FailedPaymentDocument {
28
28
  /** Raw error message for ops. */
29
29
  detail?: string;
30
30
  createdAt: Date;
31
- }
31
+ };
32
32
  export declare const FAILED_CHECKOUTS_COLLECTION: "failedCheckouts";
33
33
  export declare const FAILED_PAYMENTS_COLLECTION: "failedPayments";
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { useQuery } from "@tanstack/react-query";
5
- import { Alert, Button, Div, Heading, Input, Row, Span, Stack, StackedViewShell, Text } from "../../../ui";
5
+ import { Alert, Button, Div, Form, Heading, Input, Row, Span, Stack, StackedViewShell, Text } from "../../../ui";
6
6
  import { apiClient } from "../../../http";
7
7
  import { useCopilotChat } from "../hooks/useCopilotChat";
8
8
  const __P = {
@@ -20,11 +20,11 @@ function MessageBubble({ msg }) {
20
20
  }
21
21
  function renderCopilotChatPanel(props) {
22
22
  const { messages, conversationId, isLoading, error, input, setInput, labels, messagesEndRef, handleSubmit } = props;
23
- return (_jsxs(Stack, { className: "border border-neutral-200 h-[calc(100vh-300px)]", surface: "default", rounded: "xl", children: [_jsx(Div, { border: "bottom", className: __P.p3, children: _jsxs(Text, { className: "text-neutral-500 dark:text-neutral-400", size: "xs", weight: "medium", children: [labels?.conversationId ?? LBL_CONVERSATION_ID, ": ", conversationId] }) }), _jsxs(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "md", children: [messages.length === 0 ? (_jsxs(Stack, { justify: "center", className: "h-full text-center", align: "center", children: [_jsx(Text, { weight: "medium", children: labels?.noMessages ?? "No messages yet" }), _jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: labels?.noMessagesDesc ?? "Ask anything to get started" })] })) : null, messages.map((msg) => _jsx(MessageBubble, { msg: msg }, msg.id)), isLoading ? (_jsx(Row, { justify: "start", children: _jsx(Div, { paddingY: "y-xs-tall", surface: "muted", className: "", padding: "x-md", style: { borderRadius: "0.125rem 1.5rem 1.5rem 1.5rem" }, children: _jsx(Span, { size: "sm", className: "opacity-60 animate-pulse", children: "Thinking..." }) }) })) : null, error ? _jsx(Text, { className: CLS_ERROR_TEXT, children: labels?.errorLabel ?? "An error occurred. Please try again." }) : null, _jsx(Div, { ref: messagesEndRef })] }), _jsx(Div, { border: "top", className: __P.p3, children: _jsxs("form", { onSubmit: handleSubmit, className: "flex gap-2", children: [_jsx("input", { type: "text", value: input, onChange: (e) => setInput(e.target.value), placeholder: labels?.inputPlaceholder ?? "Ask anything...", disabled: isLoading, className: "flex-1 h-10 rounded-lg border border-neutral-200 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 text-sm outline-none focus:ring-2 ring-primary/20 transition disabled:opacity-60" }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading || !input.trim(), className: "shrink-0", children: isLoading ? labels?.sendingButton ?? "Sending..." : labels?.sendButton ?? "Send" })] }) })] }));
23
+ return (_jsxs(Stack, { className: "border border-neutral-200 h-[calc(100vh-300px)]", surface: "default", rounded: "xl", children: [_jsx(Div, { border: "bottom", className: __P.p3, children: _jsxs(Text, { className: "text-neutral-500 dark:text-neutral-400", size: "xs", weight: "medium", children: [labels?.conversationId ?? LBL_CONVERSATION_ID, ": ", conversationId] }) }), _jsxs(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "md", children: [messages.length === 0 ? (_jsxs(Stack, { justify: "center", className: "h-full text-center", align: "center", children: [_jsx(Text, { weight: "medium", children: labels?.noMessages ?? "No messages yet" }), _jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: labels?.noMessagesDesc ?? "Ask anything to get started" })] })) : null, messages.map((msg) => _jsx(MessageBubble, { msg: msg }, msg.id)), isLoading ? (_jsx(Row, { justify: "start", children: _jsx(Div, { paddingY: "y-xs-tall", surface: "muted", className: "", padding: "x-md", style: { borderRadius: "0.125rem 1.5rem 1.5rem 1.5rem" }, children: _jsx(Span, { size: "sm", className: "opacity-60 animate-pulse", children: "Thinking..." }) }) })) : null, error ? _jsx(Text, { className: CLS_ERROR_TEXT, children: labels?.errorLabel ?? "An error occurred. Please try again." }) : null, _jsx(Div, { ref: messagesEndRef })] }), _jsx(Div, { border: "top", className: __P.p3, children: _jsxs(Form, { onSubmit: handleSubmit, gap: "xs", className: "flex", children: [_jsx(Input, { type: "text", bare: true, value: input, onChange: (e) => setInput(e.target.value), placeholder: labels?.inputPlaceholder ?? "Ask anything...", disabled: isLoading, className: "flex-1 h-10" }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading || !input.trim(), className: "shrink-0", children: isLoading ? labels?.sendingButton ?? "Sending..." : labels?.sendButton ?? "Send" })] }) })] }));
24
24
  }
25
25
  function renderCopilotHistoryPanel(props) {
26
26
  const { historyQuery, conversationInput, setConversationInput, loadConversation, labels } = props;
27
- return (_jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", children: labels?.historyTitle ?? "Conversation History" }), _jsxs("form", { className: "space-y-2", onSubmit: (e) => { e.preventDefault(); if (!conversationInput.trim())
27
+ return (_jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", children: labels?.historyTitle ?? "Conversation History" }), _jsxs(Form, { spacing: "sm", onSubmit: (e) => { e.preventDefault(); if (!conversationInput.trim())
28
28
  return; loadConversation(conversationInput.trim()); }, children: [_jsx(Input, { label: labels?.conversationId ?? LBL_CONVERSATION_ID, value: conversationInput, onChange: (e) => setConversationInput(e.target.value), placeholder: "conv_..." }), _jsx(Button, { type: "submit", variant: "secondary", size: "sm", className: "w-full", children: labels?.loadConversation ?? "Load conversation" })] }), historyQuery.error ? (_jsx(Alert, { variant: "warning", title: "History unavailable", children: historyQuery.error instanceof Error ? historyQuery.error.message : "Could not load history" })) : null, _jsxs(Stack, { className: `max-h-72 ${__O.yAuto}`, gap: "sm", children: [(historyQuery.data?.messages ?? []).map((log, index) => (_jsxs(Div, { className: "border border-neutral-200", rounded: "lg", padding: "xs", children: [_jsx(Text, { className: "text-neutral-500 dark:text-neutral-400", size: "xs", weight: "medium", children: new Date(log.createdAt).toLocaleString() }), _jsxs(Text, { className: `mt-1`, truncate: 3, size: "sm", children: ["Q: ", log.prompt] }), _jsxs(Text, { className: `mt-1`, color: "muted", truncate: 3, size: "sm", children: ["A: ", log.response] })] }, `${log.createdAt}-${index}`))), !historyQuery.isLoading && (historyQuery.data?.messages?.length ?? 0) === 0 ? (_jsx(Text, { variant: "secondary", size: "sm", children: "No server history found for this conversation yet." })) : null] })] }));
29
29
  }
30
30
  export function AdminCopilotView({ endpoint, historyEndpoint = "/api/copilot/history", renderHeader, labels = {}, className = "", }) {
@@ -49,7 +49,7 @@ export function AdminCopilotView({ endpoint, historyEndpoint = "/api/copilot/his
49
49
  };
50
50
  return (_jsx(StackedViewShell, { portal: "admin", title: labels.title ?? "AI Copilot", className: className, sections: [
51
51
  renderHeader ? (renderHeader(startNewConversation)) : (_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Heading, { level: 2, children: labels.title ?? "AI Copilot" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: startNewConversation, children: labels.newConversation ?? "New Conversation" })] })),
52
- _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-1 lg:grid-cols-[2fr,1fr]", children: [_jsxs(Stack, { className: "border border-neutral-200 h-[calc(100vh-300px)]", surface: "default", rounded: "xl", children: [_jsx(Div, { border: "bottom", className: __P.p3, children: _jsxs(Text, { className: "text-neutral-500 dark:text-neutral-400", size: "xs", weight: "medium", children: [labels.conversationId ?? LBL_CONVERSATION_ID, ": ", conversationId] }) }), _jsxs(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "md", children: [messages.length === 0 ? (_jsxs(Stack, { justify: "center", className: "h-full text-center", align: "center", children: [_jsx(Text, { weight: "medium", children: labels.noMessages ?? "No messages yet" }), _jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: labels.noMessagesDesc ?? "Ask anything to get started" })] })) : null, messages.map((msg) => (_jsx(MessageBubble, { msg: msg }, msg.id))), isLoading ? (_jsx(Row, { justify: "start", children: _jsx(Div, { paddingY: "y-xs-tall", surface: "muted", className: "", padding: "x-md", style: { borderRadius: "0.125rem 1.5rem 1.5rem 1.5rem" }, children: _jsx(Span, { size: "sm", className: "opacity-60 animate-pulse", children: "Thinking..." }) }) })) : null, error ? (_jsx(Text, { className: CLS_ERROR_TEXT, children: labels.errorLabel ?? "An error occurred. Please try again." })) : null, _jsx(Div, { ref: messagesEndRef })] }), _jsx(Div, { border: "top", className: __P.p3, children: _jsxs("form", { onSubmit: handleSubmit, className: "flex gap-2", children: [_jsx("input", { type: "text", value: input, onChange: (event) => setInput(event.target.value), placeholder: labels.inputPlaceholder ?? "Ask anything...", disabled: isLoading, className: "flex-1 h-10 rounded-lg border border-neutral-200 dark:border-slate-600 bg-white dark:bg-slate-800 px-3 text-sm outline-none focus:ring-2 ring-primary/20 transition disabled:opacity-60" }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading || !input.trim(), className: "shrink-0", children: isLoading ? labels.sendingButton ?? "Sending..." : labels.sendButton ?? "Send" })] }) })] }), _jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", children: labels.historyTitle ?? "Conversation History" }), _jsxs("form", { className: "space-y-2", onSubmit: (event) => {
52
+ _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-1 lg:grid-cols-[2fr,1fr]", children: [_jsxs(Stack, { className: "border border-neutral-200 h-[calc(100vh-300px)]", surface: "default", rounded: "xl", children: [_jsx(Div, { border: "bottom", className: __P.p3, children: _jsxs(Text, { className: "text-neutral-500 dark:text-neutral-400", size: "xs", weight: "medium", children: [labels.conversationId ?? LBL_CONVERSATION_ID, ": ", conversationId] }) }), _jsxs(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "md", children: [messages.length === 0 ? (_jsxs(Stack, { justify: "center", className: "h-full text-center", align: "center", children: [_jsx(Text, { weight: "medium", children: labels.noMessages ?? "No messages yet" }), _jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: labels.noMessagesDesc ?? "Ask anything to get started" })] })) : null, messages.map((msg) => (_jsx(MessageBubble, { msg: msg }, msg.id))), isLoading ? (_jsx(Row, { justify: "start", children: _jsx(Div, { paddingY: "y-xs-tall", surface: "muted", className: "", padding: "x-md", style: { borderRadius: "0.125rem 1.5rem 1.5rem 1.5rem" }, children: _jsx(Span, { size: "sm", className: "opacity-60 animate-pulse", children: "Thinking..." }) }) })) : null, error ? (_jsx(Text, { className: CLS_ERROR_TEXT, children: labels.errorLabel ?? "An error occurred. Please try again." })) : null, _jsx(Div, { ref: messagesEndRef })] }), _jsx(Div, { border: "top", className: __P.p3, children: _jsxs(Form, { onSubmit: handleSubmit, gap: "xs", className: "flex", children: [_jsx(Input, { type: "text", bare: true, value: input, onChange: (event) => setInput(event.target.value), placeholder: labels.inputPlaceholder ?? "Ask anything...", disabled: isLoading, className: "flex-1 h-10" }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading || !input.trim(), className: "shrink-0", children: isLoading ? labels.sendingButton ?? "Sending..." : labels.sendButton ?? "Send" })] }) })] }), _jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", children: labels.historyTitle ?? "Conversation History" }), _jsxs(Form, { spacing: "sm", onSubmit: (event) => {
53
53
  event.preventDefault();
54
54
  if (!conversationInput.trim())
55
55
  return;
@@ -52,7 +52,7 @@ export interface EventDocument extends BaseDocument {
52
52
  };
53
53
  createdBy: string;
54
54
  }
55
- export interface EventEntryDocument {
55
+ export type EventEntryDocument = {
56
56
  id: string;
57
57
  eventId: string;
58
58
  userId?: string;
@@ -73,7 +73,7 @@ export interface EventEntryDocument {
73
73
  spinWonAt?: Date;
74
74
  ipAddress?: string;
75
75
  submittedAt: Date;
76
- }
76
+ };
77
77
  export declare const EVENT_INDEXED_FIELDS: readonly ["type", "status", "startsAt", "endsAt", "createdAt"];
78
78
  export declare const EVENT_ENTRY_INDEXED_FIELDS: readonly ["eventId", "userId", "reviewStatus", "submittedAt", "points"];
79
79
  export declare const EVENT_FIELDS: {
@@ -11,6 +11,7 @@
11
11
  * active members drops below `minActiveMembers`, `visibilityStatus` flips
12
12
  * to "hidden" (recomputed by `onProductStockChange`).
13
13
  */
14
+ import type { BaseDocument } from "../../../_internal/shared/types/base-document";
14
15
  export type GroupTheme =
15
16
  /** "Other anime figures you might like" */
16
17
  "related"
@@ -23,8 +24,7 @@ export type GroupTheme =
23
24
  /** Default / unspecified theme */
24
25
  | "generic";
25
26
  export type GroupVisibility = "visible" | "hidden";
26
- export interface GroupedListingDocument {
27
- id: string;
27
+ export interface GroupedListingDocument extends BaseDocument {
28
28
  slug: string;
29
29
  title: string;
30
30
  description?: string;
@@ -44,8 +44,6 @@ export interface GroupedListingDocument {
44
44
  brandSlug?: string;
45
45
  categorySlug?: string;
46
46
  createdBy: string;
47
- createdAt: Date;
48
- updatedAt: Date;
49
47
  }
50
48
  export declare const GROUPED_LISTINGS_COLLECTION: "groupedListings";
51
49
  export declare const GROUPED_LISTINGS_INDEXED_FIELDS: readonly ["storeId", "brandSlug", "categorySlug", "isActive", "isFeatured", "groupTheme", "visibilityStatus", "createdAt"];
@@ -12,6 +12,7 @@
12
12
  import { useCallback, useEffect, useRef, useState } from "react";
13
13
  import { useToast } from "../../../ui";
14
14
  import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints";
15
+ import { normalizeError } from "../../../errors/normalize";
15
16
  import { clearGuestHistory, getGuestHistory, removeGuestHistoryItem, trackGuestHistory, } from "../utils/guest-history";
16
17
  const TRACK_DEBOUNCE_MS = 1500;
17
18
  const sessionTracked = new Set();
@@ -77,8 +78,8 @@ export function useHistory(userId) {
77
78
  setItems(getGuestHistory());
78
79
  }
79
80
  }
80
- catch {
81
- // toast-intentionally-silent: data loading — silent fallback to empty
81
+ catch (err) {
82
+ void normalizeError(err);
82
83
  }
83
84
  }, [isAuth]);
84
85
  useEffect(() => {
@@ -5,30 +5,30 @@ export declare const createSectionSchema: z.ZodObject<{
5
5
  type: z.ZodString;
6
6
  enabled: z.ZodDefault<z.ZodBoolean>;
7
7
  order: z.ZodDefault<z.ZodNumber>;
8
- config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
8
+ config: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
9
9
  }, "strip", z.ZodTypeAny, {
10
10
  type: string;
11
11
  order: number;
12
12
  enabled: boolean;
13
- config?: Record<string, unknown> | undefined;
13
+ config?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
14
14
  }, {
15
15
  type: string;
16
16
  order?: number | undefined;
17
17
  enabled?: boolean | undefined;
18
- config?: Record<string, unknown> | undefined;
18
+ config?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
19
19
  }>;
20
20
  export declare const updateSectionSchema: z.ZodObject<{
21
21
  order: z.ZodOptional<z.ZodNumber>;
22
22
  enabled: z.ZodOptional<z.ZodBoolean>;
23
- config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
23
+ config: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
24
24
  }, "strip", z.ZodTypeAny, {
25
25
  order?: number | undefined;
26
26
  enabled?: boolean | undefined;
27
- config?: Record<string, unknown> | undefined;
27
+ config?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
28
28
  }, {
29
29
  order?: number | undefined;
30
30
  enabled?: boolean | undefined;
31
- config?: Record<string, unknown> | undefined;
31
+ config?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
32
32
  }>;
33
33
  export type CreateHomepageSectionInput = z.infer<typeof createSectionSchema>;
34
34
  export type UpdateHomepageSectionInput = z.infer<typeof updateSectionSchema>;
@@ -6,12 +6,12 @@ export const createSectionSchema = z.object({
6
6
  type: z.string().min(1),
7
7
  enabled: z.boolean().default(true),
8
8
  order: z.number().int().default(0),
9
- config: z.record(z.string(), z.unknown()).optional(), // audit-z-any-ok: section config validated per-type by adminSectionsBuildParse (W5 row 1)
9
+ config: z.object({}).passthrough().optional(),
10
10
  });
11
11
  export const updateSectionSchema = z.object({
12
12
  order: z.number().int().optional(),
13
13
  enabled: z.boolean().optional(),
14
- config: z.record(z.string(), z.unknown()).optional(), // audit-z-any-ok: section config validated per-type by adminSectionsBuildParse (W5 row 1)
14
+ config: z.object({}).passthrough().optional(),
15
15
  });
16
16
  // --- Actions --------------------------------------------------------------
17
17
  export async function createHomepageSection(input, createdBy) {
@@ -10,5 +10,5 @@ export function PromoGrid({ banners, eyebrow = "LIMITED TIME", heading = "HOT DE
10
10
  return null;
11
11
  return (_jsx(Section, { paddingY: "y-4xl", className: "bg-[var(--dark-section-alt)] [border-top:1px_solid_rgba(255,255,255,0.06)] [border-bottom:1px_solid_rgba(255,255,255,0.06)]", children: _jsxs(Div, { className: "mx-auto max-w-7xl", padding: "x-md", children: [_jsx(Row, { wrap: true, align: "end", justify: "between", gap: "sm", className: "mb-7 gap-y-2", children: _jsxs(Div, { children: [_jsx(Text, { className: "mb-1 font-black uppercase tracking-widest text-[var(--color-red)] tracking-[0.18em]", size: "xs", children: eyebrow }), _jsx(Heading, { level: 2, className: "[font-family:var(--font-bangers,Bangers,cursive)] text-[clamp(1.6rem,4vw,2.4rem)] tracking-[0.08em] text-white leading-none", children: heading })] }) }), _jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: banners.slice(0, 4).map((banner, i) => (_jsxs(Link, { href: banner.ctaUrl, className: `group relative block overflow-hidden bg-[var(--dark-section-card)] ${i === 0 ? "sm:row-span-2 min-h-[clamp(220px,32vh,480px)]" : "min-h-[clamp(130px,16vh,240px)]"}`, children: [_jsx(Image, { src: banner.image, alt: banner.title, fill: true, className: "object-cover transition-transform duration-500 group-hover:scale-105", sizes: i === 0
12
12
  ? "(max-width: 640px) 100vw, 40vw"
13
- : "(max-width: 640px) 50vw, 30vw" }), _jsx(Div, { className: `absolute inset-0 ${i === 0 ? "[background:linear-gradient(to_top,rgba(0,0,0,0.9)_0%,rgba(0,0,0,0.3)_50%,transparent_100%)]" : "[background:linear-gradient(to_top,rgba(0,0,0,0.85)_0%,rgba(0,0,0,0.15)_100%)]"}` }), _jsx(Div, { className: "absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100", style: { boxShadow: "inset 0 0 0 2px var(--color-yellow)" } }), _jsxs(Div, { className: `absolute bottom-0 left-0 right-0 ${__P.p4}`, children: [_jsx(Text, { className: `leading-tight [font-family:var(--font-bangers,Bangers,cursive)] tracking-[0.06em] text-white ${i === 0 ? "text-[1.35rem]" : "text-[1rem]"}`, children: banner.title }), _jsxs(Span, { layout: "inline-flex", gap: "xs", className: "mt-1 font-black uppercase text-[var(--color-yellow)]", size: "xs", children: [banner.ctaLabel, " \u2192"] })] })] }, banner.id))) })] }) }));
13
+ : "(max-width: 640px) 50vw, 30vw" }), _jsx(Div, { className: `absolute inset-0 ${i === 0 ? "[background:linear-gradient(to_top,rgba(0,0,0,0.9)_0%,rgba(0,0,0,0.3)_50%,transparent_100%)]" : "[background:linear-gradient(to_top,rgba(0,0,0,0.85)_0%,rgba(0,0,0,0.15)_100%)]"}` }), _jsx(Div, { className: "absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100 ring-2 ring-[var(--color-yellow)] ring-inset" }), _jsxs(Div, { className: `absolute bottom-0 left-0 right-0 ${__P.p4}`, children: [_jsx(Text, { className: `leading-tight [font-family:var(--font-bangers,Bangers,cursive)] tracking-[0.06em] text-white ${i === 0 ? "text-[1.35rem]" : "text-[1rem]"}`, children: banner.title }), _jsxs(Span, { layout: "inline-flex", gap: "xs", className: "mt-1 font-black uppercase text-[var(--color-yellow)]", size: "xs", children: [banner.ctaLabel, " \u2192"] })] })] }, banner.id))) })] }) }));
14
14
  }
@@ -29,7 +29,7 @@ export interface CarouselBackground {
29
29
  export type CarouselSlideHeight = "viewport" | "tall" | "medium";
30
30
  export type CarouselHoverEffect = "scale" | "color" | "glow" | "none";
31
31
  /** A content card placed in one of 6 grid zones (2 rows × 3 cols). */
32
- export interface CarouselCard {
32
+ export type CarouselCard = {
33
33
  id: string;
34
34
  /** Zone 1–6: row 1 = zones 1–3, row 2 = zones 4–6 */
35
35
  zone: 1 | 2 | 3 | 4 | 5 | 6;
@@ -61,7 +61,7 @@ export interface CarouselCard {
61
61
  gridRow?: 1 | 2;
62
62
  /** Legacy — kept for backward compat; use `zone` instead */
63
63
  gridCol?: 1 | 2 | 3;
64
- }
64
+ };
65
65
  export type GridCardCreateInput = Omit<CarouselCard, "id">;
66
66
  /** A named carousel that holds up to MAX_SLIDES_PER_CAROUSEL slide references. */
67
67
  export interface CarouselDocument extends BaseDocument {
@@ -174,14 +174,15 @@ export interface WelcomeSectionConfig {
174
174
  ctaText?: string;
175
175
  ctaLink?: string;
176
176
  }
177
+ export type TrustIndicator = {
178
+ id: string;
179
+ icon: string;
180
+ title: string;
181
+ description: string;
182
+ };
177
183
  export interface TrustIndicatorsSectionConfig {
178
184
  title: string;
179
- indicators: Array<{
180
- id: string;
181
- icon: string;
182
- title: string;
183
- description: string;
184
- }>;
185
+ indicators: TrustIndicator[];
185
186
  }
186
187
  export interface SectionCTA {
187
188
  label: string;
@@ -381,7 +382,7 @@ export type SocialFeedLayout = "grid" | "masonry" | "carousel";
381
382
  * videoId is stored; thumbnail URL is computed at render time from the video ID.
382
383
  * Do NOT store raw YouTube CDN thumbnail URLs in Firestore (they may expire).
383
384
  */
384
- export interface StaticSocialPost {
385
+ export type StaticSocialPost = {
385
386
  id: string;
386
387
  platform: SocialPlatform;
387
388
  /** YouTube video ID (e.g. "dQw4w9WgXcQ"). Required when platform is "youtube". */
@@ -389,7 +390,7 @@ export interface StaticSocialPost {
389
390
  /** YouTube channel display name shown as a chip. */
390
391
  channelName?: string;
391
392
  caption?: string;
392
- }
393
+ };
393
394
  export interface SocialFeedSectionConfig {
394
395
  title: string;
395
396
  subtitle?: string;
@@ -409,7 +410,7 @@ export interface SocialFeedSectionConfig {
409
410
  posts?: StaticSocialPost[];
410
411
  }
411
412
  /** Normalised social post returned by /api/social-feed */
412
- export interface SocialPost {
413
+ export type SocialPost = {
413
414
  id: string;
414
415
  platform: SocialPlatform;
415
416
  imageUrl?: string;
@@ -427,7 +428,7 @@ export interface SocialPost {
427
428
  comments?: number;
428
429
  };
429
430
  publishedAt?: string;
430
- }
431
+ };
431
432
  export interface PreOrdersSectionConfig {
432
433
  title: string;
433
434
  subtitle?: string;
@@ -461,7 +462,7 @@ export interface BannerSectionConfig {
461
462
  clickable: boolean;
462
463
  clickLink?: string;
463
464
  }
464
- export interface CustomCardsCard {
465
+ export type CustomCardsCard = {
465
466
  id: string;
466
467
  image?: string;
467
468
  imageAlt?: string;
@@ -479,7 +480,7 @@ export interface CustomCardsCard {
479
480
  textColor?: string;
480
481
  borderRadius?: "none" | "sm" | "md" | "lg" | "xl" | "full";
481
482
  shadowLevel?: "none" | "sm" | "md" | "lg";
482
- }
483
+ };
483
484
  export interface CustomCardsSectionConfig {
484
485
  title?: string;
485
486
  layout: "grid" | "row" | "masonry";
@@ -27,23 +27,10 @@ import { TitleBar } from "./TitleBar";
27
27
  import { BackToTop } from "./BackToTop";
28
28
  import { useDashboardNav } from "./DashboardNavContext";
29
29
  import { usePathname } from "next/navigation";
30
- // Background defaults consumed by BackgroundRenderer these are seed values
31
- // written into siteSettings on first deploy and may be overridden at runtime.
32
- // audit-hex-tokens-ok: seed defaults for siteSettings.theme.background
33
- const DEFAULT_LIGHT_BG = {
34
- type: "color",
35
- value: "#f9fafb", // audit-hex-tokens-ok: zinc-50 seed default
36
- overlay: { enabled: false, color: "#000000", opacity: 0 }, // audit-hex-tokens-ok: overlay seed default
37
- };
30
+ import { OVERLAY_FALLBACK_COLOR, SEED_DARK_BG as DEFAULT_DARK_BG, SEED_LIGHT_BG as DEFAULT_LIGHT_BG } from "./background-seed-defaults";
38
31
  const CLS_STAT_BOX = "flex flex-col items-center gap-1 p-2 bg-zinc-100 dark:bg-zinc-800 rounded-lg text-center";
39
32
  const CLS_STAT_LABEL = "text-xs text-zinc-500 dark:text-zinc-400";
40
33
  const CLS_LOGOUT_BTN = "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-error transition-colors hover:bg-error-surface hover:text-error dark:text-error dark:hover:bg-error-surface dark:hover:text-error";
41
- // audit-hex-tokens-ok: dark theme background seed default
42
- const DEFAULT_DARK_BG = {
43
- type: "color",
44
- value: "#030712", // audit-hex-tokens-ok: gray-950 seed default
45
- overlay: { enabled: false, color: "#000000", opacity: 0 }, // audit-hex-tokens-ok: overlay seed default
46
- };
47
34
  /** Collapsible accordion section for the public sidebar. */
48
35
  function CollapsibleNavGroup({ title, children, }) {
49
36
  const [open, setOpen] = useState(false);
@@ -152,8 +139,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
152
139
  value: lightBackground.value,
153
140
  overlay: {
154
141
  enabled: lightBackground.overlay?.enabled ?? false,
155
- // audit-hex-tokens-ok: black overlay fallback when admin hasn't configured one
156
- color: lightBackground.overlay?.color ?? "#000000",
142
+ color: lightBackground.overlay?.color ?? OVERLAY_FALLBACK_COLOR,
157
143
  opacity: lightBackground.overlay?.opacity ?? 0,
158
144
  },
159
145
  };
@@ -162,8 +148,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
162
148
  value: darkBackground.value,
163
149
  overlay: {
164
150
  enabled: darkBackground.overlay?.enabled ?? false,
165
- // audit-hex-tokens-ok: black overlay fallback when admin hasn't configured one
166
- color: darkBackground.overlay?.color ?? "#000000",
151
+ color: darkBackground.overlay?.color ?? OVERLAY_FALLBACK_COLOR,
167
152
  opacity: darkBackground.overlay?.opacity ?? 0,
168
153
  },
169
154
  };
@@ -0,0 +1,19 @@
1
+ export declare const SEED_LIGHT_BG: {
2
+ type: "color";
3
+ value: string;
4
+ overlay: {
5
+ enabled: boolean;
6
+ color: string;
7
+ opacity: number;
8
+ };
9
+ };
10
+ export declare const SEED_DARK_BG: {
11
+ type: "color";
12
+ value: string;
13
+ overlay: {
14
+ enabled: boolean;
15
+ color: string;
16
+ opacity: number;
17
+ };
18
+ };
19
+ export declare const OVERLAY_FALLBACK_COLOR = "#000000";