@mohasinac/appkit 4.1.3 → 4.2.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 (54) hide show
  1. package/dist/_internal/server/features/checkout/actions.d.ts +15 -0
  2. package/dist/_internal/server/features/checkout/actions.js +245 -182
  3. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  4. package/dist/_internal/server/functions/scheduled.js +9 -1
  5. package/dist/_internal/server/jobs/core/jobRunners.js +10 -0
  6. package/dist/_internal/server/jobs/core/newsletterExport.d.ts +15 -0
  7. package/dist/_internal/server/jobs/core/newsletterExport.js +43 -0
  8. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +2 -0
  9. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +1 -0
  10. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +1 -0
  11. package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +1 -0
  12. package/dist/_internal/server/jobs/core/revenueRollup.d.ts +13 -0
  13. package/dist/_internal/server/jobs/core/revenueRollup.js +22 -0
  14. package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +35 -0
  15. package/dist/_internal/server/jobs/core/whatsappNotify.js +82 -0
  16. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  17. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  18. package/dist/_internal/server/jobs/handlers/revenueRollup.d.ts +2 -0
  19. package/dist/_internal/server/jobs/handlers/revenueRollup.js +2 -0
  20. package/dist/_internal/shared/features/checkout/config.d.ts +7 -0
  21. package/dist/_internal/shared/features/checkout/config.js +7 -0
  22. package/dist/_internal/shared/fees/calculator.d.ts +26 -0
  23. package/dist/_internal/shared/fees/calculator.js +25 -0
  24. package/dist/features/about/components/FeesView.js +18 -0
  25. package/dist/features/admin/actions/notification-actions.d.ts +4 -1
  26. package/dist/features/admin/actions/notification-actions.js +56 -10
  27. package/dist/features/admin/components/AdminAdsView.js +1 -1
  28. package/dist/features/admin/components/AdminNewsletterView.js +42 -11
  29. package/dist/features/admin/components/AdminSiteSettingsView.js +83 -6
  30. package/dist/features/admin/repository/analytics-rollup.repository.d.ts +25 -0
  31. package/dist/features/admin/repository/analytics-rollup.repository.js +31 -0
  32. package/dist/features/admin/repository/site-settings.repository.js +8 -1
  33. package/dist/features/admin/schemas/firestore.d.ts +41 -0
  34. package/dist/features/admin/schemas/firestore.js +62 -24
  35. package/dist/features/layout/BackToTop.d.ts +9 -5
  36. package/dist/features/layout/BackToTop.js +17 -21
  37. package/dist/features/orders/actions/refund-actions.js +1 -0
  38. package/dist/features/orders/schemas/firestore.d.ts +14 -0
  39. package/dist/features/products/constants/action-defs.d.ts +28 -3
  40. package/dist/features/products/constants/action-defs.js +76 -1
  41. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  42. package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +10 -1
  43. package/dist/features/whatsapp-bot/helpers/whatsapp.js +47 -0
  44. package/dist/features/whatsapp-bot/types/index.d.ts +19 -0
  45. package/dist/index.d.ts +4 -2
  46. package/dist/index.js +4 -2
  47. package/dist/next/routing/route-map.d.ts +2 -0
  48. package/dist/next/routing/route-map.js +1 -0
  49. package/dist/repositories/index.d.ts +2 -0
  50. package/dist/repositories/index.js +1 -0
  51. package/dist/seed/site-settings-seed-data.js +7 -0
  52. package/dist/server.d.ts +2 -2
  53. package/dist/server.js +2 -2
  54. package/package.json +1 -1
@@ -189,6 +189,13 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
189
189
  codDepositPercent: 10,
190
190
  codHandlingFeeMin: 200,
191
191
  codHandlingFeePercent: 10,
192
+ whatsappNotifyFeeEnabled: false,
193
+ whatsappNotifyFee: 10,
194
+ giftWrapFeeEnabled: false,
195
+ giftWrapFee: 49,
196
+ shipmentProtectionFeeEnabled: false,
197
+ shipmentProtectionFeePercent: 2,
198
+ shipmentProtectionFeeMin: 30,
192
199
  sellerShippingFixed: 0,
193
200
  platformShippingPercent: 10,
194
201
  platformShippingFixedMin: 0,
@@ -284,61 +291,92 @@ export const DEFAULT_TRUST_BAR_ITEMS = [
284
291
  { icon: "??", label: "24/7 Support", visible: true },
285
292
  { icon: "?", label: "Authentic Sellers", visible: true },
286
293
  ];
294
+ // NOTE: labelKey/descKey hold plain display text (not i18n message keys) —
295
+ // this feature-flag list has no matching next-intl namespace, so the admin
296
+ // UI reads these two fields directly rather than through getTranslations().
287
297
  export const FEATURE_FLAG_META = [
288
298
  {
289
299
  key: "chats",
290
- labelKey: "chats",
291
- descKey: "chatsDesc",
292
- icon: "??",
300
+ labelKey: "In-app chats",
301
+ descKey: "Buyer/seller direct messaging.",
302
+ icon: "\u{1F4AC}",
293
303
  category: "platform",
294
304
  },
295
305
  {
296
306
  key: "smsVerification",
297
- labelKey: "smsVerification",
298
- descKey: "smsVerificationDesc",
299
- icon: "??",
307
+ labelKey: "SMS verification",
308
+ descKey: "Phone-number OTP verification during signup/checkout.",
309
+ icon: "\u{1F4F1}",
300
310
  category: "platform",
301
311
  },
302
312
  {
303
313
  key: "translations",
304
- labelKey: "translations",
305
- descKey: "translationsDesc",
306
- icon: "??",
314
+ labelKey: "Translations",
315
+ descKey: "Locale switcher and translated UI strings.",
316
+ icon: "\u{1F310}",
307
317
  category: "platform",
308
318
  },
309
319
  {
310
320
  key: "auctions",
311
- labelKey: "auctions",
312
- descKey: "auctionsDesc",
313
- icon: "??",
321
+ labelKey: "Auctions",
322
+ descKey: "Auction listings and bidding.",
323
+ icon: "\u{1F528}",
314
324
  category: "platform",
315
325
  },
316
326
  {
317
327
  key: "reviews",
318
- labelKey: "reviews",
319
- descKey: "reviewsDesc",
320
- icon: "?",
328
+ labelKey: "Reviews",
329
+ descKey: "Product and store reviews.",
330
+ icon: "",
321
331
  category: "platform",
322
332
  },
323
333
  {
324
334
  key: "notifications",
325
- labelKey: "notifications",
326
- descKey: "notificationsDesc",
327
- icon: "??",
335
+ labelKey: "Notifications",
336
+ descKey: "In-app notification bell and inbox.",
337
+ icon: "\u{1F514}",
328
338
  category: "platform",
329
339
  },
330
340
  {
331
341
  key: "sellerRegistration",
332
- labelKey: "sellerRegistration",
333
- descKey: "sellerRegistrationDesc",
334
- icon: "??",
342
+ labelKey: "Seller registration",
343
+ descKey: "\"Become a seller\" signup flow.",
344
+ icon: "\u{1F3EA}",
335
345
  category: "platform",
336
346
  },
337
347
  {
338
348
  key: "preOrders",
339
- labelKey: "preOrders",
340
- descKey: "preOrdersDesc",
341
- icon: "??",
349
+ labelKey: "Pre-orders",
350
+ descKey: "Pre-order listings and deposit checkout.",
351
+ icon: "\u{1F4E6}",
352
+ category: "platform",
353
+ },
354
+ {
355
+ key: "wishlists",
356
+ labelKey: "Wishlists",
357
+ descKey: "Save-for-later wishlist feature.",
358
+ icon: "❤️",
359
+ category: "platform",
360
+ },
361
+ {
362
+ key: "events",
363
+ labelKey: "Events",
364
+ descKey: "Sales, raffles, spin-wheel, and lottery events.",
365
+ icon: "\u{1F389}",
366
+ category: "platform",
367
+ },
368
+ {
369
+ key: "blog",
370
+ labelKey: "Blog",
371
+ descKey: "Blog posts section.",
372
+ icon: "\u{1F4DD}",
373
+ category: "platform",
374
+ },
375
+ {
376
+ key: "coupons",
377
+ labelKey: "Coupons",
378
+ descKey: "Coupon codes and discounts.",
379
+ icon: "\u{1F3AB}",
342
380
  category: "platform",
343
381
  },
344
382
  ];
@@ -7,11 +7,15 @@ export interface BackToTopProps {
7
7
  /**
8
8
  * BackToTop — floating scroll-to-top button.
9
9
  * Appears after the page has scrolled past `threshold` pixels. A small
10
- * dismiss control lets the user hide it for the rest of the browser
11
- * session (sessionStorage) on narrow viewports it can sit close enough
12
- * to the bottom nav / other floating controls to feel like clutter, so it
13
- * needs the same "get out of my way" escape hatch as StickyToolbar.
14
- * Re-appears on the next page load.
10
+ * dismiss control hides it for the current page view only — dismissal is
11
+ * plain in-memory component state, reset on every route change (via
12
+ * `usePathname()`) so navigating anywhere brings it back. Deliberately NOT
13
+ * persisted to storage: an earlier version wrote the dismissal to
14
+ * `sessionStorage`, and because this component is mounted once inside the
15
+ * persistent `AppLayoutShell` (it never remounts on client-side navigation),
16
+ * that meant one click hid the button for the rest of the browser tab
17
+ * session with no way to bring it back — reported as "the button is gone
18
+ * forever." See CLAUDE.md Recurrent Root Cause Patterns.
15
19
  */
16
20
  export declare function BackToTop({ threshold, ariaLabel, className, }: BackToTopProps): import("react").JSX.Element | null;
17
21
  /** SkipToMain — visually hidden link that becomes visible on focus for keyboard users. */
@@ -1,46 +1,42 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import Link from "next/link";
4
+ import { usePathname } from "next/navigation";
4
5
  import { useEffect, useState } from "react";
5
6
  import { ChevronUp, X } from "lucide-react";
6
7
  import { Button, Div } from "../../ui";
7
- import { normalizeError } from "../../errors/normalize";
8
- const DISMISSED_KEY = "appkit:back-to-top-dismissed";
9
8
  /**
10
9
  * BackToTop — floating scroll-to-top button.
11
10
  * Appears after the page has scrolled past `threshold` pixels. A small
12
- * dismiss control lets the user hide it for the rest of the browser
13
- * session (sessionStorage) on narrow viewports it can sit close enough
14
- * to the bottom nav / other floating controls to feel like clutter, so it
15
- * needs the same "get out of my way" escape hatch as StickyToolbar.
16
- * Re-appears on the next page load.
11
+ * dismiss control hides it for the current page view only — dismissal is
12
+ * plain in-memory component state, reset on every route change (via
13
+ * `usePathname()`) so navigating anywhere brings it back. Deliberately NOT
14
+ * persisted to storage: an earlier version wrote the dismissal to
15
+ * `sessionStorage`, and because this component is mounted once inside the
16
+ * persistent `AppLayoutShell` (it never remounts on client-side navigation),
17
+ * that meant one click hid the button for the rest of the browser tab
18
+ * session with no way to bring it back — reported as "the button is gone
19
+ * forever." See CLAUDE.md Recurrent Root Cause Patterns.
17
20
  */
18
21
  export function BackToTop({ threshold = 400, ariaLabel = "Back to top", className = "", }) {
22
+ const pathname = usePathname();
19
23
  const [visible, setVisible] = useState(false);
20
24
  const [dismissed, setDismissed] = useState(false);
21
25
  useEffect(() => {
22
- try {
23
- setDismissed(window.sessionStorage.getItem(DISMISSED_KEY) === "1");
24
- }
25
- catch (_err) {
26
- void normalizeError(_err);
27
- // sessionStorage unavailable — never dismissed for this render.
28
- }
29
26
  const handleScroll = () => setVisible(window.scrollY > threshold);
30
27
  window.addEventListener("scroll", handleScroll, { passive: true });
31
28
  return () => window.removeEventListener("scroll", handleScroll);
32
29
  }, [threshold]);
30
+ // Reset dismissal on every route change — the component itself never
31
+ // unmounts (it lives inside the persistent app shell), so without this
32
+ // a single dismiss would otherwise hide the button for the whole session.
33
+ useEffect(() => {
34
+ setDismissed(false);
35
+ }, [pathname]);
33
36
  if (!visible || dismissed)
34
37
  return null;
35
38
  const handleDismiss = () => {
36
39
  setDismissed(true);
37
- try {
38
- window.sessionStorage.setItem(DISMISSED_KEY, "1");
39
- }
40
- catch (_err) {
41
- void normalizeError(_err);
42
- // sessionStorage unavailable — dismissal still applies for this render.
43
- }
44
40
  };
45
41
  return (_jsxs(Div, { layout: "flex", align: "center", gap: "1", className: `fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))] right-4 z-[var(--appkit-z-bottom-nav)] ${className}`, children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": ariaLabel, onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }), className: "flex items-center justify-center w-10 h-10 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] dark:hover:bg-slate-700 transition-colors", children: _jsx(ChevronUp, { className: "w-5 h-5", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": "Hide back-to-top button", onClick: handleDismiss, className: "flex items-center justify-center w-6 h-6 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text)] transition-colors", children: _jsx(X, { className: "w-3 h-3", "aria-hidden": "true" }) })] }));
46
42
  }
@@ -49,6 +49,7 @@ export async function issuePartialRefund(adminUid, orderId, deductFees, refundNo
49
49
  : `Your full refund of ₹${grossRefund} is being processed.`,
50
50
  relatedId: orderId,
51
51
  relatedType: "order",
52
+ orderWhatsappAddonPaid: order.whatsappNotifyAddon === true,
52
53
  });
53
54
  serverLogger.info("Admin partial refund initiated", {
54
55
  adminUid,
@@ -183,6 +183,20 @@ export interface OrderDocument extends BaseDocument {
183
183
  codRemainingAmount?: number;
184
184
  /** COD handling fee charged to the buyer: max(codHandlingFeeMin, subtotal × codHandlingFeePercent / 100). Only set when paymentMethod === "cod". */
185
185
  codHandlingFee?: number;
186
+ /** Buyer opted into WhatsApp order-update messages at checkout (unchecked by default). */
187
+ whatsappNotifyAddon?: boolean;
188
+ /** Fee charged for the WhatsApp addon, decimal rupees — snapshot of siteSettings.commissions.whatsappNotifyFee at order time. */
189
+ whatsappNotifyFee?: number;
190
+ /** Buyer opted into gift wrapping at checkout (unchecked by default). */
191
+ giftWrapAddon?: boolean;
192
+ /** Fee charged for gift wrap, decimal rupees — snapshot of siteSettings.commissions.giftWrapFee at order time. */
193
+ giftWrapFee?: number;
194
+ /** Optional buyer-written gift message, shown to the seller for fulfilment. Capped length. */
195
+ giftWrapMessage?: string;
196
+ /** Buyer opted into shipment protection (loss/damage insurance) at checkout (unchecked by default). */
197
+ shipmentProtectionAddon?: boolean;
198
+ /** Fee charged for shipment protection, decimal rupees — snapshot of siteSettings.commissions.shipmentProtectionFeePercent/Min at order time. */
199
+ shipmentProtectionFee?: number;
186
200
  /** Order subtotal before GST — the amount GST is computed on. */
187
201
  taxableAmount?: number;
188
202
  /** Total GST charged (cgst + sgst, or igst — never both pairs at once). */
@@ -280,15 +280,40 @@ export declare const DASHBOARD_QUICK_ACTION_ID: {
280
280
  readonly ADMIN_ADD_BLOG: "dqa-admin-add-blog";
281
281
  readonly ADMIN_ADD_FAQ: "dqa-admin-add-faq";
282
282
  readonly ADMIN_SETTINGS: "dqa-admin-settings";
283
+ readonly ADMIN_STORES: "dqa-admin-stores";
284
+ readonly ADMIN_ORDERS: "dqa-admin-orders";
285
+ readonly ADMIN_PAYOUTS: "dqa-admin-payouts";
286
+ readonly ADMIN_ANALYTICS: "dqa-admin-analytics";
287
+ readonly ADMIN_EVENTS: "dqa-admin-events";
288
+ readonly ADMIN_SUPPORT: "dqa-admin-support";
289
+ readonly ADMIN_MODERATION: "dqa-admin-moderation";
290
+ readonly ADMIN_INTEGRATION_GUIDES: "dqa-admin-integration-guides";
291
+ readonly ADMIN_USERS: "dqa-admin-users";
292
+ readonly ADMIN_CATEGORIES: "dqa-admin-categories";
293
+ readonly ADMIN_REVIEWS: "dqa-admin-reviews";
294
+ readonly ADMIN_FAQS: "dqa-admin-faqs";
295
+ readonly ADMIN_CAROUSEL: "dqa-admin-carousel";
296
+ readonly ADMIN_SECTIONS: "dqa-admin-sections";
283
297
  readonly SELLER_ADD_PRODUCT: "dqa-seller-add-product";
284
298
  readonly SELLER_ADD_COUPON: "dqa-seller-add-coupon";
285
299
  readonly SELLER_VIEW_ORDERS: "dqa-seller-view-orders";
286
300
  readonly SELLER_PAYOUT_REQ: "dqa-seller-payout-request";
287
301
  readonly SELLER_SETTINGS: "dqa-seller-settings";
302
+ readonly SELLER_AUCTIONS: "dqa-seller-auctions";
303
+ readonly SELLER_SHIPPING: "dqa-seller-shipping";
304
+ readonly SELLER_MESSAGES: "dqa-seller-messages";
305
+ readonly SELLER_WHATSAPP: "dqa-seller-whatsapp";
306
+ readonly SELLER_REVIEWS: "dqa-seller-reviews";
307
+ readonly SELLER_PRODUCTS: "dqa-seller-products";
308
+ readonly SELLER_STOREFRONT: "dqa-seller-storefront";
309
+ readonly SELLER_ANALYTICS: "dqa-seller-analytics";
288
310
  readonly USER_VIEW_ORDERS: "dqa-user-view-orders";
289
311
  readonly USER_VIEW_WISHLIST: "dqa-user-view-wishlist";
290
312
  readonly USER_EDIT_PROFILE: "dqa-user-edit-profile";
291
313
  readonly USER_ADD_ADDRESS: "dqa-user-add-address";
314
+ readonly USER_RECENTLY_VIEWED: "dqa-user-recently-viewed";
315
+ readonly USER_MY_CATALOGUE: "dqa-user-my-catalogue";
316
+ readonly USER_MY_COUPONS: "dqa-user-my-coupons";
292
317
  };
293
318
  export type DashboardQuickActionId = (typeof DASHBOARD_QUICK_ACTION_ID)[keyof typeof DASHBOARD_QUICK_ACTION_ID];
294
319
  export interface DashboardQuickActionMeta {
@@ -309,9 +334,9 @@ export interface DashboardQuickActionMeta {
309
334
  }
310
335
  export declare const DASHBOARD_QUICK_ACTION_META: Record<DashboardQuickActionId, DashboardQuickActionMeta>;
311
336
  export declare const DASHBOARD_QUICK_ACTIONS: {
312
- readonly admin: readonly ["dqa-admin-add-product", "dqa-admin-add-user", "dqa-admin-add-store", "dqa-admin-add-coupon", "dqa-admin-add-event", "dqa-admin-add-blog", "dqa-admin-settings"];
313
- readonly seller: readonly ["dqa-seller-add-product", "dqa-seller-add-coupon", "dqa-seller-view-orders", "dqa-seller-payout-request", "dqa-seller-settings"];
314
- readonly user: readonly ["dqa-user-view-orders", "dqa-user-view-wishlist", "dqa-user-edit-profile", "dqa-user-add-address"];
337
+ readonly admin: readonly ["dqa-admin-add-product", "dqa-admin-add-user", "dqa-admin-add-store", "dqa-admin-add-coupon", "dqa-admin-add-event", "dqa-admin-add-blog", "dqa-admin-users", "dqa-admin-stores", "dqa-admin-orders", "dqa-admin-payouts", "dqa-admin-analytics", "dqa-admin-categories", "dqa-admin-reviews", "dqa-admin-events", "dqa-admin-faqs", "dqa-admin-carousel", "dqa-admin-sections", "dqa-admin-support", "dqa-admin-moderation", "dqa-admin-integration-guides", "dqa-admin-settings"];
338
+ readonly seller: readonly ["dqa-seller-add-product", "dqa-seller-products", "dqa-seller-view-orders", "dqa-seller-analytics", "dqa-seller-add-coupon", "dqa-seller-auctions", "dqa-seller-payout-request", "dqa-seller-shipping", "dqa-seller-messages", "dqa-seller-whatsapp", "dqa-seller-reviews", "dqa-seller-storefront", "dqa-seller-settings"];
339
+ readonly user: readonly ["dqa-user-view-orders", "dqa-user-view-wishlist", "dqa-user-edit-profile", "dqa-user-add-address", "dqa-user-recently-viewed", "dqa-user-my-catalogue", "dqa-user-my-coupons"];
315
340
  };
316
341
  export declare const ADMIN_BULK_ACTIONS: {
317
342
  readonly users: readonly ["row-manage", "row-suspend", "row-restore", "row-delete"];
@@ -351,17 +351,42 @@ export const DASHBOARD_QUICK_ACTION_ID = {
351
351
  ADMIN_ADD_BLOG: "dqa-admin-add-blog",
352
352
  ADMIN_ADD_FAQ: "dqa-admin-add-faq",
353
353
  ADMIN_SETTINGS: "dqa-admin-settings",
354
+ ADMIN_STORES: "dqa-admin-stores",
355
+ ADMIN_ORDERS: "dqa-admin-orders",
356
+ ADMIN_PAYOUTS: "dqa-admin-payouts",
357
+ ADMIN_ANALYTICS: "dqa-admin-analytics",
358
+ ADMIN_EVENTS: "dqa-admin-events",
359
+ ADMIN_SUPPORT: "dqa-admin-support",
360
+ ADMIN_MODERATION: "dqa-admin-moderation",
361
+ ADMIN_INTEGRATION_GUIDES: "dqa-admin-integration-guides",
362
+ ADMIN_USERS: "dqa-admin-users",
363
+ ADMIN_CATEGORIES: "dqa-admin-categories",
364
+ ADMIN_REVIEWS: "dqa-admin-reviews",
365
+ ADMIN_FAQS: "dqa-admin-faqs",
366
+ ADMIN_CAROUSEL: "dqa-admin-carousel",
367
+ ADMIN_SECTIONS: "dqa-admin-sections",
354
368
  // Seller / Store
355
369
  SELLER_ADD_PRODUCT: "dqa-seller-add-product",
356
370
  SELLER_ADD_COUPON: "dqa-seller-add-coupon",
357
371
  SELLER_VIEW_ORDERS: "dqa-seller-view-orders",
358
372
  SELLER_PAYOUT_REQ: "dqa-seller-payout-request",
359
373
  SELLER_SETTINGS: "dqa-seller-settings",
374
+ SELLER_AUCTIONS: "dqa-seller-auctions",
375
+ SELLER_SHIPPING: "dqa-seller-shipping",
376
+ SELLER_MESSAGES: "dqa-seller-messages",
377
+ SELLER_WHATSAPP: "dqa-seller-whatsapp",
378
+ SELLER_REVIEWS: "dqa-seller-reviews",
379
+ SELLER_PRODUCTS: "dqa-seller-products",
380
+ SELLER_STOREFRONT: "dqa-seller-storefront",
381
+ SELLER_ANALYTICS: "dqa-seller-analytics",
360
382
  // User / Buyer
361
383
  USER_VIEW_ORDERS: "dqa-user-view-orders",
362
384
  USER_VIEW_WISHLIST: "dqa-user-view-wishlist",
363
385
  USER_EDIT_PROFILE: "dqa-user-edit-profile",
364
386
  USER_ADD_ADDRESS: "dqa-user-add-address",
387
+ USER_RECENTLY_VIEWED: "dqa-user-recently-viewed",
388
+ USER_MY_CATALOGUE: "dqa-user-my-catalogue",
389
+ USER_MY_COUPONS: "dqa-user-my-coupons",
365
390
  };
366
391
  export const DASHBOARD_QUICK_ACTION_META = {
367
392
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT, label: "Add Product", variant: "primary", iconName: "Plus", requiresAuth: true, requiredRole: "admin", requiredPermission: "products.create" },
@@ -372,15 +397,40 @@ export const DASHBOARD_QUICK_ACTION_META = {
372
397
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG, label: "New Post", variant: "outline", iconName: "FileText", requiresAuth: true, requiredRole: "admin", requiredPermission: "blog.create" },
373
398
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_FAQ]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_FAQ, label: "Add FAQ", variant: "outline", iconName: "HelpCircle", requiresAuth: true, requiredRole: "admin", requiredPermission: "faqs.create" },
374
399
  [DASHBOARD_QUICK_ACTION_ID.ADMIN_SETTINGS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_SETTINGS, label: "Settings", variant: "ghost", iconName: "Settings", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.settings.read" },
400
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_STORES]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_STORES, label: "Stores", variant: "outline", iconName: "Store", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.stores.read" },
401
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_ORDERS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ORDERS, label: "Orders", variant: "outline", iconName: "ShoppingBag", requiresAuth: true, requiredRole: "admin", requiredPermission: "orders.read" },
402
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_PAYOUTS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_PAYOUTS, label: "Payouts", variant: "outline", iconName: "Banknote", requiresAuth: true, requiredRole: "admin", requiredPermission: "payouts.read" },
403
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_ANALYTICS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ANALYTICS, label: "Analytics", variant: "outline", iconName: "BarChart", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.analytics.read" },
404
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_EVENTS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_EVENTS, label: "Events", variant: "outline", iconName: "Calendar", requiresAuth: true, requiredRole: "admin", requiredPermission: "events.read" },
405
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_SUPPORT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_SUPPORT, label: "Support Tickets", variant: "outline", iconName: "LifeBuoy", requiresAuth: true, requiredRole: "admin", requiredPermission: "support.read" },
406
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_MODERATION]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_MODERATION, label: "Moderation", variant: "outline", iconName: "ShieldAlert", requiresAuth: true, requiredRole: "admin", requiredPermission: "moderation.read" },
407
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_INTEGRATION_GUIDES]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_INTEGRATION_GUIDES, label: "Integration Guides", variant: "ghost", iconName: "BookOpen", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin:site:read" },
408
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_USERS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_USERS, label: "Users", variant: "outline", iconName: "Users", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.users.read" },
409
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_CATEGORIES]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_CATEGORIES, label: "Categories", variant: "outline", iconName: "Tag", requiresAuth: true, requiredRole: "admin", requiredPermission: "categories.read" },
410
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_REVIEWS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_REVIEWS, label: "Reviews", variant: "outline", iconName: "Star", requiresAuth: true, requiredRole: "admin", requiredPermission: "reviews.read" },
411
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_FAQS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_FAQS, label: "FAQs", variant: "outline", iconName: "HelpCircle", requiresAuth: true, requiredRole: "admin", requiredPermission: "faqs.read" },
412
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_CAROUSEL]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_CAROUSEL, label: "Carousel", variant: "outline", iconName: "Layout", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.settings.read" },
413
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_SECTIONS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_SECTIONS, label: "Sections", variant: "outline", iconName: "Layers", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.settings.read" },
375
414
  [DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_PRODUCT]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_PRODUCT, label: "List a Product", variant: "primary", iconName: "Plus", requiresAuth: true, requiredRole: "seller", requiredPermission: "products.create" },
376
415
  [DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_COUPON]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_COUPON, label: "New Coupon", variant: "outline", iconName: "Tag", requiresAuth: true, requiredRole: "seller", requiredPermission: "coupons.create" },
377
416
  [DASHBOARD_QUICK_ACTION_ID.SELLER_VIEW_ORDERS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_VIEW_ORDERS, label: "Orders", variant: "outline", iconName: "ShoppingBag", requiresAuth: true, requiredRole: "seller" },
378
417
  [DASHBOARD_QUICK_ACTION_ID.SELLER_PAYOUT_REQ]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_PAYOUT_REQ, label: "Request Payout", variant: "outline", iconName: "Banknote", requiresAuth: true, requiredRole: "seller", requiredPermission: "seller.payouts.request" },
379
418
  [DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS, label: "Store Settings", variant: "ghost", iconName: "Settings", requiresAuth: true, requiredRole: "seller" },
419
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_AUCTIONS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_AUCTIONS, label: "Auctions", variant: "outline", iconName: "Gavel", requiresAuth: true, requiredRole: "seller" },
420
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_SHIPPING]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_SHIPPING, label: "Shipping", variant: "outline", iconName: "Truck", requiresAuth: true, requiredRole: "seller" },
421
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_MESSAGES]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_MESSAGES, label: "Messages", variant: "outline", iconName: "MessageCircle", requiresAuth: true, requiredRole: "seller" },
422
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_WHATSAPP]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_WHATSAPP, label: "WhatsApp", variant: "outline", iconName: "Phone", requiresAuth: true, requiredRole: "seller" },
423
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_REVIEWS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_REVIEWS, label: "Reviews", variant: "outline", iconName: "Star", requiresAuth: true, requiredRole: "seller" },
424
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_PRODUCTS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_PRODUCTS, label: "My Products", variant: "outline", iconName: "Package", requiresAuth: true, requiredRole: "seller" },
425
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_STOREFRONT]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_STOREFRONT, label: "My Storefront", variant: "outline", iconName: "Store", requiresAuth: true, requiredRole: "seller" },
426
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_ANALYTICS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_ANALYTICS, label: "Analytics", variant: "outline", iconName: "BarChart", requiresAuth: true, requiredRole: "seller" },
380
427
  [DASHBOARD_QUICK_ACTION_ID.USER_VIEW_ORDERS]: { id: DASHBOARD_QUICK_ACTION_ID.USER_VIEW_ORDERS, label: "My Orders", variant: "outline", iconName: "Package", requiresAuth: true, requiredRole: "user" },
381
428
  [DASHBOARD_QUICK_ACTION_ID.USER_VIEW_WISHLIST]: { id: DASHBOARD_QUICK_ACTION_ID.USER_VIEW_WISHLIST, label: "Wishlist", variant: "outline", iconName: "Heart", requiresAuth: true, requiredRole: "user" },
382
429
  [DASHBOARD_QUICK_ACTION_ID.USER_EDIT_PROFILE]: { id: DASHBOARD_QUICK_ACTION_ID.USER_EDIT_PROFILE, label: "Edit Profile", variant: "outline", iconName: "User", requiresAuth: true, requiredRole: "user" },
383
430
  [DASHBOARD_QUICK_ACTION_ID.USER_ADD_ADDRESS]: { id: DASHBOARD_QUICK_ACTION_ID.USER_ADD_ADDRESS, label: "Add Address", variant: "outline", iconName: "MapPin", requiresAuth: true, requiredRole: "user" },
431
+ [DASHBOARD_QUICK_ACTION_ID.USER_RECENTLY_VIEWED]: { id: DASHBOARD_QUICK_ACTION_ID.USER_RECENTLY_VIEWED, label: "Recently Viewed", variant: "outline", iconName: "Clock", requiresAuth: true, requiredRole: "user" },
432
+ [DASHBOARD_QUICK_ACTION_ID.USER_MY_CATALOGUE]: { id: DASHBOARD_QUICK_ACTION_ID.USER_MY_CATALOGUE, label: "My Catalogue", variant: "outline", iconName: "Archive", requiresAuth: true, requiredRole: "user" },
433
+ [DASHBOARD_QUICK_ACTION_ID.USER_MY_COUPONS]: { id: DASHBOARD_QUICK_ACTION_ID.USER_MY_COUPONS, label: "My Coupons", variant: "outline", iconName: "Ticket", requiresAuth: true, requiredRole: "user" },
384
434
  };
385
435
  // Quick action groups — ordered left-to-right in the top shortcut bar
386
436
  export const DASHBOARD_QUICK_ACTIONS = {
@@ -391,13 +441,35 @@ export const DASHBOARD_QUICK_ACTIONS = {
391
441
  DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON,
392
442
  DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT,
393
443
  DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG,
444
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_USERS,
445
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_STORES,
446
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_ORDERS,
447
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_PAYOUTS,
448
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_ANALYTICS,
449
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_CATEGORIES,
450
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_REVIEWS,
451
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_EVENTS,
452
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_FAQS,
453
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_CAROUSEL,
454
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_SECTIONS,
455
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_SUPPORT,
456
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_MODERATION,
457
+ DASHBOARD_QUICK_ACTION_ID.ADMIN_INTEGRATION_GUIDES,
394
458
  DASHBOARD_QUICK_ACTION_ID.ADMIN_SETTINGS,
395
459
  ],
396
460
  seller: [
397
461
  DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_PRODUCT,
398
- DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_COUPON,
462
+ DASHBOARD_QUICK_ACTION_ID.SELLER_PRODUCTS,
399
463
  DASHBOARD_QUICK_ACTION_ID.SELLER_VIEW_ORDERS,
464
+ DASHBOARD_QUICK_ACTION_ID.SELLER_ANALYTICS,
465
+ DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_COUPON,
466
+ DASHBOARD_QUICK_ACTION_ID.SELLER_AUCTIONS,
400
467
  DASHBOARD_QUICK_ACTION_ID.SELLER_PAYOUT_REQ,
468
+ DASHBOARD_QUICK_ACTION_ID.SELLER_SHIPPING,
469
+ DASHBOARD_QUICK_ACTION_ID.SELLER_MESSAGES,
470
+ DASHBOARD_QUICK_ACTION_ID.SELLER_WHATSAPP,
471
+ DASHBOARD_QUICK_ACTION_ID.SELLER_REVIEWS,
472
+ DASHBOARD_QUICK_ACTION_ID.SELLER_STOREFRONT,
401
473
  DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS,
402
474
  ],
403
475
  user: [
@@ -405,6 +477,9 @@ export const DASHBOARD_QUICK_ACTIONS = {
405
477
  DASHBOARD_QUICK_ACTION_ID.USER_VIEW_WISHLIST,
406
478
  DASHBOARD_QUICK_ACTION_ID.USER_EDIT_PROFILE,
407
479
  DASHBOARD_QUICK_ACTION_ID.USER_ADD_ADDRESS,
480
+ DASHBOARD_QUICK_ACTION_ID.USER_RECENTLY_VIEWED,
481
+ DASHBOARD_QUICK_ACTION_ID.USER_MY_CATALOGUE,
482
+ DASHBOARD_QUICK_ACTION_ID.USER_MY_COUPONS,
408
483
  ],
409
484
  };
410
485
  // Admin bulk actions per listing entity — used in BulkActionsBar on admin tables
@@ -151,7 +151,7 @@ function OrderDetailDrawer({ orderId, apiBase, onClose, }) {
151
151
  };
152
152
  const addr = order?.shippingAddress ?? {};
153
153
  const addrLine = [addr.addressLine1, addr.city, addr.state, addr.pincode].filter(Boolean).join(", ");
154
- return (_jsxs(SideDrawer, { isOpen: true, title: `Order ${order?.id ?? orderId}`, onClose: onClose, children: [loading && (_jsx(Row, { align: "center", justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })), fetchError && (_jsx(Div, { textSize: "sm", className: "mx-4 mt-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: fetchError })), order && !loading && (_jsxs(Stack, { gap: "none", children: [_jsxs(Stack, { className: `flex-1 ${__O.yAuto}`, gap: "5", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Badge, { variant: STATUS_BADGE_VARIANT[order.status?.toUpperCase()] ?? "default", children: order.status ?? "Unknown" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: toRelativeDate(order.createdAt) })] }), (order.items ?? []).length > 0 && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-primary)] mb-2", weight: "semibold", children: "Items" }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.items ?? []).map((item, i) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title ?? item.productId ?? "Item" }), _jsxs(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: ["Qty: ", item.quantity ?? 1] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "medium", children: toCurrency(item.price ?? 0) })] }, i))) })] })), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", justify: "between", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Total" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-primary)]", weight: "bold", children: toCurrency(order.totalAmount ?? 0) })] }), addrLine && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Shipping address" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: [String(addr.fullName ?? ""), addrLine].filter(Boolean).join(" · ") })] })), order.paymentMethod && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Payment" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", transform: "capitalize", children: order.paymentMethod })] })), order.emiEnabled && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: ["EMI plan ", order.emiTenureMonths ? `· ${order.emiTenureMonths} months` : ""] }), _jsx(Badge, { variant: order.emiComplete ? "success" : "warning", children: order.emiComplete ? "Fully paid" : "In progress" })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsx(Text, { size: "xs", color: "muted", children: "Token collected" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiTokenAmount ?? 0) })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-3", children: [_jsx(Text, { size: "xs", color: "muted", children: "Remaining balance" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiRemainingBalance ?? 0) })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.emiInstallments ?? []).map((inst) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["Installment ", inst.index, " \u00B7 ", toCurrency(inst.amount)] }), _jsx(Text, { size: "xs", color: "muted", children: inst.status === "paid" && inst.paidAt
154
+ return (_jsxs(SideDrawer, { isOpen: true, title: `Order ${order?.id ?? orderId}`, onClose: onClose, children: [loading && (_jsx(Row, { align: "center", justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })), fetchError && (_jsx(Div, { textSize: "sm", className: "mx-4 mt-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: fetchError })), order && !loading && (_jsxs(Stack, { gap: "none", children: [_jsxs(Stack, { className: `flex-1 ${__O.yAuto}`, gap: "5", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Badge, { variant: STATUS_BADGE_VARIANT[order.status?.toUpperCase()] ?? "default", children: order.status ?? "Unknown" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: toRelativeDate(order.createdAt) })] }), (order.items ?? []).length > 0 && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-primary)] mb-2", weight: "semibold", children: "Items" }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.items ?? []).map((item, i) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title ?? item.productId ?? "Item" }), _jsxs(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: ["Qty: ", item.quantity ?? 1] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "medium", children: toCurrency(item.price ?? 0) })] }, i))) })] })), order.giftWrapAddon && (_jsxs(Div, { className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)]", surface: "subtle", padding: "inline", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "\uD83C\uDF81 Gift wrap requested" }), order.giftWrapMessage && (_jsxs(Text, { size: "sm", className: "mt-1 text-[var(--appkit-color-text-secondary)]", children: ["\u201C", order.giftWrapMessage, "\u201D"] }))] })), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", justify: "between", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Total" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-primary)]", weight: "bold", children: toCurrency(order.totalAmount ?? 0) })] }), addrLine && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Shipping address" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: [String(addr.fullName ?? ""), addrLine].filter(Boolean).join(" · ") })] })), order.paymentMethod && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Payment" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", transform: "capitalize", children: order.paymentMethod })] })), order.emiEnabled && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: ["EMI plan ", order.emiTenureMonths ? `· ${order.emiTenureMonths} months` : ""] }), _jsx(Badge, { variant: order.emiComplete ? "success" : "warning", children: order.emiComplete ? "Fully paid" : "In progress" })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsx(Text, { size: "xs", color: "muted", children: "Token collected" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiTokenAmount ?? 0) })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-3", children: [_jsx(Text, { size: "xs", color: "muted", children: "Remaining balance" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiRemainingBalance ?? 0) })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.emiInstallments ?? []).map((inst) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["Installment ", inst.index, " \u00B7 ", toCurrency(inst.amount)] }), _jsx(Text, { size: "xs", color: "muted", children: inst.status === "paid" && inst.paidAt
155
155
  ? `Paid ${toRelativeDate(inst.paidAt)}`
156
156
  : inst.dueDate
157
157
  ? `Due ${toRelativeDate(inst.dueDate)}`
@@ -1,4 +1,4 @@
1
- import type { CheckoutMessageInput, StatusNotificationInput, WebhookVerifyInput, IncomingWebhookPayload, SendWhatsAppInput, StatusMessageInput, WaBusinessSendInput, CatalogSyncInput, CatalogSyncResult, PurchaseAnnouncementInput, PaymentProofReviewMessageInput } from "../types";
1
+ import type { CheckoutMessageInput, StatusNotificationInput, WebhookVerifyInput, IncomingWebhookPayload, SendWhatsAppInput, StatusMessageInput, WaBusinessSendInput, WaBusinessTemplateSendInput, CatalogSyncInput, CatalogSyncResult, PurchaseAnnouncementInput, PaymentProofReviewMessageInput } from "../types";
2
2
  export declare function buildCheckoutMessageURL(input: CheckoutMessageInput): string;
3
3
  export declare function buildStatusNotificationURL(input: StatusNotificationInput): string;
4
4
  export declare function verifyWebhookSignature(input: WebhookVerifyInput): boolean;
@@ -19,6 +19,15 @@ export declare function buildStatusMessage(input: StatusMessageInput, statusMess
19
19
  * platform announcements. Returns true on success, false on any failure.
20
20
  */
21
21
  export declare function sendWhatsAppBusinessMessage(input: WaBusinessSendInput): Promise<boolean>;
22
+ /**
23
+ * Send a pre-approved Meta message TEMPLATE via WhatsApp Business Cloud API.
24
+ * Required for business-initiated sends outside the 24h customer-service
25
+ * window (proactive order/status notifications) — Meta rejects free-form
26
+ * `sendWhatsAppBusinessMessage` `type: "text"` sends in that case. Returns
27
+ * true on success, false on any failure (including a missing/unapproved
28
+ * template — Meta returns a 4xx with an error body, not a network failure).
29
+ */
30
+ export declare function sendWhatsAppTemplateMessage(input: WaBusinessTemplateSendInput): Promise<boolean>;
22
31
  /**
23
32
  * Sync a store's products to a Meta Commerce Catalog via items_batch API.
24
33
  * Batches at 50 items per call (Meta limit). Returns aggregate results.
@@ -131,6 +131,53 @@ export async function sendWhatsAppBusinessMessage(input) {
131
131
  return false;
132
132
  }
133
133
  }
134
+ /**
135
+ * Send a pre-approved Meta message TEMPLATE via WhatsApp Business Cloud API.
136
+ * Required for business-initiated sends outside the 24h customer-service
137
+ * window (proactive order/status notifications) — Meta rejects free-form
138
+ * `sendWhatsAppBusinessMessage` `type: "text"` sends in that case. Returns
139
+ * true on success, false on any failure (including a missing/unapproved
140
+ * template — Meta returns a 4xx with an error body, not a network failure).
141
+ */
142
+ export async function sendWhatsAppTemplateMessage(input) {
143
+ const { toPhone, phoneNumberId, accessToken, templateName, languageCode, bodyParams } = input;
144
+ const cleanPhone = toPhone.replace(/\D/g, "");
145
+ if (!cleanPhone || !phoneNumberId || !accessToken || !templateName)
146
+ return false;
147
+ try {
148
+ const res = await fetch(`${META_GRAPH_BASE}/${phoneNumberId}/messages`, {
149
+ method: "POST",
150
+ headers: {
151
+ "Content-Type": "application/json",
152
+ Authorization: `Bearer ${accessToken}`,
153
+ },
154
+ body: JSON.stringify({
155
+ messaging_product: "whatsapp",
156
+ to: cleanPhone,
157
+ type: "template",
158
+ template: {
159
+ name: templateName,
160
+ language: { code: languageCode },
161
+ ...(bodyParams?.length
162
+ ? {
163
+ components: [
164
+ {
165
+ type: "body",
166
+ parameters: bodyParams.map((text) => ({ type: "text", text })),
167
+ },
168
+ ],
169
+ }
170
+ : {}),
171
+ },
172
+ }),
173
+ });
174
+ return res.ok;
175
+ }
176
+ catch (_err) {
177
+ void normalizeError(_err);
178
+ return false;
179
+ }
180
+ }
134
181
  /**
135
182
  * Sync a store's products to a Meta Commerce Catalog via items_batch API.
136
183
  * Batches at 50 items per call (Meta limit). Returns aggregate results.
@@ -50,6 +50,25 @@ export interface WaBusinessSendInput {
50
50
  /** Meta system user access token */
51
51
  accessToken: string;
52
52
  }
53
+ /**
54
+ * Input for sending a pre-approved Meta message TEMPLATE. Required for any
55
+ * business-initiated WhatsApp send outside the 24h customer-service window —
56
+ * Meta rejects free-form `type: "text"` sends in that case.
57
+ */
58
+ export interface WaBusinessTemplateSendInput {
59
+ /** Digits-only phone number with country code, e.g. "919876543210" */
60
+ toPhone: string;
61
+ /** Meta Phone Number ID (from Meta for Developers > App > WhatsApp > API Setup) */
62
+ phoneNumberId: string;
63
+ /** Meta system user access token */
64
+ accessToken: string;
65
+ /** Exact approved template name from Meta WhatsApp Manager (not the display label). */
66
+ templateName: string;
67
+ /** BCP-47 language code the template was approved in, e.g. "en" or "en_US". */
68
+ languageCode: string;
69
+ /** Positional {{1}}, {{2}}… body parameters, in order. */
70
+ bodyParams?: string[];
71
+ }
53
72
  /** A product mapped to the Meta Commerce API catalog item format. */
54
73
  export interface CatalogSyncProduct {
55
74
  /** Firestore product ID — used as Meta `retailer_id` */
package/dist/index.d.ts CHANGED
@@ -549,6 +549,8 @@ export type { PageViewDocument, PageViewEntityType } from "./repositories/index"
549
549
  export { reviewRepository } from "./repositories/index";
550
550
  export { sessionRepository } from "./repositories/index";
551
551
  export { siteSettingsRepository } from "./repositories/index";
552
+ export { analyticsRollupRepository } from "./repositories/index";
553
+ export type { AnalyticsRollupDocument } from "./repositories/index";
552
554
  export { ADMIN_CHECKOUT_BYPASS_FLAG_KEY } from "./features/admin/schemas/firestore";
553
555
  export { smsCounterRepository } from "./repositories/index";
554
556
  export { storeRepository } from "./repositories/index";
@@ -2453,8 +2455,8 @@ export { actionTracker, setActionTrackerSink, resetActionTrackerSink, type Actio
2453
2455
  export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_internal/shared/listing-types/cart-shipping";
2454
2456
  export { ACTIONS, action, act, canPerformAction, actionsForListingType, actionLabel, type ActionDef, type ActionKind, type ActionResource, type ActionTree, type ActionConfirmation, } from "./_internal/shared/actions/action-registry";
2455
2457
  export { buildBulkAction } from "./_internal/shared/actions/bulk-helpers";
2456
- export { computeCheckoutFees, computePayoutDeduction, computeCodHandlingFee, calculateGst, } from "./_internal/shared/fees/calculator";
2457
- export type { FeeCommissionRates, CheckoutFees, PayoutDeduction, CodHandlingFeeRates, GstBreakdown, } from "./_internal/shared/fees/calculator";
2458
+ export { computeCheckoutFees, computePayoutDeduction, computeCodHandlingFee, computeWhatsAppNotifyFee, computeGiftWrapFee, computeShipmentProtectionFee, calculateGst, } from "./_internal/shared/fees/calculator";
2459
+ export type { FeeCommissionRates, CheckoutFees, PayoutDeduction, CodHandlingFeeRates, WhatsAppNotifyFeeRates, GiftWrapFeeRates, ShipmentProtectionFeeRates, GstBreakdown, } from "./_internal/shared/fees/calculator";
2458
2460
  export { checkEmiEligibility, computeEmiSchedule } from "./_internal/shared/features/emi/schedule";
2459
2461
  export type { EmiSettings, EmiIneligibleReason, EmiEligibility, EmiInstallmentPlan, EmiScheduleResult, } from "./_internal/shared/features/emi/schedule";
2460
2462
  export { isAdminUser, isSellerUser, isModeratorUser, isEmployeeUser, isBuyerUser, isEffectiveAdminUser, } from "./features/auth/role-predicates";
package/dist/index.js CHANGED
@@ -1070,6 +1070,8 @@ export { sessionRepository } from "./repositories/index";
1070
1070
  // [DB]-Database layer â€" uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
1071
1071
  // siteSettingsRepository - Shared export for site settings repository.
1072
1072
  export { siteSettingsRepository } from "./repositories/index";
1073
+ // analyticsRollupRepository - reads the pre-computed admin-dashboard revenue rollup.
1074
+ export { analyticsRollupRepository } from "./repositories/index";
1073
1075
  // ADMIN_CHECKOUT_BYPASS_FLAG_KEY - field name for the admin checkout bypass flag.
1074
1076
  export { ADMIN_CHECKOUT_BYPASS_FLAG_KEY } from "./features/admin/schemas/firestore";
1075
1077
  // [DB]-Database layer â€" uses firebase-admin or another server-side DB SDK; can only run in a trusted server environment.
@@ -4498,8 +4500,8 @@ export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_int
4498
4500
  // SB-UNI-W-1 2026-05-13 â€" CTA action registry shell.
4499
4501
  export { ACTIONS, action, act, canPerformAction, actionsForListingType, actionLabel, } from "./_internal/shared/actions/action-registry";
4500
4502
  export { buildBulkAction } from "./_internal/shared/actions/bulk-helpers";
4501
- // Shared fee calculator — platform/gateway/GST/COD handling fee math (pure, client-safe).
4502
- export { computeCheckoutFees, computePayoutDeduction, computeCodHandlingFee, calculateGst, } from "./_internal/shared/fees/calculator";
4503
+ // Shared fee calculator — platform/gateway/GST/COD handling/WhatsApp-addon fee math (pure, client-safe).
4504
+ export { computeCheckoutFees, computePayoutDeduction, computeCodHandlingFee, computeWhatsAppNotifyFee, computeGiftWrapFee, computeShipmentProtectionFee, calculateGst, } from "./_internal/shared/fees/calculator";
4503
4505
  // EMI eligibility + schedule computation (pure, client-safe — used for checkout quotes).
4504
4506
  export { checkEmiEligibility, computeEmiSchedule } from "./_internal/shared/features/emi/schedule";
4505
4507
  // User-role predicates â€" SB-UNI-E 2026-05-13.