@mohasinac/appkit 2.7.17 → 2.7.19

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 (91) hide show
  1. package/dist/_internal/server/features/bundles/actions.d.ts +15 -0
  2. package/dist/_internal/server/features/bundles/actions.js +48 -0
  3. package/dist/_internal/server/features/bundles/index.d.ts +1 -0
  4. package/dist/_internal/server/features/bundles/index.js +1 -0
  5. package/dist/_internal/server/features/checkout/actions.d.ts +4 -0
  6. package/dist/_internal/server/features/checkout/actions.js +23 -12
  7. package/dist/_internal/server/features/payments/data.d.ts +5 -3
  8. package/dist/_internal/server/features/payments/data.js +13 -8
  9. package/dist/_internal/server/features/payments/index.d.ts +1 -1
  10. package/dist/_internal/server/features/payments/index.js +1 -1
  11. package/dist/_internal/server/features/seo/manifest.js +9 -3
  12. package/dist/_internal/shared/features/categories/bundle-config.d.ts +24 -0
  13. package/dist/_internal/shared/features/categories/bundle-config.js +28 -0
  14. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +7 -0
  15. package/dist/_internal/shared/features/categories/bundle-copy.js +9 -0
  16. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +69 -19
  17. package/dist/_internal/shared/features/categories/bundle-schemas.js +20 -2
  18. package/dist/_internal/shared/features/checkout/config.d.ts +1 -1
  19. package/dist/_internal/shared/features/checkout/config.js +1 -1
  20. package/dist/_internal/shared/features/payments/config.d.ts +2 -1
  21. package/dist/_internal/shared/features/payments/config.js +2 -1
  22. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  23. package/dist/constants/field-names.d.ts +8 -1
  24. package/dist/constants/field-names.js +9 -2
  25. package/dist/constants/table-keys.d.ts +3 -0
  26. package/dist/constants/table-keys.js +3 -0
  27. package/dist/features/admin/components/AdminSiteSettingsView.js +17 -15
  28. package/dist/features/admin/components/DashboardStats.js +20 -12
  29. package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +18 -2
  30. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +7 -1
  31. package/dist/features/admin/components/analytics/AdminStatCard.js +13 -2
  32. package/dist/features/admin/schemas/firestore.d.ts +39 -4
  33. package/dist/features/admin/schemas/firestore.js +14 -1
  34. package/dist/features/categories/components/BundleBuyNowCta.d.ts +3 -1
  35. package/dist/features/categories/components/BundleBuyNowCta.js +3 -3
  36. package/dist/features/categories/components/BundleDynamicRuleEditor.d.ts +1 -1
  37. package/dist/features/categories/components/BundleDynamicRuleEditor.js +2 -1
  38. package/dist/features/categories/components/BundlesListView.d.ts +4 -1
  39. package/dist/features/categories/components/BundlesListView.js +2 -2
  40. package/dist/features/categories/components/CategoryBundlesListing.d.ts +4 -1
  41. package/dist/features/categories/components/CategoryBundlesListing.js +9 -8
  42. package/dist/features/categories/schemas/firestore.d.ts +29 -1
  43. package/dist/features/checkout/actions/checkout-actions.d.ts +11 -0
  44. package/dist/features/checkout/actions/checkout-actions.js +26 -0
  45. package/dist/features/filters/FilterFacetSection.d.ts +6 -1
  46. package/dist/features/filters/FilterFacetSection.js +25 -2
  47. package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +4 -1
  48. package/dist/features/homepage/components/FeaturedBundlesSection.js +9 -8
  49. package/dist/features/orders/actions/refund-actions.js +10 -7
  50. package/dist/features/orders/schemas/firestore.d.ts +16 -13
  51. package/dist/features/orders/schemas/firestore.js +6 -5
  52. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  53. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -1
  54. package/dist/features/products/components/AuctionsIndexListing.js +2 -1
  55. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  56. package/dist/features/products/components/ProductDetailView.d.ts +3 -1
  57. package/dist/features/products/components/ProductDetailView.js +2 -2
  58. package/dist/features/products/components/ProductFilters.d.ts +10 -4
  59. package/dist/features/products/components/ProductFilters.js +20 -16
  60. package/dist/features/products/components/ProductGrid.js +2 -2
  61. package/dist/features/products/components/ProductsIndexListing.js +2 -1
  62. package/dist/features/products/constants/action-defs.d.ts +64 -22
  63. package/dist/features/products/constants/action-defs.js +101 -64
  64. package/dist/features/products/constants/product-features.constants.js +5 -1
  65. package/dist/features/products/schemas/index.d.ts +2 -2
  66. package/dist/features/products/schemas/product-features.d.ts +1 -1
  67. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  68. package/dist/features/products/schemas/product-features.validators.js +4 -0
  69. package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
  70. package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +10 -4
  71. package/dist/features/stores/components/StoreBundlesPageView.d.ts +4 -1
  72. package/dist/features/stores/components/StoreBundlesPageView.js +2 -2
  73. package/dist/features/stores/components/StoresIndexListing.js +2 -1
  74. package/dist/providers/db-firebase/admin-app-lite.js +8 -2
  75. package/dist/providers/db-firebase/admin.js +8 -2
  76. package/dist/seed/addresses-seed-data.js +24 -7
  77. package/dist/seed/cart-seed-data.d.ts +10 -10
  78. package/dist/seed/cart-seed-data.js +39 -2
  79. package/dist/seed/grouped-listings-seed-data.js +47 -0
  80. package/dist/seed/orders-seed-data.js +150 -0
  81. package/dist/seed/products-standard-seed-data.js +42 -0
  82. package/dist/seed/site-settings-seed-data.js +14 -4
  83. package/dist/seed/users-seed-data.js +1 -1
  84. package/dist/seed/wishlists-seed-data.js +7 -0
  85. package/dist/server-entry.d.ts +2 -1
  86. package/dist/server-entry.js +2 -1
  87. package/dist/server.d.ts +2 -1
  88. package/dist/server.js +3 -1
  89. package/dist/tailwind-utilities.css +1 -1
  90. package/dist/ui/components/SideDrawer.style.css +49 -11
  91. package/package.json +1 -1
@@ -1,17 +1,14 @@
1
1
  /**
2
2
  * action-defs.ts
3
3
  *
4
- * Single source of truth for ALL action IDs, labels, variants, and groupings
5
- * used across the platform:
4
+ * Universal CTA registry for the entire platform.
5
+ * Every action public marketplace CTAs, dashboard row actions, quick actions — is
6
+ * registered here with auth requirements, RBAC permissions, and enable/disable defaults.
6
7
  *
7
- * Product detail pages — desktop action panel + mobile BuyBar
8
- * Listing pages BulkActionsBar selections
9
- * Table rows RowActionMenu items (admin / seller / user)
10
- * Form shells submit / cancel / save-draft / publish / delete
11
- * Dashboard quick bar — top-level shortcut buttons per dashboard type
12
- *
13
- * Pure TypeScript — no React, no JSX, no callbacks.
14
- * Consuming components resolve iconName → Lucide component, and supply onClick.
8
+ * Three enforcement layers read from this registry:
9
+ * Client useAuthGate() shows LoginRequiredModal for Tier 1 (public) actions
10
+ * Server checkActionAllowed() enforces the same guards on every server action
11
+ * Admin ActionPermissionsManager toggles defaultEnabled via siteSettings.actionConfig
15
12
  */
16
13
  // ─────────────────────────────────────────────────────────────────────────────
17
14
  // § 1 Product / listing actions
@@ -30,23 +27,73 @@ export const ACTION_ID = {
30
27
  PLACE_BID: "place-bid",
31
28
  BUY_NOW_AUCTION: "buy-now-auction",
32
29
  WATCH_AUCTION: "watch-auction",
30
+ UNWATCH_AUCTION: "unwatch-auction",
33
31
  // Pre-order
34
32
  RESERVE_NOW: "reserve-now",
35
33
  CANCEL_RESERVATION: "cancel-reservation",
34
+ // ── Checkout / navigation CTAs ───────────────────────────────────────────
35
+ CHECKOUT: "checkout",
36
+ ENTER_PRIZE_DRAW: "enter-prize-draw",
37
+ ENTER_RAFFLE: "enter-raffle",
38
+ REPORT_LISTING: "report-listing",
39
+ FOLLOW_STORE: "follow-store",
40
+ // ── Reviews & social ─────────────────────────────────────────────────────
41
+ WRITE_REVIEW: "write-review",
42
+ MESSAGE_SELLER: "message-seller",
43
+ // ── Seller (public-facing CTAs on store/product pages) ───────────────────
44
+ BECOME_SELLER: "become-seller",
45
+ REQUEST_PAYOUT: "request-payout",
46
+ RESPOND_TO_REVIEW: "respond-to-review",
47
+ // ── User account actions ─────────────────────────────────────────────────
48
+ CANCEL_ORDER: "cancel-order",
49
+ REQUEST_RETURN: "request-return",
50
+ REORDER: "reorder",
51
+ TRACK_ORDER: "track-order",
52
+ EDIT_PROFILE: "edit-profile",
53
+ ADD_ADDRESS: "add-address",
54
+ EDIT_ADDRESS: "edit-address",
55
+ DELETE_ADDRESS: "delete-address",
56
+ CHANGE_PASSWORD: "change-password",
57
+ DELETE_ACCOUNT: "delete-account",
36
58
  };
37
59
  export const ACTION_META = {
38
- [ACTION_ID.BUY_NOW]: { id: ACTION_ID.BUY_NOW, label: "Buy Now", variant: "primary" },
60
+ [ACTION_ID.BUY_NOW]: { id: ACTION_ID.BUY_NOW, label: "Buy Now", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
39
61
  [ACTION_ID.ADD_TO_CART]: { id: ACTION_ID.ADD_TO_CART, label: "Add to Cart", variant: "secondary", iconName: "ShoppingCart" },
40
- [ACTION_ID.ADD_TO_WISHLIST]: { id: ACTION_ID.ADD_TO_WISHLIST, label: "Add to Wishlist", variant: "ghost", iconName: "Heart" },
41
- [ACTION_ID.REMOVE_FROM_WISHLIST]: { id: ACTION_ID.REMOVE_FROM_WISHLIST, label: "Remove from Wishlist", variant: "ghost", iconName: "HeartOff" },
42
- [ACTION_ID.MAKE_OFFER]: { id: ACTION_ID.MAKE_OFFER, label: "Make an Offer", variant: "outline", iconName: "Tag", requiresAuth: true },
62
+ [ACTION_ID.ADD_TO_WISHLIST]: { id: ACTION_ID.ADD_TO_WISHLIST, label: "Add to Wishlist", variant: "ghost", iconName: "Heart", requiresAuth: true, authMessage: "You need to be signed in to save items to your wishlist." },
63
+ [ACTION_ID.REMOVE_FROM_WISHLIST]: { id: ACTION_ID.REMOVE_FROM_WISHLIST, label: "Remove from Wishlist", variant: "ghost", iconName: "HeartOff", requiresAuth: true, authMessage: "You need to be signed in to manage your wishlist." },
64
+ [ACTION_ID.MAKE_OFFER]: { id: ACTION_ID.MAKE_OFFER, label: "Make an Offer", variant: "outline", iconName: "Tag", requiresAuth: true, authMessage: "You need to be signed in to make an offer." },
43
65
  [ACTION_ID.SHARE]: { id: ACTION_ID.SHARE, label: "Share", variant: "ghost", iconName: "Share2" },
44
66
  [ACTION_ID.COMPARE]: { id: ACTION_ID.COMPARE, label: "Compare", variant: "secondary", iconName: "Columns" },
45
- [ACTION_ID.PLACE_BID]: { id: ACTION_ID.PLACE_BID, label: "Place Bid", variant: "primary", requiresAuth: true },
46
- [ACTION_ID.BUY_NOW_AUCTION]: { id: ACTION_ID.BUY_NOW_AUCTION, label: "Buy Now", variant: "secondary", requiresAuth: true },
47
- [ACTION_ID.WATCH_AUCTION]: { id: ACTION_ID.WATCH_AUCTION, label: "Watch", variant: "ghost", iconName: "Eye" },
48
- [ACTION_ID.RESERVE_NOW]: { id: ACTION_ID.RESERVE_NOW, label: "Reserve Now", variant: "primary", requiresAuth: true },
49
- [ACTION_ID.CANCEL_RESERVATION]: { id: ACTION_ID.CANCEL_RESERVATION, label: "Cancel Reservation", variant: "danger", iconName: "X", requiresAuth: true },
67
+ [ACTION_ID.PLACE_BID]: { id: ACTION_ID.PLACE_BID, label: "Place Bid", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to place a bid." },
68
+ [ACTION_ID.BUY_NOW_AUCTION]: { id: ACTION_ID.BUY_NOW_AUCTION, label: "Buy Now", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to purchase items." },
69
+ [ACTION_ID.WATCH_AUCTION]: { id: ACTION_ID.WATCH_AUCTION, label: "Watch", variant: "ghost", iconName: "Eye", requiresAuth: true, authMessage: "You need to be signed in to watch auctions." },
70
+ [ACTION_ID.UNWATCH_AUCTION]: { id: ACTION_ID.UNWATCH_AUCTION, label: "Unwatch", variant: "ghost", iconName: "EyeOff", requiresAuth: true, authMessage: "You need to be signed in to manage your watchlist." },
71
+ [ACTION_ID.RESERVE_NOW]: { id: ACTION_ID.RESERVE_NOW, label: "Reserve Now", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to reserve a pre-order." },
72
+ [ACTION_ID.CANCEL_RESERVATION]: { id: ACTION_ID.CANCEL_RESERVATION, label: "Cancel Reservation", variant: "danger", iconName: "X", requiresAuth: true, authMessage: "You need to be signed in to cancel a reservation." },
73
+ // ── Checkout / navigation CTAs ────────────────────────────────────────────
74
+ [ACTION_ID.CHECKOUT]: { id: ACTION_ID.CHECKOUT, label: "Checkout", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to checkout." },
75
+ [ACTION_ID.ENTER_PRIZE_DRAW]: { id: ACTION_ID.ENTER_PRIZE_DRAW, label: "Enter Draw", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a prize draw." },
76
+ [ACTION_ID.ENTER_RAFFLE]: { id: ACTION_ID.ENTER_RAFFLE, label: "Enter Raffle", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to enter a raffle." },
77
+ [ACTION_ID.REPORT_LISTING]: { id: ACTION_ID.REPORT_LISTING, label: "Report Listing", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to report a listing." },
78
+ [ACTION_ID.FOLLOW_STORE]: { id: ACTION_ID.FOLLOW_STORE, label: "Follow Store", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to follow a store." },
79
+ // ── Reviews & social ──────────────────────────────────────────────────────
80
+ [ACTION_ID.WRITE_REVIEW]: { id: ACTION_ID.WRITE_REVIEW, label: "Write a Review", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to write a review." },
81
+ [ACTION_ID.MESSAGE_SELLER]: { id: ACTION_ID.MESSAGE_SELLER, label: "Message Seller", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to message a seller." },
82
+ // ── Seller CTAs ───────────────────────────────────────────────────────────
83
+ [ACTION_ID.BECOME_SELLER]: { id: ACTION_ID.BECOME_SELLER, label: "Apply as Seller", variant: "primary", requiresAuth: true, authMessage: "You need to be signed in to apply as a seller." },
84
+ [ACTION_ID.REQUEST_PAYOUT]: { id: ACTION_ID.REQUEST_PAYOUT, label: "Request Payout", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a payout.", requiredPermission: "seller.payouts.request" },
85
+ [ACTION_ID.RESPOND_TO_REVIEW]: { id: ACTION_ID.RESPOND_TO_REVIEW, label: "Respond to Review", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to respond to a review.", requiredPermission: "seller.reviews.respond" },
86
+ // ── User account actions ──────────────────────────────────────────────────
87
+ [ACTION_ID.CANCEL_ORDER]: { id: ACTION_ID.CANCEL_ORDER, label: "Cancel Order", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to cancel an order." },
88
+ [ACTION_ID.REQUEST_RETURN]: { id: ACTION_ID.REQUEST_RETURN, label: "Request Return", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to request a return." },
89
+ [ACTION_ID.REORDER]: { id: ACTION_ID.REORDER, label: "Reorder", variant: "secondary", requiresAuth: true, authMessage: "You need to be signed in to reorder." },
90
+ [ACTION_ID.TRACK_ORDER]: { id: ACTION_ID.TRACK_ORDER, label: "Track Order", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to track your order." },
91
+ [ACTION_ID.EDIT_PROFILE]: { id: ACTION_ID.EDIT_PROFILE, label: "Edit Profile", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to edit your profile." },
92
+ [ACTION_ID.ADD_ADDRESS]: { id: ACTION_ID.ADD_ADDRESS, label: "Add Address", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to add an address." },
93
+ [ACTION_ID.EDIT_ADDRESS]: { id: ACTION_ID.EDIT_ADDRESS, label: "Edit Address", variant: "ghost", requiresAuth: true, authMessage: "You need to be signed in to edit an address." },
94
+ [ACTION_ID.DELETE_ADDRESS]: { id: ACTION_ID.DELETE_ADDRESS, label: "Delete Address", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete an address." },
95
+ [ACTION_ID.CHANGE_PASSWORD]: { id: ACTION_ID.CHANGE_PASSWORD, label: "Change Password", variant: "outline", requiresAuth: true, authMessage: "You need to be signed in to change your password." },
96
+ [ACTION_ID.DELETE_ACCOUNT]: { id: ACTION_ID.DELETE_ACCOUNT, label: "Delete Account", variant: "danger", requiresAuth: true, authMessage: "You need to be signed in to delete your account.", requiredPermission: "user.account.delete" },
50
97
  };
51
98
  // Detail page action groups — ordered top-to-bottom in the right-hand panel
52
99
  export const DETAIL_ACTIONS = {
@@ -66,7 +113,7 @@ export const MOBILE_PRIMARY_ACTIONS = {
66
113
  // Listing page bulk actions — shown in BulkActionsBar when items are selected
67
114
  export const LISTING_BULK_ACTIONS = {
68
115
  products: [ACTION_ID.ADD_TO_CART, ACTION_ID.ADD_TO_WISHLIST, ACTION_ID.COMPARE],
69
- auctions: [ACTION_ID.WATCH_AUCTION, ACTION_ID.ADD_TO_WISHLIST, ACTION_ID.COMPARE],
116
+ auctions: [ACTION_ID.WATCH_AUCTION, ACTION_ID.UNWATCH_AUCTION, ACTION_ID.COMPARE],
70
117
  preorders: [ACTION_ID.ADD_TO_CART, ACTION_ID.ADD_TO_WISHLIST, ACTION_ID.COMPARE],
71
118
  stores: [ACTION_ID.COMPARE],
72
119
  };
@@ -96,23 +143,23 @@ export const ROW_ACTION_ID = {
96
143
  ARCHIVE: "row-archive",
97
144
  };
98
145
  export const ROW_ACTION_META = {
99
- [ROW_ACTION_ID.EDIT]: { id: ROW_ACTION_ID.EDIT, label: "Edit", iconName: "Pencil" },
100
- [ROW_ACTION_ID.VIEW]: { id: ROW_ACTION_ID.VIEW, label: "View", iconName: "Eye" },
101
- [ROW_ACTION_ID.DELETE]: { id: ROW_ACTION_ID.DELETE, label: "Delete", iconName: "Trash2", destructive: true, separator: true },
102
- [ROW_ACTION_ID.APPROVE]: { id: ROW_ACTION_ID.APPROVE, label: "Approve", iconName: "Check" },
103
- [ROW_ACTION_ID.REJECT]: { id: ROW_ACTION_ID.REJECT, label: "Reject", iconName: "X", destructive: true },
104
- [ROW_ACTION_ID.SUSPEND]: { id: ROW_ACTION_ID.SUSPEND, label: "Suspend", iconName: "Ban", destructive: true, separator: true },
105
- [ROW_ACTION_ID.RESTORE]: { id: ROW_ACTION_ID.RESTORE, label: "Restore", iconName: "RotateCcw" },
106
- [ROW_ACTION_ID.MANAGE]: { id: ROW_ACTION_ID.MANAGE, label: "Manage", iconName: "Settings" },
107
- [ROW_ACTION_ID.DUPLICATE]: { id: ROW_ACTION_ID.DUPLICATE, label: "Duplicate", iconName: "Copy" },
108
- [ROW_ACTION_ID.EXPORT]: { id: ROW_ACTION_ID.EXPORT, label: "Export", iconName: "Download" },
109
- [ROW_ACTION_ID.TRACK]: { id: ROW_ACTION_ID.TRACK, label: "Track Shipment", iconName: "Truck" },
110
- [ROW_ACTION_ID.CANCEL]: { id: ROW_ACTION_ID.CANCEL, label: "Cancel", iconName: "X", destructive: true },
111
- [ROW_ACTION_ID.REFUND]: { id: ROW_ACTION_ID.REFUND, label: "Refund", iconName: "RefreshCw", destructive: true, separator: true },
112
- [ROW_ACTION_ID.RESEND]: { id: ROW_ACTION_ID.RESEND, label: "Resend", iconName: "Send" },
113
- [ROW_ACTION_ID.REPLY]: { id: ROW_ACTION_ID.REPLY, label: "Reply", iconName: "MessageSquare" },
114
- [ROW_ACTION_ID.PUBLISH]: { id: ROW_ACTION_ID.PUBLISH, label: "Publish", iconName: "Upload" },
115
- [ROW_ACTION_ID.ARCHIVE]: { id: ROW_ACTION_ID.ARCHIVE, label: "Archive", iconName: "Archive", separator: true },
146
+ [ROW_ACTION_ID.EDIT]: { id: ROW_ACTION_ID.EDIT, label: "Edit", iconName: "Pencil", requiresAuth: true },
147
+ [ROW_ACTION_ID.VIEW]: { id: ROW_ACTION_ID.VIEW, label: "View", iconName: "Eye", requiresAuth: true },
148
+ [ROW_ACTION_ID.DELETE]: { id: ROW_ACTION_ID.DELETE, label: "Delete", iconName: "Trash2", requiresAuth: true, destructive: true, separator: true },
149
+ [ROW_ACTION_ID.APPROVE]: { id: ROW_ACTION_ID.APPROVE, label: "Approve", iconName: "Check", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.content.approve" },
150
+ [ROW_ACTION_ID.REJECT]: { id: ROW_ACTION_ID.REJECT, label: "Reject", iconName: "X", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.content.approve", destructive: true },
151
+ [ROW_ACTION_ID.SUSPEND]: { id: ROW_ACTION_ID.SUSPEND, label: "Suspend", iconName: "Ban", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.users.suspend", destructive: true, separator: true },
152
+ [ROW_ACTION_ID.RESTORE]: { id: ROW_ACTION_ID.RESTORE, label: "Restore", iconName: "RotateCcw", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.users.suspend" },
153
+ [ROW_ACTION_ID.MANAGE]: { id: ROW_ACTION_ID.MANAGE, label: "Manage", iconName: "Settings", requiresAuth: true },
154
+ [ROW_ACTION_ID.DUPLICATE]: { id: ROW_ACTION_ID.DUPLICATE, label: "Duplicate", iconName: "Copy", requiresAuth: true },
155
+ [ROW_ACTION_ID.EXPORT]: { id: ROW_ACTION_ID.EXPORT, label: "Export", iconName: "Download", requiresAuth: true },
156
+ [ROW_ACTION_ID.TRACK]: { id: ROW_ACTION_ID.TRACK, label: "Track Shipment", iconName: "Truck", requiresAuth: true },
157
+ [ROW_ACTION_ID.CANCEL]: { id: ROW_ACTION_ID.CANCEL, label: "Cancel", iconName: "X", requiresAuth: true, destructive: true },
158
+ [ROW_ACTION_ID.REFUND]: { id: ROW_ACTION_ID.REFUND, label: "Refund", iconName: "RefreshCw", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.orders.refund", destructive: true, separator: true },
159
+ [ROW_ACTION_ID.RESEND]: { id: ROW_ACTION_ID.RESEND, label: "Resend", iconName: "Send", requiresAuth: true },
160
+ [ROW_ACTION_ID.REPLY]: { id: ROW_ACTION_ID.REPLY, label: "Reply", iconName: "MessageSquare", requiresAuth: true },
161
+ [ROW_ACTION_ID.PUBLISH]: { id: ROW_ACTION_ID.PUBLISH, label: "Publish", iconName: "Upload", requiresAuth: true },
162
+ [ROW_ACTION_ID.ARCHIVE]: { id: ROW_ACTION_ID.ARCHIVE, label: "Archive", iconName: "Archive", requiresAuth: true, separator: true },
116
163
  };
117
164
  // Admin dashboard row action groups per entity type
118
165
  export const ADMIN_ROW_ACTIONS = {
@@ -166,21 +213,11 @@ export const FORM_ACTION_META = {
166
213
  [FORM_ACTION_ID.DELETE]: { id: FORM_ACTION_ID.DELETE, label: "Delete", variant: "danger", type: "button", iconName: "Trash2", destructive: true },
167
214
  [FORM_ACTION_ID.DISCARD]: { id: FORM_ACTION_ID.DISCARD, label: "Discard", variant: "ghost", type: "button", destructive: true },
168
215
  };
169
- /**
170
- * Preset ordered action groups for common form footer layouts.
171
- * Consumed by FormShell and DrawerFormFooter to determine which buttons render,
172
- * in which order (left → right).
173
- */
174
216
  export const FORM_FOOTER_PRESET = {
175
- /** Standard edit drawer: Cancel | Save Changes */
176
217
  drawerEdit: [FORM_ACTION_ID.CANCEL, FORM_ACTION_ID.SUBMIT],
177
- /** Edit drawer with delete: Delete | Cancel | Save Changes */
178
218
  drawerEditDelete: [FORM_ACTION_ID.DELETE, FORM_ACTION_ID.CANCEL, FORM_ACTION_ID.SUBMIT],
179
- /** Content editor (blog / product): Discard | Save Draft | Publish */
180
219
  contentEditor: [FORM_ACTION_ID.DISCARD, FORM_ACTION_ID.SAVE_DRAFT, FORM_ACTION_ID.PUBLISH],
181
- /** Simple modal / dialog: Cancel | Submit */
182
220
  modalForm: [FORM_ACTION_ID.CANCEL, FORM_ACTION_ID.SUBMIT],
183
- /** Settings page: Reset | Save Changes */
184
221
  settingsForm: [FORM_ACTION_ID.RESET, FORM_ACTION_ID.SUBMIT],
185
222
  };
186
223
  // ─────────────────────────────────────────────────────────────────────────────
@@ -210,23 +247,23 @@ export const DASHBOARD_QUICK_ACTION_ID = {
210
247
  USER_ADD_ADDRESS: "dqa-user-add-address",
211
248
  };
212
249
  export const DASHBOARD_QUICK_ACTION_META = {
213
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT, label: "Add Product", variant: "primary", iconName: "Plus" },
214
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER, label: "Add User", variant: "outline", iconName: "UserPlus" },
215
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE, label: "Add Store", variant: "outline", iconName: "Store" },
216
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON, label: "Add Coupon", variant: "outline", iconName: "Tag" },
217
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT, label: "Add Event", variant: "outline", iconName: "Calendar" },
218
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG, label: "New Post", variant: "outline", iconName: "FileText" },
219
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_FAQ]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_FAQ, label: "Add FAQ", variant: "outline", iconName: "HelpCircle" },
220
- [DASHBOARD_QUICK_ACTION_ID.ADMIN_SETTINGS]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_SETTINGS, label: "Settings", variant: "ghost", iconName: "Settings" },
221
- [DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_PRODUCT]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_PRODUCT, label: "List a Product", variant: "primary", iconName: "Plus" },
222
- [DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_COUPON]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_ADD_COUPON, label: "New Coupon", variant: "outline", iconName: "Tag" },
223
- [DASHBOARD_QUICK_ACTION_ID.SELLER_VIEW_ORDERS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_VIEW_ORDERS, label: "Orders", variant: "outline", iconName: "ShoppingBag" },
224
- [DASHBOARD_QUICK_ACTION_ID.SELLER_PAYOUT_REQ]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_PAYOUT_REQ, label: "Request Payout", variant: "outline", iconName: "Banknote" },
225
- [DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS, label: "Store Settings", variant: "ghost", iconName: "Settings" },
226
- [DASHBOARD_QUICK_ACTION_ID.USER_VIEW_ORDERS]: { id: DASHBOARD_QUICK_ACTION_ID.USER_VIEW_ORDERS, label: "My Orders", variant: "outline", iconName: "Package" },
227
- [DASHBOARD_QUICK_ACTION_ID.USER_VIEW_WISHLIST]: { id: DASHBOARD_QUICK_ACTION_ID.USER_VIEW_WISHLIST, label: "Wishlist", variant: "outline", iconName: "Heart" },
228
- [DASHBOARD_QUICK_ACTION_ID.USER_EDIT_PROFILE]: { id: DASHBOARD_QUICK_ACTION_ID.USER_EDIT_PROFILE, label: "Edit Profile", variant: "outline", iconName: "User" },
229
- [DASHBOARD_QUICK_ACTION_ID.USER_ADD_ADDRESS]: { id: DASHBOARD_QUICK_ACTION_ID.USER_ADD_ADDRESS, label: "Add Address", variant: "outline", iconName: "MapPin" },
250
+ [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" },
251
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER, label: "Add User", variant: "outline", iconName: "UserPlus", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.users.create" },
252
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE, label: "Add Store", variant: "outline", iconName: "Store", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.stores.create" },
253
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON, label: "Add Coupon", variant: "outline", iconName: "Tag", requiresAuth: true, requiredRole: "admin", requiredPermission: "coupons.create" },
254
+ [DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT, label: "Add Event", variant: "outline", iconName: "Calendar", requiresAuth: true, requiredRole: "admin", requiredPermission: "events.create" },
255
+ [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" },
256
+ [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" },
257
+ [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" },
258
+ [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" },
259
+ [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" },
260
+ [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" },
261
+ [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" },
262
+ [DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS]: { id: DASHBOARD_QUICK_ACTION_ID.SELLER_SETTINGS, label: "Store Settings", variant: "ghost", iconName: "Settings", requiresAuth: true, requiredRole: "seller" },
263
+ [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" },
264
+ [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" },
265
+ [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" },
266
+ [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" },
230
267
  };
231
268
  // Quick action groups — ordered left-to-right in the top shortcut bar
232
269
  export const DASHBOARD_QUICK_ACTIONS = {
@@ -18,10 +18,14 @@ export const PRODUCT_FEATURE_CATEGORY_OPTIONS = [
18
18
  { value: "custom", label: "Custom" },
19
19
  ];
20
20
  export const PRODUCT_FEATURE_PRODUCT_TYPE_OPTIONS = [
21
- { value: "all", label: "All product types" },
21
+ { value: "all", label: "All listing types" },
22
22
  { value: "product", label: "Standard product" },
23
23
  { value: "auction", label: "Auction" },
24
24
  { value: "preorder", label: "Pre-order" },
25
+ { value: "prize-draw", label: "Prize draw" },
26
+ { value: "classified", label: "Classified" },
27
+ { value: "digital-code", label: "Digital code" },
28
+ { value: "live", label: "Live item" },
25
29
  ];
26
30
  export const PRODUCT_FEATURE_SCOPE_OPTIONS = [
27
31
  { value: "platform", label: "Platform (all stores)" },
@@ -315,6 +315,7 @@ export declare const productItemSchema: z.ZodObject<{
315
315
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
316
316
  prizeRevealStatus?: "pending" | "closed" | "open" | undefined;
317
317
  categorySlug?: string | undefined;
318
+ features?: string[] | undefined;
318
319
  images?: string[] | undefined;
319
320
  description?: string | undefined;
320
321
  isPromoted?: boolean | undefined;
@@ -328,7 +329,6 @@ export declare const productItemSchema: z.ZodObject<{
328
329
  name: string;
329
330
  unit?: string | undefined;
330
331
  }[] | undefined;
331
- features?: string[] | undefined;
332
332
  shippingInfo?: string | undefined;
333
333
  returnPolicy?: string | undefined;
334
334
  insurance?: boolean | undefined;
@@ -444,6 +444,7 @@ export declare const productItemSchema: z.ZodObject<{
444
444
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
445
445
  prizeRevealStatus?: "pending" | "closed" | "open" | undefined;
446
446
  categorySlug?: string | undefined;
447
+ features?: string[] | undefined;
447
448
  images?: string[] | undefined;
448
449
  description?: string | undefined;
449
450
  isPromoted?: boolean | undefined;
@@ -457,7 +458,6 @@ export declare const productItemSchema: z.ZodObject<{
457
458
  name: string;
458
459
  unit?: string | undefined;
459
460
  }[] | undefined;
460
- features?: string[] | undefined;
461
461
  shippingInfo?: string | undefined;
462
462
  returnPolicy?: string | undefined;
463
463
  insurance?: boolean | undefined;
@@ -20,7 +20,7 @@ export declare const MAX_STORE_CUSTOM_FEATURES = 20;
20
20
  export declare const MAX_FEATURES_PER_PRODUCT = 10;
21
21
  export type ProductFeatureScope = "platform" | "store";
22
22
  export type ProductFeatureCategory = "shipping" | "seller" | "condition" | "platform" | "auction" | "preorder" | "custom";
23
- export type ProductFeatureProductType = "product" | "auction" | "preorder" | "all";
23
+ export type ProductFeatureProductType = "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live" | "all";
24
24
  /**
25
25
  * Either an appkit icon-set name key (e.g. "truck") or a raw SVG path-d string.
26
26
  * Path strings must start with "M " — see isFeatureIconPath().
@@ -6,7 +6,7 @@
6
6
  * of hand-rolling parallel schemas.
7
7
  */
8
8
  import { z } from "zod";
9
- export declare const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM: z.ZodEnum<["product", "auction", "preorder", "all"]>;
9
+ export declare const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM: z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>;
10
10
  export declare const PRODUCT_FEATURE_CATEGORY_ENUM: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
11
11
  export declare const PRODUCT_FEATURE_SCOPE_ENUM: z.ZodEnum<["platform", "store"]>;
12
12
  /** Shape for POST /api/admin/features — admin creates either scope. */
@@ -17,7 +17,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
17
17
  iconColor: z.ZodOptional<z.ZodString>;
18
18
  category: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
19
19
  scope: z.ZodEnum<["platform", "store"]>;
20
- productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">;
20
+ productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">;
21
21
  storeId: z.ZodOptional<z.ZodString>;
22
22
  isActive: z.ZodBoolean;
23
23
  displayOrder: z.ZodNumber;
@@ -28,7 +28,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
28
28
  isActive: boolean;
29
29
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
30
30
  displayOrder: number;
31
- productTypes: ("all" | "product" | "auction" | "preorder")[];
31
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
32
32
  storeId?: string | undefined;
33
33
  description?: string | undefined;
34
34
  iconColor?: string | undefined;
@@ -39,7 +39,7 @@ export declare const productFeatureAdminCreateSchema: z.ZodObject<{
39
39
  isActive: boolean;
40
40
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
41
41
  displayOrder: number;
42
- productTypes: ("all" | "product" | "auction" | "preorder")[];
42
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
43
43
  storeId?: string | undefined;
44
44
  description?: string | undefined;
45
45
  iconColor?: string | undefined;
@@ -51,7 +51,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
51
51
  icon: z.ZodString;
52
52
  iconColor: z.ZodOptional<z.ZodString>;
53
53
  category: z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>;
54
- productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">;
54
+ productTypes: z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">;
55
55
  isActive: z.ZodBoolean;
56
56
  displayOrder: z.ZodNumber;
57
57
  }, "strip", z.ZodTypeAny, {
@@ -60,7 +60,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
60
60
  isActive: boolean;
61
61
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
62
62
  displayOrder: number;
63
- productTypes: ("all" | "product" | "auction" | "preorder")[];
63
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
64
64
  description?: string | undefined;
65
65
  iconColor?: string | undefined;
66
66
  }, {
@@ -69,7 +69,7 @@ export declare const productFeatureStoreCreateSchema: z.ZodObject<{
69
69
  isActive: boolean;
70
70
  category: "seller" | "auction" | "preorder" | "condition" | "shipping" | "custom" | "platform";
71
71
  displayOrder: number;
72
- productTypes: ("all" | "product" | "auction" | "preorder")[];
72
+ productTypes: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[];
73
73
  description?: string | undefined;
74
74
  iconColor?: string | undefined;
75
75
  }>;
@@ -80,7 +80,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
80
80
  icon: z.ZodOptional<z.ZodString>;
81
81
  iconColor: z.ZodOptional<z.ZodString>;
82
82
  category: z.ZodOptional<z.ZodEnum<["shipping", "seller", "condition", "platform", "auction", "preorder", "custom"]>>;
83
- productTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "all"]>, "many">>;
83
+ productTypes: z.ZodOptional<z.ZodArray<z.ZodEnum<["product", "auction", "preorder", "prize-draw", "classified", "digital-code", "live", "all"]>, "many">>;
84
84
  isActive: z.ZodOptional<z.ZodBoolean>;
85
85
  displayOrder: z.ZodOptional<z.ZodNumber>;
86
86
  }, "strip", z.ZodTypeAny, {
@@ -91,7 +91,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
91
91
  description?: string | undefined;
92
92
  iconColor?: string | undefined;
93
93
  displayOrder?: number | undefined;
94
- productTypes?: ("all" | "product" | "auction" | "preorder")[] | undefined;
94
+ productTypes?: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[] | undefined;
95
95
  }, {
96
96
  label?: string | undefined;
97
97
  icon?: string | undefined;
@@ -100,7 +100,7 @@ export declare const productFeatureUpdateSchema: z.ZodObject<{
100
100
  description?: string | undefined;
101
101
  iconColor?: string | undefined;
102
102
  displayOrder?: number | undefined;
103
- productTypes?: ("all" | "product" | "auction" | "preorder")[] | undefined;
103
+ productTypes?: ("all" | "product" | "auction" | "preorder" | "prize-draw" | "classified" | "digital-code" | "live")[] | undefined;
104
104
  }>;
105
105
  export type ProductFeatureAdminCreatePayload = z.infer<typeof productFeatureAdminCreateSchema>;
106
106
  export type ProductFeatureStoreCreatePayload = z.infer<typeof productFeatureStoreCreateSchema>;
@@ -10,6 +10,10 @@ export const PRODUCT_FEATURE_PRODUCT_TYPE_ENUM = z.enum([
10
10
  "product",
11
11
  "auction",
12
12
  "preorder",
13
+ "prize-draw",
14
+ "classified",
15
+ "digital-code",
16
+ "live",
13
17
  "all",
14
18
  ]);
15
19
  export const PRODUCT_FEATURE_CATEGORY_ENUM = z.enum([
@@ -61,7 +61,7 @@ export function SellerCouponEditorView({ couponId, initial, onSave, onCancel, })
61
61
  }
62
62
  };
63
63
  const isEdit = Boolean(couponId);
64
- return (_jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: "max-w-lg mx-auto", children: [_jsx(Div, { className: "border-b border-[var(--appkit-color-border,#e5e7eb)] dark:border-[var(--appkit-color-border-dark,#374151)] px-6 py-5", children: _jsxs(Row, { className: "items-center justify-between gap-3", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-[var(--appkit-color-text-primary)]", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", className: "px-6 py-6", children: [error && (_jsx(Div, { className: "rounded-lg border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
64
+ return (_jsx("form", { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: "max-w-lg mx-auto rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] shadow-sm overflow-hidden", children: [_jsx("div", { className: "h-[3px] w-full", style: { background: "linear-gradient(to right,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 55%,var(--appkit-color-secondary-400,#84e122) 100%)" }, "aria-hidden": "true" }), _jsx(Div, { className: "border-b border-[var(--appkit-color-border)] px-6 py-5", children: _jsxs(Row, { className: "items-center justify-between gap-3", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-[var(--appkit-color-text)]", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", className: "px-6 py-6", children: [error && (_jsx(Div, { className: "rounded-lg border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
65
65
  ? "Enter a value between 1 and 100"
66
- : "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Usage Limits" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Validity Period" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsx(Div, { className: "flex items-center gap-3 rounded-lg border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-4 py-3", children: _jsxs("label", { className: "flex items-center gap-3 cursor-pointer w-full", children: [_jsx("input", { type: "checkbox", checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-primary)]", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-6 py-4", children: _jsxs(Row, { className: "items-center justify-end gap-3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] }) }));
66
+ : "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Usage Limits" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-secondary)] mb-3", children: "Validity Period" }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsx(Div, { className: "flex items-center gap-3 rounded-lg border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)] px-4 py-3", children: _jsxs("label", { className: "flex items-center gap-3 cursor-pointer w-full", children: [_jsx("input", { type: "checkbox", checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-zinc-300 dark:border-slate-600 text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "font-medium text-[var(--appkit-color-text-primary)]", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)] px-6 py-4", children: _jsxs(Row, { className: "items-center justify-end gap-3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] }) }));
67
67
  }
@@ -1,8 +1,14 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Heading, Span, Text } from "../../../../ui";
3
- function SellerAnalyticsStatCard({ label, value, icon, colorClassName, }) {
4
- return (_jsx(Div, { className: "bg-white dark:bg-neutral-900 rounded-xl border border-neutral-200 dark:border-neutral-800 p-4 sm:p-5", children: _jsxs(Div, { className: "flex items-start justify-between gap-3", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-xs sm:text-sm text-neutral-600 dark:text-neutral-400 font-medium uppercase tracking-wide", children: label }), _jsx(Heading, { level: 3, className: `mt-1 text-2xl font-bold ${colorClassName}`, children: value })] }), _jsx(Span, { className: "text-2xl sm:text-3xl", "aria-hidden": true, children: icon })] }) }));
3
+ import { TrendingUp, ShoppingBag, Package, CheckCircle } from "lucide-react";
4
+ import { Div } from "../../../../ui";
5
+ const GREEN_GRAD = "linear-gradient(135deg,var(--appkit-color-cobalt,#3570fc) 0%,var(--appkit-color-secondary-400,#84e122) 100%)";
6
+ const BRAND_GRAD = "linear-gradient(135deg,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 55%,var(--appkit-color-secondary-400,#84e122) 100%)";
7
+ const AMBER_GRAD = "linear-gradient(135deg,var(--appkit-color-amber-500,#f59e0b) 0%,var(--appkit-color-amber-600,#d97706) 100%)";
8
+ const BLUE_GRAD = "linear-gradient(135deg,var(--appkit-color-primary-700,#1343de) 0%,var(--appkit-color-cobalt,#3570fc) 100%)";
9
+ function StatCard({ label, value, gradient, Icon }) {
10
+ return (_jsxs(Div, { className: "relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]", style: { background: gradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx("div", { className: "mt-2 text-2xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-none", children: value })] }), _jsx("div", { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center", style: { background: gradient }, children: _jsx(Icon, { className: "w-5 h-5 text-white" }) })] })] }));
5
11
  }
6
12
  export function SellerAnalyticsStats({ summary, labels = {}, formatRevenue = (amount) => String(amount), className = "", }) {
7
- return (_jsxs(Div, { className: `grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-4 gap-4 ${className}`, children: [_jsx(SellerAnalyticsStatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatRevenue(summary.totalRevenue), icon: "\uD83D\uDCB0", colorClassName: "text-emerald-600 dark:text-emerald-400" }), _jsx(SellerAnalyticsStatCard, { label: labels.totalOrders ?? "Total Orders", value: String(summary.totalOrders), icon: "\uD83D\uDCE6", colorClassName: "text-blue-600 dark:text-blue-400" }), _jsx(SellerAnalyticsStatCard, { label: labels.totalProducts ?? "Total Products", value: String(summary.totalProducts), icon: "\uD83D\uDECD\uFE0F", colorClassName: "text-violet-600 dark:text-violet-400" }), _jsx(SellerAnalyticsStatCard, { label: labels.publishedProducts ?? "Published Products", value: String(summary.publishedProducts), icon: "\u2705", colorClassName: "text-amber-600 dark:text-amber-400" })] }));
13
+ return (_jsxs(Div, { className: `grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 ${className}`, children: [_jsx(StatCard, { label: labels.totalRevenue ?? "Total Revenue", value: formatRevenue(summary.totalRevenue), gradient: GREEN_GRAD, Icon: TrendingUp }), _jsx(StatCard, { label: labels.totalOrders ?? "Total Orders", value: String(summary.totalOrders), gradient: BRAND_GRAD, Icon: ShoppingBag }), _jsx(StatCard, { label: labels.totalProducts ?? "Total Products", value: String(summary.totalProducts), gradient: AMBER_GRAD, Icon: Package }), _jsx(StatCard, { label: labels.publishedProducts ?? "Published", value: String(summary.publishedProducts), gradient: BLUE_GRAD, Icon: CheckCircle })] }));
8
14
  }
@@ -1,5 +1,8 @@
1
1
  export interface StoreBundlesPageViewProps {
2
2
  storeSlug: string;
3
+ onBuyNow?: (input: {
4
+ bundleSlug: string;
5
+ }) => Promise<unknown>;
3
6
  }
4
7
  /**
5
8
  * Public store → Bundles tab.
@@ -9,4 +12,4 @@ export interface StoreBundlesPageViewProps {
9
12
  * the seller's active bundle categories and hands them to
10
13
  * `CategoryBundlesListing`. Mirrors the StorePrizeDrawsPageView pattern.
11
14
  */
12
- export declare function StoreBundlesPageView({ storeSlug, }: StoreBundlesPageViewProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
15
+ export declare function StoreBundlesPageView({ storeSlug, onBuyNow, }: StoreBundlesPageViewProps): Promise<import("react/jsx-runtime").JSX.Element | null>;
@@ -10,7 +10,7 @@ import { CategoryBundlesListing } from "../../categories/components/CategoryBund
10
10
  * the seller's active bundle categories and hands them to
11
11
  * `CategoryBundlesListing`. Mirrors the StorePrizeDrawsPageView pattern.
12
12
  */
13
- export async function StoreBundlesPageView({ storeSlug, }) {
13
+ export async function StoreBundlesPageView({ storeSlug, onBuyNow, }) {
14
14
  const store = await getStoreBySlug(storeSlug);
15
15
  const storeId = store?.id;
16
16
  if (!storeId) {
@@ -20,5 +20,5 @@ export async function StoreBundlesPageView({ storeSlug, }) {
20
20
  .listByType("bundle", { activeOnly: true, limit: 50 })
21
21
  .then((rows) => rows.filter((c) => c.createdByStoreId === storeId))
22
22
  .catch(() => []);
23
- return _jsx(CategoryBundlesListing, { initialBundles: bundles });
23
+ return _jsx(CategoryBundlesListing, { initialBundles: bundles, onBuyNow: onBuyNow });
24
24
  }
@@ -80,6 +80,7 @@ export function StoresIndexListing({ initialData }) {
80
80
  setSearchInput("");
81
81
  }, [table]);
82
82
  const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
83
+ const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
83
84
  const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
84
85
  table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
85
86
  activeFilterCount > 0;
@@ -132,5 +133,5 @@ export function StoresIndexListing({ initialData }) {
132
133
  }) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: stores.map((store) => {
133
134
  const storeKey = store.storeSlug ?? store.id;
134
135
  return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
135
- }) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(StoreFilters, { table: pendingTable }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }));
136
+ }) })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(StoreFilters, { table: pendingTable }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] }))] }));
136
137
  }
@@ -24,12 +24,14 @@ export function getAdminAppLite() {
24
24
  return existing;
25
25
  }
26
26
  const keyPath = nodePath().join(nodeCwd(), "firebase-admin-key.json");
27
+ const storageBucket = process.env.FIREBASE_ADMIN_STORAGE_BUCKET?.trim() ??
28
+ process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET?.trim();
27
29
  let app;
28
30
  if (nodeFs().existsSync(keyPath)) {
29
31
  const sa = JSON.parse(nodeFs().readFileSync(keyPath, "utf8"));
30
32
  const dbUrl = process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
31
33
  `https://${sa.project_id}-default-rtdb.firebaseio.com`;
32
- app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl });
34
+ app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl, ...(storageBucket && { storageBucket }) });
33
35
  }
34
36
  else if (process.env.FIREBASE_ADMIN_PROJECT_ID &&
35
37
  process.env.FIREBASE_ADMIN_CLIENT_EMAIL &&
@@ -45,6 +47,7 @@ export function getAdminAppLite() {
45
47
  privateKey: parsePrivateKey(process.env.FIREBASE_ADMIN_PRIVATE_KEY),
46
48
  }),
47
49
  databaseURL: dbUrl,
50
+ ...(storageBucket && { storageBucket }),
48
51
  });
49
52
  }
50
53
  else if (
@@ -66,7 +69,10 @@ export function getAdminAppLite() {
66
69
  process.env.FIREBASE_ADMIN_DATABASE_URL ??
67
70
  process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
68
71
  (projectId ? `https://${projectId}-default-rtdb.firebaseio.com` : undefined);
69
- app = initializeApp(dbUrl ? { databaseURL: dbUrl } : undefined);
72
+ app = initializeApp({
73
+ ...(dbUrl && { databaseURL: dbUrl }),
74
+ ...(storageBucket && { storageBucket }),
75
+ });
70
76
  }
71
77
  else {
72
78
  throw new Error("@mohasinac/db-firebase: Firebase Admin credentials not found.");
@@ -67,13 +67,15 @@ export function getAdminApp() {
67
67
  return existing;
68
68
  }
69
69
  const keyPath = nodePath().join(nodeCwd(), "firebase-admin-key.json");
70
+ const storageBucket = process.env.FIREBASE_ADMIN_STORAGE_BUCKET?.trim() ??
71
+ process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET?.trim();
70
72
  let app;
71
73
  try {
72
74
  if (nodeFs().existsSync(keyPath)) {
73
75
  const sa = JSON.parse(nodeFs().readFileSync(keyPath, "utf8"));
74
76
  const dbUrl = process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL ??
75
77
  `https://${sa.project_id}-default-rtdb.firebaseio.com`;
76
- app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl });
78
+ app = initializeApp({ credential: cert(keyPath), databaseURL: dbUrl, ...(storageBucket && { storageBucket }) });
77
79
  }
78
80
  else if (process.env.FIREBASE_ADMIN_PROJECT_ID &&
79
81
  process.env.FIREBASE_ADMIN_CLIENT_EMAIL &&
@@ -89,6 +91,7 @@ export function getAdminApp() {
89
91
  privateKey: parsePrivateKey(process.env.FIREBASE_ADMIN_PRIVATE_KEY),
90
92
  }),
91
93
  databaseURL: dbUrl,
94
+ ...(storageBucket && { storageBucket }),
92
95
  });
93
96
  }
94
97
  else if (
@@ -112,7 +115,10 @@ export function getAdminApp() {
112
115
  (projectId
113
116
  ? `https://${projectId}-default-rtdb.firebaseio.com`
114
117
  : undefined);
115
- app = initializeApp(dbUrl ? { databaseURL: dbUrl } : undefined);
118
+ app = initializeApp({
119
+ ...(dbUrl && { databaseURL: dbUrl }),
120
+ ...(storageBucket && { storageBucket }),
121
+ });
116
122
  }
117
123
  else {
118
124
  throw new Error("@mohasinac/db-firebase: Firebase Admin credentials not found.\n" +