@mohasinac/appkit 4.9.4 → 4.10.0

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 (160) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
  2. package/dist/_internal/client/features/layout/SidebarCollapseToggle.d.ts +5 -1
  3. package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +8 -2
  4. package/dist/_internal/client/features/layout/sidebarPositionClasses.d.ts +20 -0
  5. package/dist/_internal/client/features/layout/sidebarPositionClasses.js +20 -0
  6. package/dist/_internal/client/hand-mode/HandModeProvider.d.ts +21 -0
  7. package/dist/_internal/client/hand-mode/HandModeProvider.js +81 -0
  8. package/dist/_internal/client/hand-mode/index.d.ts +2 -0
  9. package/dist/_internal/client/hand-mode/index.js +1 -0
  10. package/dist/_internal/server/features/auctions/actions.js +3 -2
  11. package/dist/_internal/server/features/auctions/service.d.ts +4 -3
  12. package/dist/_internal/server/features/auctions/service.js +6 -6
  13. package/dist/_internal/server/features/bundles/data.d.ts +9 -0
  14. package/dist/_internal/server/features/bundles/data.js +14 -0
  15. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  16. package/dist/_internal/server/features/bundles/index.js +1 -1
  17. package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
  18. package/dist/_internal/server/features/catalogue/og.js +27 -0
  19. package/dist/_internal/server/features/checkout/actions.d.ts +43 -0
  20. package/dist/_internal/server/features/checkout/actions.js +172 -81
  21. package/dist/_internal/server/features/checkout/index.d.ts +1 -1
  22. package/dist/_internal/server/features/checkout/index.js +1 -1
  23. package/dist/_internal/server/features/grouped/data.d.ts +12 -0
  24. package/dist/_internal/server/features/grouped/data.js +26 -1
  25. package/dist/_internal/server/features/grouped/index.d.ts +1 -1
  26. package/dist/_internal/server/features/grouped/index.js +1 -1
  27. package/dist/_internal/server/features/products/data.d.ts +23 -0
  28. package/dist/_internal/server/features/products/data.js +159 -0
  29. package/dist/_internal/server/features/products/index.d.ts +1 -1
  30. package/dist/_internal/server/features/products/index.js +1 -1
  31. package/dist/_internal/server/features/seo/robots.js +3 -2
  32. package/dist/_internal/server/features/seo/sitemap.js +70 -5
  33. package/dist/_internal/shared/config/schema.d.ts +1 -0
  34. package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
  35. package/dist/_internal/shared/features/auctions/config.js +31 -1
  36. package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
  37. package/dist/_internal/shared/listing-types/art/config.js +1 -1
  38. package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
  39. package/dist/_internal/shared/listing-types/classified/config.js +2 -2
  40. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
  41. package/dist/_internal/shared/listing-types/digital-code/config.js +2 -2
  42. package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
  43. package/dist/_internal/shared/listing-types/live/config.js +2 -2
  44. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
  45. package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
  46. package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
  47. package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
  48. package/dist/client.d.ts +4 -6
  49. package/dist/client.js +10 -4
  50. package/dist/constants/api-endpoints.d.ts +6 -0
  51. package/dist/constants/api-endpoints.js +2 -0
  52. package/dist/features/about/components/AboutView.js +2 -2
  53. package/dist/features/about/components/DeveloperView.js +2 -2
  54. package/dist/features/about/schemas/firestore.d.ts +1 -0
  55. package/dist/features/account/components/UserSidebar.js +8 -2
  56. package/dist/features/account/hooks/useProfile.d.ts +2 -0
  57. package/dist/features/admin/components/AdminSidebar.js +8 -2
  58. package/dist/features/admin/components/AdminSiteSettingsView.js +12 -4
  59. package/dist/features/admin/schemas/firestore.d.ts +9 -0
  60. package/dist/features/admin/schemas/firestore.js +6 -0
  61. package/dist/features/auctions/actions/bid-actions.js +4 -1
  62. package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
  63. package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
  64. package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
  65. package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
  66. package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
  67. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
  68. package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
  69. package/dist/features/auth/schemas/firestore.d.ts +3 -0
  70. package/dist/features/cart/components/CartDrawer.js +4 -1
  71. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
  72. package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
  73. package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
  74. package/dist/features/catalogue/index.d.ts +2 -0
  75. package/dist/features/catalogue/index.js +1 -0
  76. package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
  77. package/dist/features/categories/components/BundleDetailView.js +3 -2
  78. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
  79. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
  80. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
  81. package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
  82. package/dist/features/contact/components/ContactPageView.js +8 -2
  83. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
  84. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
  85. package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
  86. package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
  87. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +8 -0
  88. package/dist/features/grouped/repository/grouped-listings.repository.js +16 -0
  89. package/dist/features/homepage/components/BrandsSection.js +3 -1
  90. package/dist/features/homepage/components/FeaturedBundlesSection.js +12 -1
  91. package/dist/features/homepage/components/GoogleReviewsSection.js +2 -2
  92. package/dist/features/homepage/components/HeroCarousel.js +3 -1
  93. package/dist/features/homepage/components/SectionCarousel.js +5 -2
  94. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  95. package/dist/features/homepage/components/WhatsAppCommunitySection.js +3 -1
  96. package/dist/features/layout/BackToTop.js +3 -1
  97. package/dist/features/layout/BottomActions.js +4 -3
  98. package/dist/features/layout/BottomActionsContext.d.ts +10 -0
  99. package/dist/features/layout/BottomActionsContext.js +5 -1
  100. package/dist/features/layout/SidebarLayout.js +7 -1
  101. package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
  102. package/dist/features/layout/hooks/useBottomActions.js +4 -1
  103. package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
  104. package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
  105. package/dist/features/media/MediaVideo.js +17 -1
  106. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
  107. package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
  108. package/dist/features/products/components/ProductDetailPageView.js +11 -116
  109. package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
  110. package/dist/features/products/components/RelatedItemsSection.js +17 -0
  111. package/dist/features/seller/components/SellerProductShell.js +1 -1
  112. package/dist/features/seller/components/SellerSidebar.js +8 -2
  113. package/dist/features/shell/QuickFormDrawer.js +3 -1
  114. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +114 -3
  115. package/dist/hand-mode/index.d.ts +12 -0
  116. package/dist/hand-mode/index.js +11 -0
  117. package/dist/index.d.ts +2 -0
  118. package/dist/index.js +1 -0
  119. package/dist/next/routing/route-map.d.ts +2 -0
  120. package/dist/next/routing/route-map.js +7 -0
  121. package/dist/react/contexts/SessionContext.d.ts +5 -0
  122. package/dist/react/contexts/SessionContext.js +2 -0
  123. package/dist/seed/bids-seed-data.js +48 -3
  124. package/dist/seed/grouped-listings-seed-data.js +167 -0
  125. package/dist/seed/payouts-seed-data.js +4 -4
  126. package/dist/seed/products-art-seed-data.js +188 -5
  127. package/dist/seed/products-auctions-seed-data.js +215 -6
  128. package/dist/seed/products-classifieds-seed-data.js +255 -8
  129. package/dist/seed/products-digital-codes-seed-data.js +258 -19
  130. package/dist/seed/products-live-items-seed-data.js +143 -10
  131. package/dist/seed/products-preorders-seed-data.js +213 -8
  132. package/dist/seed/products-prize-draws-seed-data.js +198 -0
  133. package/dist/seed/products-standard-seed-data.js +141 -0
  134. package/dist/seed/products-stickers-seed-data.js +178 -5
  135. package/dist/seed/site-settings-seed-data.js +13 -31
  136. package/dist/seed/store-extensions-seed-data.js +1 -1
  137. package/dist/server-entry.d.ts +5 -2
  138. package/dist/server-entry.js +5 -2
  139. package/dist/server.d.ts +4 -2
  140. package/dist/server.js +4 -2
  141. package/dist/styles.css +30 -1
  142. package/dist/tailwind-utilities.css +1 -1
  143. package/dist/ui/components/Drawer.js +7 -4
  144. package/dist/ui/components/FilterDrawer.js +1 -1
  145. package/dist/ui/components/HandModeRow.d.ts +12 -0
  146. package/dist/ui/components/HandModeRow.js +18 -0
  147. package/dist/ui/components/ImageLightbox.js +37 -3
  148. package/dist/ui/components/ImageLightbox.style.css +28 -0
  149. package/dist/ui/components/Layout.d.ts +5 -1
  150. package/dist/ui/components/Layout.js +2 -1
  151. package/dist/ui/components/Layout.style.css +1 -0
  152. package/dist/ui/components/ListingFilterDrawer.js +3 -1
  153. package/dist/ui/components/Modal.js +3 -1
  154. package/dist/ui/components/SideDrawer.js +8 -5
  155. package/dist/ui/components/SideModal.js +7 -4
  156. package/dist/ui/index.d.ts +1 -0
  157. package/dist/ui/index.js +1 -0
  158. package/dist/utils/media-url.d.ts +9 -0
  159. package/dist/utils/media-url.js +39 -0
  160. package/package.json +1 -1
@@ -21,6 +21,7 @@ function staticPages(baseUrl) {
21
21
  page(String(ROUTES.PUBLIC.PRODUCTS), "hourly", 0.9),
22
22
  page(String(ROUTES.PUBLIC.AUCTIONS), "hourly", 0.9),
23
23
  page(String(ROUTES.PUBLIC.CATEGORIES), "weekly", 0.8),
24
+ page(String(ROUTES.PUBLIC.BRANDS), "weekly", 0.7),
24
25
  page(String(ROUTES.PUBLIC.BLOG), "daily", 0.7),
25
26
  page(String(ROUTES.PUBLIC.EVENTS), "daily", 0.7),
26
27
  page(String(ROUTES.PUBLIC.SELLERS), "weekly", 0.6),
@@ -39,6 +40,12 @@ function staticPages(baseUrl) {
39
40
  page(String(ROUTES.PUBLIC.REFUND_POLICY), "yearly", 0.3),
40
41
  page(String(ROUTES.PUBLIC.SHIPPING_POLICY), "yearly", 0.3),
41
42
  page(String(ROUTES.PUBLIC.PRE_ORDERS), "daily", 0.7),
43
+ page(String(ROUTES.PUBLIC.BUNDLES), "weekly", 0.6),
44
+ page(String(ROUTES.PUBLIC.PRIZE_DRAWS), "daily", 0.7),
45
+ page(String(ROUTES.PUBLIC.CLASSIFIED), "daily", 0.6),
46
+ page(String(ROUTES.PUBLIC.DIGITAL_CODES), "daily", 0.6),
47
+ page(String(ROUTES.PUBLIC.LIVE), "hourly", 0.7),
48
+ page(String(ROUTES.PUBLIC.ART), "weekly", 0.6),
42
49
  page(String(ROUTES.PUBLIC.FEES), "monthly", 0.4),
43
50
  page(String(ROUTES.PUBLIC.HOW_AUCTIONS_WORK), "monthly", 0.4),
44
51
  page(String(ROUTES.PUBLIC.HOW_PRE_ORDERS_WORK), "monthly", 0.4),
@@ -53,13 +60,16 @@ function staticPages(baseUrl) {
53
60
  page(String(ROUTES.PUBLIC.SCAM_FAQS), "weekly", 0.7),
54
61
  ];
55
62
  }
63
+ // "art" and "stickers" listing types render at the same PRODUCT_DETAIL route
64
+ // as "standard" (see route-map.ts's ART comment) — no dedicated detail page.
65
+ const PRODUCT_DETAIL_LISTING_TYPES = ["standard", "art", "stickers"];
56
66
  async function fetchProductUrls(baseUrl) {
57
67
  try {
58
68
  const db = getAdminDb();
59
69
  const snap = await db
60
70
  .collection(PRODUCT_COLLECTION)
61
71
  .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
62
- .where("listingType", "==", "standard")
72
+ .where("listingType", "in", PRODUCT_DETAIL_LISTING_TYPES)
63
73
  .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
64
74
  .limit(5000)
65
75
  .get();
@@ -80,6 +90,38 @@ async function fetchProductUrls(baseUrl) {
80
90
  return [];
81
91
  }
82
92
  }
93
+ async function fetchListingTypeUrls(baseUrl, listingType, buildUrl, label) {
94
+ try {
95
+ const db = getAdminDb();
96
+ const snap = await db
97
+ .collection(PRODUCT_COLLECTION)
98
+ .where(PRODUCT_STATUS, "==", PRODUCT_STATUS_PUBLISHED)
99
+ .where("listingType", "==", listingType)
100
+ .select(PRODUCT_SLUG, PRODUCT_UPDATED_AT)
101
+ .limit(2000)
102
+ .get();
103
+ return snap.docs.map((doc) => {
104
+ const data = doc.data();
105
+ const slug = data[PRODUCT_SLUG] ?? doc.id;
106
+ return {
107
+ url: `${baseUrl}${buildUrl(slug)}`,
108
+ lastModified: data[PRODUCT_UPDATED_AT]?.toDate?.() ?? new Date(),
109
+ changeFrequency: "daily",
110
+ priority: 0.7,
111
+ };
112
+ });
113
+ }
114
+ catch (err) {
115
+ void normalizeError(err);
116
+ serverLogger.warn(`sitemap: failed to fetch ${label} URLs`, { error: err });
117
+ return [];
118
+ }
119
+ }
120
+ const fetchPreOrderUrls = (baseUrl) => fetchListingTypeUrls(baseUrl, "pre-order", (slug) => ROUTES.PUBLIC.PRE_ORDER_DETAIL(slug), "pre-order");
121
+ const fetchPrizeDrawUrls = (baseUrl) => fetchListingTypeUrls(baseUrl, "prize-draw", (slug) => ROUTES.PUBLIC.PRIZE_DRAW_DETAIL(slug), "prize-draw");
122
+ const fetchClassifiedUrls = (baseUrl) => fetchListingTypeUrls(baseUrl, "classified", (slug) => ROUTES.PUBLIC.CLASSIFIED_DETAIL(slug), "classified");
123
+ const fetchDigitalCodeUrls = (baseUrl) => fetchListingTypeUrls(baseUrl, "digital-code", (slug) => ROUTES.PUBLIC.DIGITAL_CODE_DETAIL(slug), "digital-code");
124
+ const fetchLiveUrls = (baseUrl) => fetchListingTypeUrls(baseUrl, "live", (slug) => ROUTES.PUBLIC.LIVE_DETAIL(slug), "live");
83
125
  async function fetchAuctionUrls(baseUrl) {
84
126
  try {
85
127
  const db = getAdminDb();
@@ -133,11 +175,17 @@ async function fetchEventUrls(baseUrl) {
133
175
  return [];
134
176
  }
135
177
  }
136
- async function fetchCategoryUrls(baseUrl) {
178
+ // categories is a discriminated-union collection (categoryType: "listing" |
179
+ // "brand" | "bundle" — see CLAUDE.md's SB-UNI-C/SB-UNI-D). Each discriminator
180
+ // renders at a different public route, so each needs its own filtered query;
181
+ // querying the collection without a categoryType filter (the prior bug here)
182
+ // mapped brand/bundle rows onto the wrong /categories/{slug} URL.
183
+ async function fetchCategoryTypeUrls(baseUrl, categoryType, buildUrl, label) {
137
184
  try {
138
185
  const db = getAdminDb();
139
186
  const snap = await db
140
187
  .collection(CATEGORIES_COLLECTION)
188
+ .where(CATEGORY_FIELDS.CATEGORY_TYPE, "==", categoryType)
141
189
  .where(CATEGORY_FIELDS.IS_ACTIVE, "==", true)
142
190
  .select(CATEGORY_FIELDS.SLUG, CATEGORY_FIELDS.UPDATED_AT)
143
191
  .limit(500)
@@ -146,7 +194,7 @@ async function fetchCategoryUrls(baseUrl) {
146
194
  const data = doc.data();
147
195
  const slug = data[CATEGORY_FIELDS.SLUG] ?? doc.id;
148
196
  return {
149
- url: `${baseUrl}/categories/${slug}`,
197
+ url: `${baseUrl}${buildUrl(slug)}`,
150
198
  lastModified: data[CATEGORY_FIELDS.UPDATED_AT]?.toDate?.() ?? new Date(),
151
199
  changeFrequency: "weekly",
152
200
  priority: 0.7,
@@ -155,10 +203,13 @@ async function fetchCategoryUrls(baseUrl) {
155
203
  }
156
204
  catch (err) {
157
205
  void normalizeError(err);
158
- serverLogger.warn("sitemap: failed to fetch category URLs", { error: err });
206
+ serverLogger.warn(`sitemap: failed to fetch ${label} URLs`, { error: err });
159
207
  return [];
160
208
  }
161
209
  }
210
+ const fetchCategoryUrls = (baseUrl) => fetchCategoryTypeUrls(baseUrl, "listing", (slug) => ROUTES.PUBLIC.CATEGORY_DETAIL(slug), "category");
211
+ const fetchBrandUrls = (baseUrl) => fetchCategoryTypeUrls(baseUrl, "brand", (slug) => ROUTES.PUBLIC.BRAND_DETAIL(slug), "brand");
212
+ const fetchBundleUrls = (baseUrl) => fetchCategoryTypeUrls(baseUrl, "bundle", (slug) => ROUTES.PUBLIC.BUNDLE_DETAIL(slug), "bundle");
162
213
  async function fetchBlogPostUrls(baseUrl) {
163
214
  try {
164
215
  const db = getAdminDb();
@@ -242,21 +293,35 @@ async function fetchScammerUrls(baseUrl) {
242
293
  }
243
294
  }
244
295
  export async function buildSitemap({ baseUrl }) {
245
- const [productUrls, categoryUrls, eventUrls, blogUrls, auctionUrls, storeUrls, scammerUrls] = await Promise.all([
296
+ const [productUrls, categoryUrls, brandUrls, bundleUrls, eventUrls, blogUrls, auctionUrls, preOrderUrls, prizeDrawUrls, classifiedUrls, digitalCodeUrls, liveUrls, storeUrls, scammerUrls,] = await Promise.all([
246
297
  fetchProductUrls(baseUrl),
247
298
  fetchCategoryUrls(baseUrl),
299
+ fetchBrandUrls(baseUrl),
300
+ fetchBundleUrls(baseUrl),
248
301
  fetchEventUrls(baseUrl),
249
302
  fetchBlogPostUrls(baseUrl),
250
303
  fetchAuctionUrls(baseUrl),
304
+ fetchPreOrderUrls(baseUrl),
305
+ fetchPrizeDrawUrls(baseUrl),
306
+ fetchClassifiedUrls(baseUrl),
307
+ fetchDigitalCodeUrls(baseUrl),
308
+ fetchLiveUrls(baseUrl),
251
309
  fetchStoreUrls(baseUrl),
252
310
  fetchScammerUrls(baseUrl),
253
311
  ]);
254
312
  return [
255
313
  ...staticPages(baseUrl),
256
314
  ...categoryUrls,
315
+ ...brandUrls,
316
+ ...bundleUrls,
257
317
  ...blogUrls,
258
318
  ...productUrls,
259
319
  ...auctionUrls,
320
+ ...preOrderUrls,
321
+ ...prizeDrawUrls,
322
+ ...classifiedUrls,
323
+ ...digitalCodeUrls,
324
+ ...liveUrls,
260
325
  ...eventUrls,
261
326
  ...storeUrls,
262
327
  ...scammerUrls,
@@ -100,6 +100,7 @@ export interface AppkitBrandConfig {
100
100
  youtube?: string;
101
101
  facebook?: string;
102
102
  linkedin?: string;
103
+ github?: string;
103
104
  };
104
105
  }
105
106
  export interface AppkitSeoConfig {
@@ -4,3 +4,23 @@ export declare const AUCTIONS_SITEMAP_LIMIT = 5000;
4
4
  export declare const AUCTION_DEFAULT_EXTENSION_MINUTES = 5;
5
5
  export declare const AUCTION_MIN_BID_INCREMENT = 1;
6
6
  export declare const AUCTION_SNIPING_WINDOW_SECONDS = 300;
7
+ export interface BidIncrementTier {
8
+ /** Upper bound (inclusive) of this band's current-bid range. `null` = open-ended ("and above"), must be the last tier. */
9
+ upTo: number | null;
10
+ increment: number;
11
+ }
12
+ /** Seed/fallback tier table — single source of truth reused by both DEFAULT_SITE_SETTINGS_DATA and the admin editor's initial state. */
13
+ export declare const DEFAULT_AUCTION_BID_INCREMENT_TIERS: BidIncrementTier[];
14
+ /**
15
+ * Resolve the tiered minimum bid increment for a given current bid amount.
16
+ * Tier lookup is inclusive of each band's upper bound — e.g. with the
17
+ * default tiers, a current bid of exactly 1000 resolves to the 100
18
+ * increment (the 100-1000 tier), not the 200 increment of the next band.
19
+ */
20
+ export declare function resolveTieredBidIncrement(currentBidAmount: number, tiers: BidIncrementTier[]): number;
21
+ /**
22
+ * Effective minimum bid increment = max(admin tier floor, per-listing
23
+ * override). A seller's per-listing "Minimum Bid Increment" can require
24
+ * MORE than the platform tier, but can never undercut it.
25
+ */
26
+ export declare function resolveMinBidIncrement(currentBidAmount: number, tiers: BidIncrementTier[], override?: number | null): number;
@@ -2,5 +2,35 @@ export const AUCTIONS_PAGE_SIZE = 20;
2
2
  export const AUCTIONS_ACTIVE_LIMIT = 50;
3
3
  export const AUCTIONS_SITEMAP_LIMIT = 5000;
4
4
  export const AUCTION_DEFAULT_EXTENSION_MINUTES = 5;
5
- export const AUCTION_MIN_BID_INCREMENT = 1; // ₹1
5
+ export const AUCTION_MIN_BID_INCREMENT = 1; // ₹1 — last-resort fallback when bidIncrementTiers is empty/missing
6
6
  export const AUCTION_SNIPING_WINDOW_SECONDS = 300; // 5 minutes — triggers auto-extend
7
+ /** Seed/fallback tier table — single source of truth reused by both DEFAULT_SITE_SETTINGS_DATA and the admin editor's initial state. */
8
+ export const DEFAULT_AUCTION_BID_INCREMENT_TIERS = [
9
+ { upTo: 100, increment: 10 },
10
+ { upTo: 1000, increment: 100 },
11
+ { upTo: 5000, increment: 200 },
12
+ { upTo: 10000, increment: 500 },
13
+ { upTo: null, increment: 1000 },
14
+ ];
15
+ /**
16
+ * Resolve the tiered minimum bid increment for a given current bid amount.
17
+ * Tier lookup is inclusive of each band's upper bound — e.g. with the
18
+ * default tiers, a current bid of exactly 1000 resolves to the 100
19
+ * increment (the 100-1000 tier), not the 200 increment of the next band.
20
+ */
21
+ export function resolveTieredBidIncrement(currentBidAmount, tiers) {
22
+ for (const tier of tiers) {
23
+ if (tier.upTo === null || currentBidAmount <= tier.upTo)
24
+ return tier.increment;
25
+ }
26
+ return tiers[tiers.length - 1]?.increment ?? AUCTION_MIN_BID_INCREMENT;
27
+ }
28
+ /**
29
+ * Effective minimum bid increment = max(admin tier floor, per-listing
30
+ * override). A seller's per-listing "Minimum Bid Increment" can require
31
+ * MORE than the platform tier, but can never undercut it.
32
+ */
33
+ export function resolveMinBidIncrement(currentBidAmount, tiers, override) {
34
+ const tierValue = resolveTieredBidIncrement(currentBidAmount, tiers);
35
+ return typeof override === "number" && override > tierValue ? override : tierValue;
36
+ }
@@ -8,7 +8,7 @@ export declare const config: {
8
8
  badge: {
9
9
  label: string;
10
10
  className: string;
11
- } | undefined;
11
+ };
12
12
  priceLabel: string;
13
13
  typeLabel: string;
14
14
  showsStockQuantity: boolean;
@@ -7,7 +7,7 @@ export const config = {
7
7
  slugPrefix: "art-",
8
8
  cartLine: "single-product",
9
9
  detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(idOrSlug)),
10
- badge: undefined,
10
+ badge: { label: "Art Print", className: "bg-primary-700 text-white" },
11
11
  priceLabel: "Price (₹)",
12
12
  typeLabel: "Art Print",
13
13
  showsStockQuantity: true,
@@ -8,7 +8,7 @@ export declare const config: {
8
8
  badge: {
9
9
  label: string;
10
10
  className: string;
11
- } | undefined;
11
+ };
12
12
  priceLabel: string;
13
13
  typeLabel: string;
14
14
  showsStockQuantity: boolean;
@@ -6,8 +6,8 @@ export const config = {
6
6
  listingType: LISTING_TYPE,
7
7
  slugPrefix: "classified-",
8
8
  cartLine: "blocked",
9
- detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(idOrSlug)),
10
- badge: undefined,
9
+ detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.CLASSIFIED_DETAIL(idOrSlug)),
10
+ badge: { label: "Classified", className: "bg-secondary text-white" },
11
11
  priceLabel: "Asking Price (₹)",
12
12
  typeLabel: "Classified",
13
13
  showsStockQuantity: true,
@@ -8,7 +8,7 @@ export declare const config: {
8
8
  badge: {
9
9
  label: string;
10
10
  className: string;
11
- } | undefined;
11
+ };
12
12
  priceLabel: string;
13
13
  typeLabel: string;
14
14
  showsStockQuantity: boolean;
@@ -6,8 +6,8 @@ export const config = {
6
6
  listingType: LISTING_TYPE,
7
7
  slugPrefix: "digitalcode-",
8
8
  cartLine: "single-product",
9
- detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(idOrSlug)),
10
- badge: undefined,
9
+ detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.DIGITAL_CODE_DETAIL(idOrSlug)),
10
+ badge: { label: "Digital Code", className: "bg-success-surface text-success" },
11
11
  priceLabel: "Price per Code (₹)",
12
12
  typeLabel: "Digital Code",
13
13
  showsStockQuantity: false,
@@ -8,7 +8,7 @@ export declare const config: {
8
8
  badge: {
9
9
  label: string;
10
10
  className: string;
11
- } | undefined;
11
+ };
12
12
  priceLabel: string;
13
13
  typeLabel: string;
14
14
  showsStockQuantity: boolean;
@@ -6,8 +6,8 @@ export const config = {
6
6
  listingType: LISTING_TYPE,
7
7
  slugPrefix: "live-",
8
8
  cartLine: "single-product",
9
- detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(idOrSlug)),
10
- badge: undefined,
9
+ detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.LIVE_DETAIL(idOrSlug)),
10
+ badge: { label: "Live Item", className: "bg-danger-surface text-error" },
11
11
  priceLabel: "Price (₹)",
12
12
  typeLabel: "Live Item",
13
13
  showsStockQuantity: true,
@@ -8,7 +8,7 @@ export declare const config: {
8
8
  badge: {
9
9
  label: string;
10
10
  className: string;
11
- } | undefined;
11
+ };
12
12
  priceLabel: string;
13
13
  typeLabel: string;
14
14
  showsStockQuantity: boolean;
@@ -7,7 +7,7 @@ export const config = {
7
7
  slugPrefix: "prizedraw-",
8
8
  cartLine: "single-product",
9
9
  detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.PRIZE_DRAW_DETAIL(idOrSlug)),
10
- badge: undefined,
10
+ badge: { label: "Prize Draw", className: "bg-primary text-white" },
11
11
  priceLabel: "Price (₹)",
12
12
  typeLabel: "Prize Draw",
13
13
  showsStockQuantity: false,
@@ -8,7 +8,7 @@ export declare const config: {
8
8
  badge: {
9
9
  label: string;
10
10
  className: string;
11
- } | undefined;
11
+ };
12
12
  priceLabel: string;
13
13
  typeLabel: string;
14
14
  showsStockQuantity: boolean;
@@ -7,7 +7,7 @@ export const config = {
7
7
  slugPrefix: "sticker-",
8
8
  cartLine: "single-product",
9
9
  detailRoute: (idOrSlug) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(idOrSlug)),
10
- badge: undefined,
10
+ badge: { label: "Sticker Sheet", className: "bg-primary-700 text-white" },
11
11
  priceLabel: "Price (₹)",
12
12
  typeLabel: "Stickers",
13
13
  showsStockQuantity: true,
package/dist/client.d.ts CHANGED
@@ -24,11 +24,15 @@ export { registerClientRealtimeProvider, getClientRealtimeProvider, type IClient
24
24
  export { registerClientSessionAdapter, getClientSessionAdapter, type IClientSessionAdapter, type AdapterAuthUser, type AuthUnsubscribe, } from "./contracts/client-session";
25
25
  export { ThemeProvider, useTheme, buildThemeRegistry } from "./theme";
26
26
  export type { ModePreference, SiteSettingsThemeInput, ThemeContextValue, ThemeProviderProps, ThemeRegistry, } from "./theme";
27
+ export { HandModeProvider, useHandMode } from "./hand-mode";
28
+ export type { HandMode, HandModeContextValue, HandModeProviderProps, } from "./hand-mode";
27
29
  export { BUILT_IN_THEMES, DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, REQUIRED_GRADIENT_KEYS, REQUIRED_THEME_TOKENS, getDefaultBuiltInTheme, } from "./tokens/themes";
28
30
  export type { GradientKey, RequiredThemeToken, ThemeMode, ThemeRecord, } from "./tokens/themes";
29
31
  export { ConfirmDeleteModal } from "./ui/components/ConfirmDeleteModal";
30
32
  export { EmptyState } from "./ui/components/EmptyState";
31
33
  export { Skeleton } from "./ui/components/Skeleton";
34
+ export { IconBox } from "./ui/components/IconBox";
35
+ export type { IconBoxProps, IconBoxRounded, IconBoxSize, IconBoxTone, } from "./ui/components/IconBox";
32
36
  export { Alert } from "./ui/components/Alert";
33
37
  export type { SkeletonProps } from "./ui/components/Skeleton";
34
38
  export { Divider } from "./ui/components/Divider";
@@ -327,14 +331,8 @@ export { ActionPermissionsManager } from "./features/site-settings/components/Ac
327
331
  export type { ActionPermissionsManagerProps } from "./features/site-settings/components/ActionPermissionsManager";
328
332
  export { NavPermissionsManager } from "./features/site-settings/components/NavPermissionsManager";
329
333
  export type { NavPermissionsManagerProps, NavGroup as NavPermissionsGroup, NavItem as NavPermissionsItem } from "./features/site-settings/components/NavPermissionsManager";
330
- export { ClassifiedDetailView } from "./_internal/client/features/classified/ClassifiedDetailView";
331
- export type { ClassifiedDetailViewProps } from "./_internal/client/features/classified/ClassifiedDetailView";
332
- export { DigitalCodeDetailView } from "./_internal/client/features/digital-code/DigitalCodeDetailView";
333
- export type { DigitalCodeDetailViewProps } from "./_internal/client/features/digital-code/DigitalCodeDetailView";
334
334
  export { CodeRevealPanel } from "./_internal/client/features/digital-code/CodeRevealPanel";
335
335
  export type { CodeRevealPanelProps, RevealedCode } from "./_internal/client/features/digital-code/CodeRevealPanel";
336
- export { LiveItemDetailView } from "./_internal/client/features/live/LiveItemDetailView";
337
- export type { LiveItemDetailViewProps } from "./_internal/client/features/live/LiveItemDetailView";
338
336
  export { PhysicalLocationModal } from "./features/seller/components/PhysicalLocationModal";
339
337
  export type { PhysicalLocation } from "./features/seller/components/PhysicalLocationModal";
340
338
  export { Toggle } from "./ui/components/Toggle";
package/dist/client.js CHANGED
@@ -32,6 +32,12 @@ export { registerClientSessionAdapter, getClientSessionAdapter, } from "./contra
32
32
  // Re-exported through the public `./theme` barrel to keep audit-appkit-reexports
33
33
  // clean (no `_internal/` symbols may surface through public barrels).
34
34
  export { ThemeProvider, useTheme, buildThemeRegistry } from "./theme";
35
+ // [CLIENT-ONLY] HandModeProvider — applies the "left-hand mode" layout
36
+ // preference to `<html>` via a `data-hand` attribute so drawers/sidebars/
37
+ // close-buttons/floating CTAs flip which screen edge they dock to.
38
+ // Re-exported through the public `./hand-mode` barrel to keep
39
+ // audit-appkit-reexports clean (no `_internal/` symbols in public barrels).
40
+ export { HandModeProvider, useHandMode } from "./hand-mode";
35
41
  export { BUILT_IN_THEMES, DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, REQUIRED_GRADIENT_KEYS, REQUIRED_THEME_TOKENS, getDefaultBuiltInTheme, } from "./tokens/themes";
36
42
  // [CLIENT-ONLY]-Cannot run in SSR mode â€" uses browser-only APIs (window, navigator, localStorage, matchMedia, DOM events) that do not exist in Node.js.
37
43
  // ConfirmDeleteModal - Component for confirm delete modal.
@@ -39,6 +45,9 @@ export { ConfirmDeleteModal } from "./ui/components/ConfirmDeleteModal";
39
45
  // S-STORE — pure-UI primitives + Seller view consumed by new dashboard pages
40
46
  export { EmptyState } from "./ui/components/EmptyState";
41
47
  export { Skeleton } from "./ui/components/Skeleton";
48
+ // IconBox — square-icon container primitive; was exported from index.ts only,
49
+ // missing here even though CLAUDE.md documents it as a client-usable pattern.
50
+ export { IconBox } from "./ui/components/IconBox";
42
51
  export { Alert } from "./ui/components/Alert";
43
52
  export { Divider } from "./ui/components/Divider";
44
53
  export { Tabs, TabsList, TabsTrigger, TabsContent } from "./ui/components/Tabs";
@@ -324,11 +333,8 @@ export { SellerTrustBadge } from "./features/scams/components/SellerTrustBadge";
324
333
  // [CLIENT-ONLY] — Admin panel components for action/nav permission management.
325
334
  export { ActionPermissionsManager } from "./features/site-settings/components/ActionPermissionsManager";
326
335
  export { NavPermissionsManager } from "./features/site-settings/components/NavPermissionsManager";
327
- // ── Classified + digital-code + live-item client views ────────────────────────
328
- export { ClassifiedDetailView } from "./_internal/client/features/classified/ClassifiedDetailView";
329
- export { DigitalCodeDetailView } from "./_internal/client/features/digital-code/DigitalCodeDetailView";
336
+ // ── Digital-code code-reveal panel (still used directly on order pages) ───────
330
337
  export { CodeRevealPanel } from "./_internal/client/features/digital-code/CodeRevealPanel";
331
- export { LiveItemDetailView } from "./_internal/client/features/live/LiveItemDetailView";
332
338
  export { PhysicalLocationModal } from "./features/seller/components/PhysicalLocationModal";
333
339
  // [CLIENT-ONLY] — Toggle switch primitive.
334
340
  export { Toggle } from "./ui/components/Toggle";
@@ -66,6 +66,7 @@ export declare const ACCOUNT_ENDPOINTS: {
66
66
  readonly CATALOGUE_LIST: (id: string) => string;
67
67
  readonly CATALOGUE_SUBMIT: (id: string) => string;
68
68
  readonly PUBLIC_CATALOGUE: (ownerSlug: string) => string;
69
+ readonly PUBLIC_CATALOGUE_ITEM: (ownerSlug: string, itemId: string) => string;
69
70
  /** Tester Hub — GET active checklist items joined with the tester's own responses; PUT upserts one item's answer/comment/screenshot. */
70
71
  readonly TESTER_CHECKLIST: "/api/user/tester-checklist";
71
72
  readonly TESTER_CHECKLIST_ITEM_BY_ID: (checklistItemId: string) => string;
@@ -249,6 +250,7 @@ export declare const CATEGORY_ENDPOINTS: {
249
250
  export declare const CHECKOUT_ENDPOINTS: {
250
251
  readonly PREFLIGHT: "/api/checkout/preflight";
251
252
  readonly PLACE_ORDER: "/api/checkout";
253
+ readonly PRICING_PREVIEW: "/api/checkout/pricing-preview";
252
254
  };
253
255
  export declare const PAYMENT_ENDPOINTS: {
254
256
  readonly CREATE_ORDER: "/api/payment/create-order";
@@ -517,6 +519,7 @@ export declare const API_ENDPOINTS: {
517
519
  readonly CATALOGUE_LIST: (id: string) => string;
518
520
  readonly CATALOGUE_SUBMIT: (id: string) => string;
519
521
  readonly PUBLIC_CATALOGUE: (ownerSlug: string) => string;
522
+ readonly PUBLIC_CATALOGUE_ITEM: (ownerSlug: string, itemId: string) => string;
520
523
  /** Tester Hub — GET active checklist items joined with the tester's own responses; PUT upserts one item's answer/comment/screenshot. */
521
524
  readonly TESTER_CHECKLIST: "/api/user/tester-checklist";
522
525
  readonly TESTER_CHECKLIST_ITEM_BY_ID: (checklistItemId: string) => string;
@@ -703,6 +706,7 @@ export declare const API_ENDPOINTS: {
703
706
  readonly CHECKOUT: {
704
707
  readonly PREFLIGHT: "/api/checkout/preflight";
705
708
  readonly PLACE_ORDER: "/api/checkout";
709
+ readonly PRICING_PREVIEW: "/api/checkout/pricing-preview";
706
710
  };
707
711
  readonly PAYMENT: {
708
712
  readonly CREATE_ORDER: "/api/payment/create-order";
@@ -970,6 +974,7 @@ export declare const API_ROUTES: {
970
974
  readonly CATALOGUE_LIST: (id: string) => string;
971
975
  readonly CATALOGUE_SUBMIT: (id: string) => string;
972
976
  readonly PUBLIC_CATALOGUE: (ownerSlug: string) => string;
977
+ readonly PUBLIC_CATALOGUE_ITEM: (ownerSlug: string, itemId: string) => string;
973
978
  /** Tester Hub — GET active checklist items joined with the tester's own responses; PUT upserts one item's answer/comment/screenshot. */
974
979
  readonly TESTER_CHECKLIST: "/api/user/tester-checklist";
975
980
  readonly TESTER_CHECKLIST_ITEM_BY_ID: (checklistItemId: string) => string;
@@ -1156,6 +1161,7 @@ export declare const API_ROUTES: {
1156
1161
  readonly CHECKOUT: {
1157
1162
  readonly PREFLIGHT: "/api/checkout/preflight";
1158
1163
  readonly PLACE_ORDER: "/api/checkout";
1164
+ readonly PRICING_PREVIEW: "/api/checkout/pricing-preview";
1159
1165
  };
1160
1166
  readonly PAYMENT: {
1161
1167
  readonly CREATE_ORDER: "/api/payment/create-order";
@@ -81,6 +81,7 @@ export const ACCOUNT_ENDPOINTS = {
81
81
  CATALOGUE_LIST: (id) => `/api/user/catalogue/${id}/list`,
82
82
  CATALOGUE_SUBMIT: (id) => `/api/user/catalogue/${id}/submit`,
83
83
  PUBLIC_CATALOGUE: (ownerSlug) => `/api/catalogue/${ownerSlug}`,
84
+ PUBLIC_CATALOGUE_ITEM: (ownerSlug, itemId) => `/api/catalogue/${ownerSlug}/${itemId}`,
84
85
  /** Tester Hub — GET active checklist items joined with the tester's own responses; PUT upserts one item's answer/comment/screenshot. */
85
86
  TESTER_CHECKLIST: "/api/user/tester-checklist",
86
87
  TESTER_CHECKLIST_ITEM_BY_ID: (checklistItemId) => `/api/user/tester-checklist/${encodeURIComponent(checklistItemId)}`,
@@ -295,6 +296,7 @@ export const CATEGORY_ENDPOINTS = {
295
296
  export const CHECKOUT_ENDPOINTS = {
296
297
  PREFLIGHT: "/api/checkout/preflight",
297
298
  PLACE_ORDER: "/api/checkout",
299
+ PRICING_PREVIEW: "/api/checkout/pricing-preview",
298
300
  };
299
301
  // ---------------------------------------------------------------------------
300
302
  // Payment
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Avatar, Badge, Card, Div, Heading, Row, Section, Span, Stack, Text } from "../../../ui";
2
+ import { Anchor, Avatar, Badge, Card, Div, Heading, Row, Section, Span, Stack, Text } from "../../../ui";
3
3
  export function AboutView({ labels = {}, howItems = [], valueItems = [], milestones = [], teamMembers = [], ctaBannerClass = "", renderCtaButtons, }) {
4
- return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "y-4xl", children: _jsxs(Div, { paddingX: "x-page", className: "max-w-4xl mx-auto text-center", children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-6", mdSize: "5xl", size: "4xl", weight: "bold", children: labels.title }), _jsx(Text, { color: "inverse", variant: "none", className: "/80 max-w-2xl mx-auto", size: "xl", children: labels.subtitle })] }) }), _jsxs(Div, { paddingX: "x-page", className: "max-w-5xl mx-auto", padding: "y-4xl", children: [_jsxs(Section, { className: "text-center max-w-3xl mx-auto", children: [_jsx(Heading, { level: 2, className: "mb-4", children: labels.missionTitle }), _jsx(Text, { size: "lg", variant: "secondary", className: "leading-relaxed", children: labels.missionText })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-12", align: "center", children: labels.howItWorksTitle }), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: howItems.map(({ title, text, icon, tone }) => (_jsxs(Card, { variant: `gradient-${tone ?? "indigo"}`, padding: "md", spacing: "sm", children: [_jsx(Div, { children: _jsx(Span, { size: "4xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text })] }, title))) })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-12", align: "center", children: labels.valuesTitle }), _jsx(Div, { className: "grid md:grid-cols-3", gap: "5", children: valueItems.map(({ title, text, icon }) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsx(Div, { children: _jsx(Span, { size: "3xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text })] }, title))) })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-10", align: "center", children: labels.milestonesTitle }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl mx-auto pl-[2rem]", gap: "xl", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] }), teamMembers.length > 0 && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-2", align: "center", children: labels.teamTitle }), labels.teamSubtitle && (_jsx(Text, { size: "lg", variant: "secondary", align: "center", className: "mb-12 max-w-2xl mx-auto", children: labels.teamSubtitle })), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: teamMembers.map((member) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", align: "center", className: "text-center", children: [_jsx(Avatar, { src: member.photoUrl, name: member.name, alt: member.name, size: "lg" }), _jsx(Heading, { level: 3, children: member.name }), _jsxs(Row, { gap: "xs", wrap: true, justify: "center", children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: member.role }), member.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), member.isDeveloper && _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: member.bio })] }, member.name))) })] })), _jsxs(Section, { color: "inverse", tone: "accent-banner", className: `text-center ${ctaBannerClass}`, padding: "xl", rounded: "2xl", children: [_jsx(Heading, { color: "inverse", level: 2, variant: "none", className: "mb-8", size: "3xl", weight: "bold", children: labels.ctaTitle }), renderCtaButtons ? (renderCtaButtons()) : (_jsx(Row, { justify: "center", gap: "3", wrap: true, children: _jsxs(Span, { color: "inverse", size: "sm", className: "/70", children: [labels.ctaSell, " \u00B7 ", labels.ctaShop] }) }))] })] })] }));
4
+ return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "y-4xl", children: _jsxs(Div, { paddingX: "x-page", className: "max-w-4xl mx-auto text-center", children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-6", mdSize: "5xl", size: "4xl", weight: "bold", children: labels.title }), _jsx(Text, { color: "inverse", variant: "none", className: "/80 max-w-2xl mx-auto", size: "xl", children: labels.subtitle })] }) }), _jsxs(Div, { paddingX: "x-page", className: "max-w-5xl mx-auto", padding: "y-4xl", children: [_jsxs(Section, { className: "text-center max-w-3xl mx-auto", children: [_jsx(Heading, { level: 2, className: "mb-4", children: labels.missionTitle }), _jsx(Text, { size: "lg", variant: "secondary", className: "leading-relaxed", children: labels.missionText })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-12", align: "center", children: labels.howItWorksTitle }), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: howItems.map(({ title, text, icon, tone }) => (_jsxs(Card, { variant: `gradient-${tone ?? "indigo"}`, padding: "md", spacing: "sm", children: [_jsx(Div, { children: _jsx(Span, { size: "4xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text })] }, title))) })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-12", align: "center", children: labels.valuesTitle }), _jsx(Div, { className: "grid md:grid-cols-3", gap: "5", children: valueItems.map(({ title, text, icon }) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsx(Div, { children: _jsx(Span, { size: "3xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text })] }, title))) })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-10", align: "center", children: labels.milestonesTitle }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl mx-auto pl-[2rem]", gap: "xl", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] }), teamMembers.length > 0 && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-2", align: "center", children: labels.teamTitle }), labels.teamSubtitle && (_jsx(Text, { size: "lg", variant: "secondary", align: "center", className: "mb-12 max-w-2xl mx-auto", children: labels.teamSubtitle })), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: teamMembers.map((member) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", align: "center", className: "text-center", children: [_jsx(Avatar, { src: member.photoUrl, name: member.name, alt: member.name, size: "lg" }), _jsx(Heading, { level: 3, children: member.name }), _jsxs(Row, { gap: "xs", wrap: true, justify: "center", children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: member.role }), member.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), member.isDeveloper && _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: member.bio }), member.githubUrl && (_jsx(Anchor, { href: member.githubUrl, tone: "muted", size: "sm", underline: "hover", children: "GitHub \u2197" }))] }, member.name))) })] })), _jsxs(Section, { color: "inverse", tone: "accent-banner", className: `text-center ${ctaBannerClass}`, padding: "xl", rounded: "2xl", children: [_jsx(Heading, { color: "inverse", level: 2, variant: "none", className: "mb-8", size: "3xl", weight: "bold", children: labels.ctaTitle }), renderCtaButtons ? (renderCtaButtons()) : (_jsx(Row, { justify: "center", gap: "3", wrap: true, children: _jsxs(Span, { color: "inverse", size: "sm", className: "/70", children: [labels.ctaSell, " \u00B7 ", labels.ctaShop] }) }))] })] })] }));
5
5
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { normalizeError } from "../../../errors/normalize";
3
- import { Avatar, Badge, Div, Heading, Row, Section, Span, Stack, Text, TextLink } from "../../../ui";
3
+ import { Anchor, Avatar, Badge, Div, Heading, Row, Section, Span, Stack, Text, TextLink } from "../../../ui";
4
4
  import { ROUTES } from "../../../constants";
5
5
  import { PAGE_CONTAINER } from "../../../_internal/shared/styles/page";
6
6
  import { siteSettingsRepository } from "../../../repositories";
@@ -25,5 +25,5 @@ export async function DeveloperView({ labels = {} }) {
25
25
  // Firestore unavailable — render the empty state below rather than crash.
26
26
  }
27
27
  const developers = teamMembers.filter((m) => m.isDeveloper);
28
- return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Div, { className: `${page.container.sm}`, children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: labels.title ?? "Meet the Developer" }), _jsx(Text, { color: "inverse", variant: "none", className: "/80", size: "sm", children: labels.subtitle ?? "The person building LetItRip, one feature at a time." })] }) }), _jsxs(Div, { className: `${page.container.sm}`, padding: "content-banner", children: [developers.length === 0 ? (_jsx(Text, { variant: "secondary", children: labels.empty ?? "Developer profile coming soon." })) : (_jsx(Stack, { gap: "xl", children: developers.map((dev) => (_jsxs(Stack, { gap: "md", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsxs(Row, { gap: "md", align: "center", children: [_jsx(Avatar, { src: dev.photoUrl, name: dev.name, alt: dev.name, size: "lg" }), _jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, children: dev.name }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: dev.role }), dev.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] })] })] }), _jsx(Text, { variant: "secondary", className: "leading-relaxed", children: dev.bio })] }, dev.name))) })), milestones.length > 0 && (_jsxs(Div, { className: "mt-14 border-t", border: "default", padding: "t-xl", children: [_jsx(Heading, { level: 3, className: "mb-6", color: "muted", size: "sm", weight: "semibold", transform: "uppercase", children: labels.journeyTitle ?? "The Journey" }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl pl-[2rem]", gap: "lg", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] })), _jsx(Div, { className: "mt-10", children: _jsx(TextLink, { href: String(ROUTES.PUBLIC.ABOUT), variant: "muted", size: "sm", children: labels.backToTeam ?? "← Meet the whole team" }) })] })] }));
28
+ return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "banner", children: _jsxs(Div, { className: `${page.container.sm}`, children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-3", children: labels.title ?? "Meet the Developer" }), _jsx(Text, { color: "inverse", variant: "none", className: "/80", size: "sm", children: labels.subtitle ?? "The person building LetItRip, one feature at a time." })] }) }), _jsxs(Div, { className: `${page.container.sm}`, padding: "content-banner", children: [developers.length === 0 ? (_jsx(Text, { variant: "secondary", children: labels.empty ?? "Developer profile coming soon." })) : (_jsx(Stack, { gap: "xl", children: developers.map((dev) => (_jsxs(Stack, { gap: "md", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsxs(Row, { gap: "md", align: "center", children: [_jsx(Avatar, { src: dev.photoUrl, name: dev.name, alt: dev.name, size: "lg" }), _jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, children: dev.name }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: dev.role }), dev.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] })] })] }), _jsx(Text, { variant: "secondary", className: "leading-relaxed", children: dev.bio }), dev.githubUrl && (_jsx(Anchor, { href: dev.githubUrl, tone: "brand", size: "sm", underline: "hover", children: "GitHub \u2197" }))] }, dev.name))) })), milestones.length > 0 && (_jsxs(Div, { className: "mt-14 border-t", border: "default", padding: "t-xl", children: [_jsx(Heading, { level: 3, className: "mb-6", color: "muted", size: "sm", weight: "semibold", transform: "uppercase", children: labels.journeyTitle ?? "The Journey" }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl pl-[2rem]", gap: "lg", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] })), _jsx(Div, { className: "mt-10", children: _jsx(TextLink, { href: String(ROUTES.PUBLIC.ABOUT), variant: "muted", size: "sm", children: labels.backToTeam ?? "← Meet the whole team" }) })] })] }));
29
29
  }
@@ -26,6 +26,7 @@ export interface AboutTeamMember {
26
26
  photoUrl?: string;
27
27
  isFounder?: boolean;
28
28
  isDeveloper?: boolean;
29
+ githubUrl?: string;
29
30
  }
30
31
  export interface AboutContentDocument {
31
32
  title: string;
@@ -9,6 +9,8 @@ import { BottomSheet } from "../../layout/BottomSheet";
9
9
  import { SidebarCollapseToggle } from "../../../_internal/client/features/layout/SidebarCollapseToggle";
10
10
  import { useSidebarSearch } from "../../../_internal/client/features/layout/useSidebarSearch";
11
11
  import { findActiveNavGroup, findActiveNavItem } from "../../../_internal/client/features/layout/navActive";
12
+ import { getSidebarRailClasses, getSidebarOverlayClasses } from "../../../_internal/client/features/layout/sidebarPositionClasses";
13
+ import { useHandMode } from "../../../_internal/client/hand-mode";
12
14
  const __O = {
13
15
  hidden: "overflow-hidden",
14
16
  yAuto: "overflow-y-auto",
@@ -75,11 +77,15 @@ function DrawerContent({ groups, items, activeHref, onItemClick, }) {
75
77
  })] }));
76
78
  }
77
79
  function DrawerPanel({ title, onClose, children, }) {
78
- return (_jsxs(Div, { className: "hidden lg:block", children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 z-40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs(Stack, { border: "default", shadow: "2xl", role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 border-l", surface: "default", children: [_jsxs(Row, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", align: "center", justify: "between", children: [_jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: title }), _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "sm", onClick: onClose, icon: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: children })] })] }));
80
+ const { hand } = useHandMode();
81
+ const { edgeClass, borderClass } = getSidebarOverlayClasses(hand);
82
+ return (_jsxs(Div, { className: "hidden lg:block", children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 z-40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs(Stack, { border: "default", shadow: "2xl", role: "dialog", "aria-modal": "true", "aria-label": title, className: `fixed top-0 ${edgeClass} z-50 h-full w-64 ${borderClass}`, surface: "default", children: [_jsxs(Row, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", align: "center", justify: "between", children: [_jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: title }), _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "sm", onClick: onClose, icon: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: children })] })] }));
79
83
  }
80
84
  export function UserSidebar({ items, groups, mobileOpen = false, onCloseMobile, desktopOpen = false, onToggle, variant = "overlay", renderFooter }) {
81
85
  const pathname = usePathname();
82
86
  const close = onCloseMobile ?? (() => { });
87
+ const { hand } = useHandMode();
88
+ const rail = getSidebarRailClasses(hand);
83
89
  const [mounted, setMounted] = useState(false);
84
90
  useEffect(() => { setMounted(true); }, []);
85
91
  useEffect(() => {
@@ -91,7 +97,7 @@ export function UserSidebar({ items, groups, mobileOpen = false, onCloseMobile,
91
97
  // ── Persistent sidebar variant (desktop left slide-over, mobile bottom-sheet) ──────
92
98
  if (variant === "sidebar") {
93
99
  const handleToggle = onToggle ?? close;
94
- return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { surface: "overlay-xs", className: "hidden lg:block fixed inset-0 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: `hidden lg:flex fixed left-0 z-40 transition-transform duration-300 top-[var(--header-height,3.5rem)] h-[calc(100vh-var(--header-height,3.5rem))] w-[13rem] ${desktopOpen ? "translate-x-0" : "-translate-x-[calc(100%-1.25rem)]"}`, children: [_jsxs(Stack, { border: "default", surface: "default", className: `flex-1 border-r dark:border-[var(--appkit-color-border)] ${__O.hidden}`, shadow: "xl", children: [_jsx(Div, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", children: _jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: "My Account" }) }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname }) }), renderFooter && _jsx(Div, { border: "top", padding: "inline", children: renderFooter() })] }), _jsx(SidebarCollapseToggle, { expanded: desktopOpen, onToggle: handleToggle })] }), _jsx(Div, { className: "lg:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "My Account", children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname, onItemClick: close }) }) })] }));
100
+ return (_jsxs(_Fragment, { children: [desktopOpen && (_jsx(Div, { surface: "overlay-xs", className: "hidden lg:block fixed inset-0 backdrop-blur-[2px] z-30", onClick: handleToggle, "aria-hidden": "true" })), _jsxs(Div, { className: `hidden lg:flex fixed ${rail.edgeClass} z-40 transition-transform duration-300 top-[var(--header-height,3.5rem)] h-[calc(100vh-var(--header-height,3.5rem))] w-[13rem] ${desktopOpen ? "translate-x-0" : rail.collapsedTranslateClass}`, children: [_jsxs(Stack, { border: "default", surface: "default", className: `flex-1 ${rail.borderClass} dark:border-[var(--appkit-color-border)] ${__O.hidden}`, shadow: "xl", children: [_jsx(Div, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", children: _jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: "My Account" }) }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname }) }), renderFooter && _jsx(Div, { border: "top", padding: "inline", children: renderFooter() })] }), _jsx(SidebarCollapseToggle, { expanded: desktopOpen, onToggle: handleToggle, edge: hand })] }), _jsx(Div, { className: "lg:hidden", children: _jsx(BottomSheet, { open: mobileOpen, onClose: close, title: "My Account", children: _jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname, onItemClick: close }) }) })] }));
95
101
  }
96
102
  // ── Overlay variant (default) — portal on desktop, bottom-sheet on mobile ─
97
103
  const content = (_jsx(DrawerContent, { groups: groups, items: items, activeHref: pathname, onItemClick: close }));
@@ -14,6 +14,7 @@ export interface UserProfile {
14
14
  uiPreferences?: {
15
15
  collapsedSections?: string[];
16
16
  dataViewMode?: "table" | "grid" | "list";
17
+ handMode?: "left" | "right";
17
18
  };
18
19
  }
19
20
  export interface UpdateCurrentProfileInput {
@@ -33,6 +34,7 @@ export interface UpdateCurrentProfileInput {
33
34
  uiPreferences?: {
34
35
  collapsedSections?: string[];
35
36
  dataViewMode?: "table" | "grid" | "list";
37
+ handMode?: "left" | "right";
36
38
  };
37
39
  }
38
40
  export declare function useCurrentProfile(options?: {