@mohasinac/appkit 2.7.49 → 2.7.52

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 (232) hide show
  1. package/dist/_internal/client/features/layout/SidebarCollapseToggle.d.ts +19 -0
  2. package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +15 -0
  3. package/dist/_internal/client/features/layout/index.d.ts +2 -0
  4. package/dist/_internal/client/features/layout/index.js +1 -0
  5. package/dist/_internal/server/features/checkout/actions.js +25 -2
  6. package/dist/_internal/server/features/promotions/actions.js +3 -1
  7. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.d.ts +1 -0
  8. package/dist/_internal/server/jobs/core/__tests__/couponExpiry.test.js +65 -0
  9. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.d.ts +1 -0
  10. package/dist/_internal/server/jobs/core/__tests__/listingProcessor.test.js +163 -0
  11. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.d.ts +1 -0
  12. package/dist/_internal/server/jobs/core/__tests__/mediaTmpCleanup.test.js +115 -0
  13. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.d.ts +1 -0
  14. package/dist/_internal/server/jobs/core/__tests__/pendingOrderTimeout.test.js +96 -0
  15. package/dist/_internal/shared/actions/action-registry.js +149 -0
  16. package/dist/client.d.ts +45 -6
  17. package/dist/client.js +27 -4
  18. package/dist/constants/api-endpoints.d.ts +42 -0
  19. package/dist/constants/api-endpoints.js +14 -0
  20. package/dist/features/_guide-cls.d.ts +13 -0
  21. package/dist/features/_guide-cls.js +14 -0
  22. package/dist/features/about/components/HelpPageView.js +29 -26
  23. package/dist/features/account/components/BuyerAccountGuideView.d.ts +1 -0
  24. package/dist/features/account/components/BuyerAccountGuideView.js +39 -0
  25. package/dist/features/account/components/BuyerAuctionsGuideView.d.ts +1 -0
  26. package/dist/features/account/components/BuyerAuctionsGuideView.js +38 -0
  27. package/dist/features/account/components/BuyerOrdersGuideView.d.ts +1 -0
  28. package/dist/features/account/components/BuyerOrdersGuideView.js +48 -0
  29. package/dist/features/account/components/BuyerShoppingGuideView.d.ts +1 -0
  30. package/dist/features/account/components/BuyerShoppingGuideView.js +38 -0
  31. package/dist/features/account/components/UserSidebar.js +2 -1
  32. package/dist/features/admin/components/AdminAnalyticsGuideView.d.ts +1 -0
  33. package/dist/features/admin/components/AdminAnalyticsGuideView.js +24 -0
  34. package/dist/features/admin/components/AdminBlogEditorView.js +102 -75
  35. package/dist/features/admin/components/AdminBundleEditorView.js +20 -14
  36. package/dist/features/admin/components/AdminBundlesView.d.ts +1 -3
  37. package/dist/features/admin/components/AdminBundlesView.js +173 -52
  38. package/dist/features/admin/components/AdminCatalogGuideView.d.ts +1 -0
  39. package/dist/features/admin/components/AdminCatalogGuideView.js +28 -0
  40. package/dist/features/admin/components/AdminContentGuideView.d.ts +1 -0
  41. package/dist/features/admin/components/AdminContentGuideView.js +36 -0
  42. package/dist/features/admin/components/AdminCouponEditorView.js +1 -1
  43. package/dist/features/admin/components/AdminGuideHubView.d.ts +5 -0
  44. package/dist/features/admin/components/AdminGuideHubView.js +79 -0
  45. package/dist/features/admin/components/AdminOrdersGuideView.d.ts +1 -0
  46. package/dist/features/admin/components/AdminOrdersGuideView.js +44 -0
  47. package/dist/features/admin/components/AdminProductsView.js +8 -3
  48. package/dist/features/admin/components/AdminSidebar.js +2 -1
  49. package/dist/features/admin/components/AdminSiteConfigGuideView.d.ts +1 -0
  50. package/dist/features/admin/components/AdminSiteConfigGuideView.js +21 -0
  51. package/dist/features/admin/components/AdminStoresGuideView.d.ts +1 -0
  52. package/dist/features/admin/components/AdminStoresGuideView.js +32 -0
  53. package/dist/features/admin/components/AdminTeamGuideView.d.ts +1 -0
  54. package/dist/features/admin/components/AdminTeamGuideView.js +33 -0
  55. package/dist/features/admin/components/AdminTrustGuideView.d.ts +1 -0
  56. package/dist/features/admin/components/AdminTrustGuideView.js +40 -0
  57. package/dist/features/admin/components/AdminUsersGuideView.d.ts +1 -0
  58. package/dist/features/admin/components/AdminUsersGuideView.js +38 -0
  59. package/dist/features/auth/repository/session.repository.js +16 -1
  60. package/dist/features/cart/hooks/useAddToCart.d.ts +2 -0
  61. package/dist/features/cart/hooks/useAddToCart.js +2 -0
  62. package/dist/features/cart/hooks/useGuestCart.d.ts +2 -0
  63. package/dist/features/cart/utils/guest-cart.d.ts +7 -0
  64. package/dist/features/cart/utils/pending-ops.d.ts +6 -0
  65. package/dist/features/categories/components/CategoryBundlesListing.js +3 -25
  66. package/dist/features/categories/components/CategoryProductsListing.js +6 -10
  67. package/dist/features/categories/components/index.d.ts +0 -2
  68. package/dist/features/categories/components/index.js +0 -1
  69. package/dist/features/categories/schemas/firestore.d.ts +9 -0
  70. package/dist/features/events/components/AdminEventEditorView.js +223 -269
  71. package/dist/features/events/components/EventRaffleWinnerView.d.ts +9 -1
  72. package/dist/features/events/components/EventRaffleWinnerView.js +3 -3
  73. package/dist/features/events/components/SpinWheelView.js +3 -3
  74. package/dist/features/grouped/repository/grouped-listings.repository.d.ts +9 -0
  75. package/dist/features/grouped/repository/grouped-listings.repository.js +12 -0
  76. package/dist/features/layout/BottomNavLayout.d.ts +5 -0
  77. package/dist/features/layout/BottomNavLayout.js +21 -0
  78. package/dist/features/layout/TitleBar.js +7 -1
  79. package/dist/features/layout/TitleBarLayout.d.ts +8 -2
  80. package/dist/features/layout/TitleBarLayout.js +11 -7
  81. package/dist/features/media/upload/MediaUploadField.js +1 -1
  82. package/dist/features/orders/schemas/index.d.ts +2 -2
  83. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +12 -6
  84. package/dist/features/products/components/AuctionsIndexListing.js +3 -1
  85. package/dist/features/products/components/MarketplaceBundleCard.d.ts +21 -0
  86. package/dist/features/products/components/MarketplaceBundleCard.js +56 -0
  87. package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
  88. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +3 -1
  89. package/dist/features/products/components/PrizeDrawEntryActions.js +8 -12
  90. package/dist/features/products/components/ProductDetailActions.d.ts +5 -1
  91. package/dist/features/products/components/ProductDetailActions.js +3 -1
  92. package/dist/features/products/components/ProductDetailPageView.d.ts +2 -0
  93. package/dist/features/products/components/ProductDetailPageView.js +4 -0
  94. package/dist/features/products/components/ProductGrid.js +3 -3
  95. package/dist/features/products/components/ProductsIndexListing.js +18 -9
  96. package/dist/features/products/components/index.d.ts +2 -4
  97. package/dist/features/products/components/index.js +1 -2
  98. package/dist/features/promotions/components/CouponCard.d.ts +11 -1
  99. package/dist/features/promotions/components/CouponCard.js +36 -2
  100. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +27 -0
  101. package/dist/features/promotions/repository/claimed-coupons.repository.js +115 -0
  102. package/dist/features/promotions/schemas/firestore.d.ts +34 -0
  103. package/dist/features/promotions/schemas/firestore.js +14 -1
  104. package/dist/features/reviews/components/index.d.ts +0 -2
  105. package/dist/features/reviews/components/index.js +0 -1
  106. package/dist/features/seller/components/BrandInlineSelect.js +23 -3
  107. package/dist/features/seller/components/CategoryInlineSelect.js +20 -3
  108. package/dist/features/seller/components/SellerAddressesView.js +3 -2
  109. package/dist/features/seller/components/SellerAnalyticsAlertsView.d.ts +6 -0
  110. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +124 -0
  111. package/dist/features/seller/components/SellerAnalyticsView.d.ts +2 -0
  112. package/dist/features/seller/components/SellerAnalyticsView.js +17 -9
  113. package/dist/features/seller/components/SellerBidsView.js +33 -3
  114. package/dist/features/seller/components/SellerBundlesView.d.ts +5 -0
  115. package/dist/features/seller/components/SellerBundlesView.js +104 -0
  116. package/dist/features/seller/components/SellerClassifiedView.d.ts +5 -0
  117. package/dist/features/seller/components/SellerClassifiedView.js +113 -0
  118. package/dist/features/seller/components/SellerDashboardView.js +19 -7
  119. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +5 -0
  120. package/dist/features/seller/components/SellerDigitalCodesView.js +115 -0
  121. package/dist/features/seller/components/SellerGoogleReviewsView.d.ts +14 -0
  122. package/dist/features/seller/components/SellerGoogleReviewsView.js +95 -0
  123. package/dist/features/seller/components/SellerGroupedListingsView.d.ts +6 -0
  124. package/dist/features/seller/components/SellerGroupedListingsView.js +112 -0
  125. package/dist/features/seller/components/SellerLiveView.d.ts +5 -0
  126. package/dist/features/seller/components/SellerLiveView.js +113 -0
  127. package/dist/features/seller/components/SellerOrdersView.js +53 -2
  128. package/dist/features/seller/components/SellerPayoutMethodsView.d.ts +7 -0
  129. package/dist/features/seller/components/SellerPayoutMethodsView.js +120 -0
  130. package/dist/features/seller/components/SellerPayoutSettingsView.js +50 -12
  131. package/dist/features/seller/components/SellerProductShell.d.ts +3 -1
  132. package/dist/features/seller/components/SellerProductShell.js +35 -11
  133. package/dist/features/seller/components/SellerProductsCards.d.ts +19 -0
  134. package/dist/features/seller/components/SellerProductsCards.js +19 -0
  135. package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +14 -0
  136. package/dist/features/seller/components/SellerProductsFilterDrawer.js +22 -0
  137. package/dist/features/seller/components/SellerProductsView.d.ts +3 -1
  138. package/dist/features/seller/components/SellerProductsView.js +92 -49
  139. package/dist/features/seller/components/SellerReviewsView.js +67 -4
  140. package/dist/features/seller/components/SellerShippingConfigsView.d.ts +7 -0
  141. package/dist/features/seller/components/SellerShippingConfigsView.js +149 -0
  142. package/dist/features/seller/components/SellerShippingView.js +37 -10
  143. package/dist/features/seller/components/SellerSidebar.js +2 -1
  144. package/dist/features/seller/components/SellerStoreCategoriesView.d.ts +9 -0
  145. package/dist/features/seller/components/SellerStoreCategoriesView.js +122 -0
  146. package/dist/features/seller/components/SellerStorefrontView.d.ts +0 -7
  147. package/dist/features/seller/components/SellerStorefrontView.js +26 -29
  148. package/dist/features/seller/components/SellerTemplatesView.d.ts +10 -0
  149. package/dist/features/seller/components/SellerTemplatesView.js +265 -0
  150. package/dist/features/seller/components/index.d.ts +26 -0
  151. package/dist/features/seller/components/index.js +13 -0
  152. package/dist/features/seller/components/seller-products-styles.d.ts +7 -0
  153. package/dist/features/seller/components/seller-products-styles.js +14 -0
  154. package/dist/features/shell/FormShell.d.ts +7 -1
  155. package/dist/features/shell/FormShell.js +5 -2
  156. package/dist/features/store-extensions/index.d.ts +4 -0
  157. package/dist/features/store-extensions/index.js +4 -0
  158. package/dist/features/store-extensions/repository/rbac.repositories.d.ts +23 -0
  159. package/dist/features/store-extensions/repository/rbac.repositories.js +32 -0
  160. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +91 -0
  161. package/dist/features/store-extensions/repository/store-extensions.repositories.js +127 -0
  162. package/dist/features/store-extensions/schemas/firestore.d.ts +244 -0
  163. package/dist/features/store-extensions/schemas/firestore.js +158 -0
  164. package/dist/features/store-extensions/schemas/rbac.d.ts +65 -0
  165. package/dist/features/store-extensions/schemas/rbac.js +43 -0
  166. package/dist/features/stores/api/[storeSlug]/reviews/route.js +49 -7
  167. package/dist/features/stores/components/InteractiveStoreCard.js +7 -3
  168. package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +3 -0
  169. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +101 -0
  170. package/dist/features/stores/components/StoreFinanceGuideView.d.ts +3 -0
  171. package/dist/features/stores/components/StoreFinanceGuideView.js +79 -0
  172. package/dist/features/stores/components/StoreGuideHubView.d.ts +5 -0
  173. package/dist/features/stores/components/StoreGuideHubView.js +89 -0
  174. package/dist/features/stores/components/StoreListingsGuideView.d.ts +3 -0
  175. package/dist/features/stores/components/StoreListingsGuideView.js +151 -0
  176. package/dist/features/stores/components/StoreOrdersGuideView.d.ts +3 -0
  177. package/dist/features/stores/components/StoreOrdersGuideView.js +122 -0
  178. package/dist/features/stores/components/StoreProductsListing.js +6 -10
  179. package/dist/features/stores/components/StoreReviewsListing.js +100 -23
  180. package/dist/features/stores/components/StoreSettingsGuideView.d.ts +3 -0
  181. package/dist/features/stores/components/StoreSettingsGuideView.js +56 -0
  182. package/dist/features/stores/components/index.d.ts +12 -1
  183. package/dist/features/stores/components/index.js +6 -1
  184. package/dist/features/stores/hooks/useStores.d.ts +5 -0
  185. package/dist/features/stores/hooks/useStores.js +10 -0
  186. package/dist/features/stores/manifest.js +1 -1
  187. package/dist/features/stores/schemas/firestore.d.ts +2 -0
  188. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +13 -2
  189. package/dist/index.d.ts +60 -9
  190. package/dist/index.js +66 -15
  191. package/dist/next/routing/route-map.d.ts +134 -0
  192. package/dist/next/routing/route-map.js +59 -0
  193. package/dist/providers/db-firebase/__tests__/filter-aliases.test.d.ts +1 -0
  194. package/dist/providers/db-firebase/__tests__/filter-aliases.test.js +93 -0
  195. package/dist/providers/db-firebase/sieve.d.ts +49 -0
  196. package/dist/providers/db-firebase/sieve.js +61 -8
  197. package/dist/react/hooks/useFormStatePreservation.d.ts +17 -0
  198. package/dist/react/hooks/useFormStatePreservation.js +62 -0
  199. package/dist/react/hooks/useInlineRowEdit.d.ts +24 -0
  200. package/dist/react/hooks/useInlineRowEdit.js +68 -0
  201. package/dist/react/index.d.ts +4 -0
  202. package/dist/react/index.js +4 -0
  203. package/dist/repositories/index.d.ts +5 -0
  204. package/dist/repositories/index.js +7 -0
  205. package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
  206. package/dist/seed/claimed-coupons-seed-data.d.ts +13 -0
  207. package/dist/seed/claimed-coupons-seed-data.js +79 -0
  208. package/dist/seed/index.d.ts +2 -0
  209. package/dist/seed/index.js +3 -0
  210. package/dist/seed/manifest.js +17 -0
  211. package/dist/seed/store-extensions-seed-data.d.ts +19 -0
  212. package/dist/seed/store-extensions-seed-data.js +421 -0
  213. package/dist/tailwind-utilities.css +1 -1
  214. package/dist/ui/components/Button.style.css +20 -12
  215. package/dist/ui/components/ClaimCouponButton.d.ts +28 -0
  216. package/dist/ui/components/ClaimCouponButton.js +89 -0
  217. package/dist/ui/components/ListingToolbar.d.ts +8 -1
  218. package/dist/ui/components/ListingToolbar.js +4 -2
  219. package/dist/ui/components/QuickCreateModal.d.ts +15 -0
  220. package/dist/ui/components/QuickCreateModal.js +48 -0
  221. package/dist/ui/components/QuickCreateModal.style.css +84 -0
  222. package/dist/ui/components/StickyBottomBar.d.ts +22 -0
  223. package/dist/ui/components/StickyBottomBar.js +20 -0
  224. package/dist/ui/components/VacationBanner.d.ts +11 -0
  225. package/dist/ui/components/VacationBanner.js +16 -0
  226. package/dist/ui/components/__tests__/BulkActionBar.test.d.ts +1 -0
  227. package/dist/ui/components/__tests__/BulkActionBar.test.js +96 -0
  228. package/dist/ui/components/__tests__/ListingToolbar.test.d.ts +1 -0
  229. package/dist/ui/components/__tests__/ListingToolbar.test.js +125 -0
  230. package/dist/ui/index.d.ts +9 -1
  231. package/dist/ui/index.js +4 -0
  232. package/package.json +8 -2
@@ -0,0 +1,265 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback, useMemo } from "react";
4
+ import { Plus } from "lucide-react";
5
+ import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
+ import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, FilterDrawer, Input, ListingToolbar, Pagination, RowActionMenu, Select, SideDrawer, Stack, Text, } from "../../../ui";
8
+ import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { toRecordArray, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
10
+ import { TABLE_KEYS } from "../../../constants/table-keys";
11
+ const PAGE_SIZE = 25;
12
+ const DEFAULT_SORT = "name";
13
+ const SORT_OPTIONS = [
14
+ { value: "name", label: "Name A–Z" },
15
+ { value: "-name", label: "Name Z–A" },
16
+ { value: "-createdAt", label: "Newest" },
17
+ ];
18
+ const CONDITION_OPTIONS = [
19
+ { value: "", label: "Any condition" },
20
+ { value: "new", label: "New" },
21
+ { value: "like_new", label: "Like New" },
22
+ { value: "good", label: "Good" },
23
+ { value: "fair", label: "Fair" },
24
+ { value: "used", label: "Used" },
25
+ ];
26
+ const EMPTY_DRAFT = {
27
+ name: "",
28
+ description: "",
29
+ category: "",
30
+ brand: "",
31
+ condition: "",
32
+ };
33
+ const COLUMNS = [
34
+ {
35
+ key: "name",
36
+ header: "Name",
37
+ render: (row) => _jsx(Text, { className: "text-sm font-medium", children: row.name }),
38
+ },
39
+ {
40
+ key: "category",
41
+ header: "Category",
42
+ render: (row) => (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.category || "—" })),
43
+ },
44
+ {
45
+ key: "brand",
46
+ header: "Brand",
47
+ render: (row) => (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.brand || "—" })),
48
+ },
49
+ {
50
+ key: "condition",
51
+ header: "Condition",
52
+ render: (row) => row.condition ? (_jsx("span", { className: "inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium bg-zinc-100 text-zinc-600 dark:bg-slate-800 dark:text-slate-400 capitalize", children: row.condition.replace(/_/g, " ") })) : (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: "\u2014" })),
53
+ },
54
+ ];
55
+ export function SellerTemplatesView({ onDelete, onBulkDelete, onDuplicate, }) {
56
+ const table = useUrlTable({ defaults: { sort: DEFAULT_SORT } });
57
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
58
+ const [filterOpen, setFilterOpen] = useState(false);
59
+ const [pendingCondition, setPendingCondition] = useState(table.get(TABLE_KEYS.CONDITION) || "");
60
+ // Drawer state
61
+ const [drawerOpen, setDrawerOpen] = useState(false);
62
+ const [drawerMode, setDrawerMode] = useState("create");
63
+ const [editingId, setEditingId] = useState(null);
64
+ const [draft, setDraft] = useState(EMPTY_DRAFT);
65
+ const [saving, setSaving] = useState(false);
66
+ const [savingError, setSavingError] = useState(null);
67
+ // Delete state
68
+ const [deleteTargetId, setDeleteTargetId] = useState(null);
69
+ const [deletingId, setDeletingId] = useState(null);
70
+ const [duplicatingId, setDuplicatingId] = useState(null);
71
+ const commitSearch = useCallback(() => {
72
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
73
+ }, [searchInput, table]);
74
+ const openFilters = useCallback(() => {
75
+ setPendingCondition(table.get(TABLE_KEYS.CONDITION) || "");
76
+ setFilterOpen(true);
77
+ }, [table]);
78
+ const applyFilters = useCallback(() => {
79
+ table.setMany({ [TABLE_KEYS.CONDITION]: pendingCondition });
80
+ setFilterOpen(false);
81
+ }, [pendingCondition, table]);
82
+ const resetAll = useCallback(() => {
83
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.CONDITION]: "" });
84
+ setSearchInput("");
85
+ }, [table]);
86
+ const q = table.get(TABLE_KEYS.QUERY) || "";
87
+ const conditionFilter = table.get(TABLE_KEYS.CONDITION) || "";
88
+ const sort = table.get(TABLE_KEYS.SORT) || DEFAULT_SORT;
89
+ const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
90
+ const hasActiveState = !!q || sort !== DEFAULT_SORT || !!conditionFilter;
91
+ const filterActiveCount = conditionFilter ? 1 : 0;
92
+ // Fetch all templates (server doesn't paginate this endpoint)
93
+ const { rows: allRows, isLoading, errorMessage, refetch } = useSellerListingData({
94
+ queryKey: ["seller", "templates"],
95
+ endpoint: SELLER_ENDPOINTS.TEMPLATES,
96
+ page: 1,
97
+ pageSize: 1000,
98
+ mapRows: (response) => toRecordArray(response.templates).map((item, index) => ({
99
+ id: toStringValue(item.id, `tpl-${index}`),
100
+ raw: item,
101
+ name: String(item.name ?? ""),
102
+ category: String(item.category ?? ""),
103
+ brand: String(item.brand ?? ""),
104
+ condition: String(item.condition ?? ""),
105
+ })),
106
+ });
107
+ // Client-side filter + sort
108
+ const filteredRows = useMemo(() => {
109
+ let result = allRows;
110
+ if (q)
111
+ result = result.filter((r) => r.name.toLowerCase().includes(q.toLowerCase()));
112
+ if (conditionFilter)
113
+ result = result.filter((r) => r.condition === conditionFilter);
114
+ return [...result].sort((a, b) => sort === "-name" ? b.name.localeCompare(a.name) : a.name.localeCompare(b.name));
115
+ }, [allRows, q, conditionFilter, sort]);
116
+ const totalPages = Math.max(1, Math.ceil(filteredRows.length / PAGE_SIZE));
117
+ const safePage = Math.min(Math.max(1, currentPage), totalPages);
118
+ const pageRows = filteredRows.slice((safePage - 1) * PAGE_SIZE, safePage * PAGE_SIZE);
119
+ const selection = useBulkSelection({ items: filteredRows, keyExtractor: (r) => r.id });
120
+ // Drawer helpers
121
+ const openCreate = useCallback(() => {
122
+ setDraft(EMPTY_DRAFT);
123
+ setEditingId(null);
124
+ setDrawerMode("create");
125
+ setSavingError(null);
126
+ setDrawerOpen(true);
127
+ }, []);
128
+ const openEdit = useCallback((row) => {
129
+ setDraft({
130
+ name: row.name,
131
+ description: String(row.raw.description ?? ""),
132
+ category: row.category,
133
+ brand: row.brand,
134
+ condition: row.condition,
135
+ });
136
+ setEditingId(row.id);
137
+ setDrawerMode("edit");
138
+ setSavingError(null);
139
+ setDrawerOpen(true);
140
+ }, []);
141
+ const closeDrawer = useCallback(() => {
142
+ setDrawerOpen(false);
143
+ setEditingId(null);
144
+ setSavingError(null);
145
+ }, []);
146
+ const handleSave = useCallback(async () => {
147
+ if (!draft.name.trim()) {
148
+ setSavingError("Template name is required.");
149
+ return;
150
+ }
151
+ setSaving(true);
152
+ setSavingError(null);
153
+ try {
154
+ const body = {
155
+ name: draft.name.trim(),
156
+ description: draft.description.trim() || undefined,
157
+ category: draft.category.trim() || undefined,
158
+ brand: draft.brand.trim() || undefined,
159
+ condition: draft.condition || undefined,
160
+ };
161
+ const url = drawerMode === "edit" && editingId
162
+ ? SELLER_ENDPOINTS.TEMPLATE_BY_ID(editingId)
163
+ : SELLER_ENDPOINTS.TEMPLATES;
164
+ const method = drawerMode === "edit" ? "PUT" : "POST";
165
+ await fetch(url, {
166
+ method,
167
+ headers: { "Content-Type": "application/json" },
168
+ credentials: "include",
169
+ body: JSON.stringify(body),
170
+ });
171
+ closeDrawer();
172
+ refetch?.();
173
+ }
174
+ catch {
175
+ setSavingError("Failed to save template. Please try again.");
176
+ }
177
+ finally {
178
+ setSaving(false);
179
+ }
180
+ }, [draft, drawerMode, editingId, closeDrawer, refetch]);
181
+ const handleDelete = useCallback(async (id) => {
182
+ setDeletingId(id);
183
+ try {
184
+ if (onDelete) {
185
+ await onDelete(id);
186
+ }
187
+ else {
188
+ await fetch(SELLER_ENDPOINTS.TEMPLATE_BY_ID(id), {
189
+ method: "DELETE",
190
+ credentials: "include",
191
+ });
192
+ }
193
+ refetch?.();
194
+ }
195
+ finally {
196
+ setDeletingId(null);
197
+ setDeleteTargetId(null);
198
+ }
199
+ }, [onDelete, refetch]);
200
+ const handleDuplicate = useCallback(async (row) => {
201
+ setDuplicatingId(row.id);
202
+ try {
203
+ if (onDuplicate) {
204
+ await onDuplicate(row);
205
+ }
206
+ else {
207
+ await fetch(SELLER_ENDPOINTS.TEMPLATES, {
208
+ method: "POST",
209
+ headers: { "Content-Type": "application/json" },
210
+ credentials: "include",
211
+ body: JSON.stringify({
212
+ name: `Copy of ${row.name}`,
213
+ description: String(row.raw.description ?? "") || undefined,
214
+ category: row.category || undefined,
215
+ brand: row.brand || undefined,
216
+ condition: row.condition || undefined,
217
+ }),
218
+ });
219
+ }
220
+ refetch?.();
221
+ }
222
+ finally {
223
+ setDuplicatingId(null);
224
+ }
225
+ }, [onDuplicate, refetch]);
226
+ const bulkActions = [
227
+ {
228
+ id: "bulk-delete",
229
+ label: "Delete selected",
230
+ variant: "danger",
231
+ onClick: async () => {
232
+ if (onBulkDelete) {
233
+ await onBulkDelete(selection.selectedIds);
234
+ }
235
+ else {
236
+ await Promise.all(selection.selectedIds.map((id) => fetch(SELLER_ENDPOINTS.TEMPLATE_BY_ID(id), {
237
+ method: "DELETE",
238
+ credentials: "include",
239
+ })));
240
+ }
241
+ selection.clearSelection();
242
+ refetch?.();
243
+ },
244
+ },
245
+ ];
246
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search templates by name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: "table", onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Template" })] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: safePage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedCount > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx(Div, { className: "mb-4 rounded-xl 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: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : pageRows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: q || conditionFilter
247
+ ? "No templates match your search or filters"
248
+ : "No templates yet — add your first product template" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: pageRows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids), actions: (row) => (_jsx(RowActionMenu, { actions: [
249
+ {
250
+ label: "Edit",
251
+ onClick: () => openEdit(row),
252
+ },
253
+ {
254
+ label: "Clone",
255
+ disabled: duplicatingId === row.id,
256
+ onClick: () => handleDuplicate(row),
257
+ },
258
+ {
259
+ label: "Delete",
260
+ destructive: true,
261
+ onClick: () => setDeleteTargetId(row.id),
262
+ disabled: deletingId === row.id,
263
+ },
264
+ ] })) }))] }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: () => setPendingCondition(""), activeCount: filterActiveCount, hideTrigger: true, children: _jsx(Div, { className: "space-y-4 p-4", children: _jsxs(Div, { children: [_jsx("label", { className: "mb-1.5 block text-sm font-medium text-zinc-700 dark:text-zinc-300", children: "Condition" }), _jsx(Select, { value: pendingCondition, onValueChange: setPendingCondition, options: CONDITION_OPTIONS })] }) }) }), _jsx(SideDrawer, { isOpen: drawerOpen, onClose: closeDrawer, title: drawerMode === "create" ? "New Template" : "Edit Template", mode: drawerMode, isDirty: draft.name !== "" || draft.category !== "" || draft.brand !== "", footer: _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: closeDrawer, disabled: saving, children: "Cancel" }), _jsx(Button, { size: "sm", isLoading: saving, onClick: handleSave, children: drawerMode === "create" ? "Create Template" : "Save Changes" })] }), children: _jsxs(Stack, { gap: "md", children: [savingError && (_jsx(Div, { className: "rounded-lg border border-red-200 bg-red-50 dark:bg-red-950/40 dark:border-red-900/60 px-3 py-2 text-sm text-red-700 dark:text-red-200", children: savingError })), _jsx(Input, { id: "tpl-name", label: "Template Name", value: draft.name, onChange: (e) => setDraft((d) => ({ ...d, name: e.target.value })), placeholder: "e.g. Pok\u00E9mon Card Standard", required: true, autoComplete: "off" }), _jsx(Input, { id: "tpl-description", label: "Description (optional)", value: draft.description, onChange: (e) => setDraft((d) => ({ ...d, description: e.target.value })), placeholder: "Short note about when to use this template", autoComplete: "off" }), _jsx(Input, { id: "tpl-category", label: "Category (optional)", value: draft.category, onChange: (e) => setDraft((d) => ({ ...d, category: e.target.value })), placeholder: "e.g. trading-cards", autoComplete: "off" }), _jsx(Input, { id: "tpl-brand", label: "Brand (optional)", value: draft.brand, onChange: (e) => setDraft((d) => ({ ...d, brand: e.target.value })), placeholder: "e.g. Pok\u00E9mon Company", autoComplete: "off" }), _jsx(Select, { label: "Condition (optional)", name: "tpl-condition", value: draft.condition, onValueChange: (v) => setDraft((d) => ({ ...d, condition: v })), options: CONDITION_OPTIONS })] }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Template", message: "Are you sure you want to delete this template? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
265
+ }
@@ -97,3 +97,29 @@ export { CategoryInlineSelect } from "./CategoryInlineSelect";
97
97
  export type { CategoryInlineSelectProps } from "./CategoryInlineSelect";
98
98
  export { BrandInlineSelect } from "./BrandInlineSelect";
99
99
  export type { BrandInlineSelectProps } from "./BrandInlineSelect";
100
+ export { SellerGroupedListingsView } from "./SellerGroupedListingsView";
101
+ export type { SellerGroupedListingsViewProps } from "./SellerGroupedListingsView";
102
+ export { SellerGroupedListingsView as StoreGroupedListingsView } from "./SellerGroupedListingsView";
103
+ export type { SellerGroupedListingsViewProps as StoreGroupedListingsViewProps } from "./SellerGroupedListingsView";
104
+ export { SellerAnalyticsAlertsView } from "./SellerAnalyticsAlertsView";
105
+ export type { SellerAnalyticsAlertsViewProps } from "./SellerAnalyticsAlertsView";
106
+ export { SellerAnalyticsAlertsView as StoreAnalyticsAlertsView } from "./SellerAnalyticsAlertsView";
107
+ export type { SellerAnalyticsAlertsViewProps as StoreAnalyticsAlertsViewProps } from "./SellerAnalyticsAlertsView";
108
+ export { SellerStoreCategoriesView } from "./SellerStoreCategoriesView";
109
+ export type { SellerStoreCategoriesViewProps } from "./SellerStoreCategoriesView";
110
+ export { SellerTemplatesView } from "./SellerTemplatesView";
111
+ export type { SellerTemplatesViewProps } from "./SellerTemplatesView";
112
+ export { SellerPayoutMethodsView } from "./SellerPayoutMethodsView";
113
+ export type { SellerPayoutMethodsViewProps } from "./SellerPayoutMethodsView";
114
+ export { SellerShippingConfigsView } from "./SellerShippingConfigsView";
115
+ export type { SellerShippingConfigsViewProps } from "./SellerShippingConfigsView";
116
+ export { SellerGoogleReviewsView } from "./SellerGoogleReviewsView";
117
+ export type { SellerGoogleReviewsViewProps } from "./SellerGoogleReviewsView";
118
+ export { SellerBundlesView } from "./SellerBundlesView";
119
+ export type { SellerBundlesViewProps } from "./SellerBundlesView";
120
+ export { SellerClassifiedView } from "./SellerClassifiedView";
121
+ export type { SellerClassifiedViewProps } from "./SellerClassifiedView";
122
+ export { SellerDigitalCodesView } from "./SellerDigitalCodesView";
123
+ export type { SellerDigitalCodesViewProps } from "./SellerDigitalCodesView";
124
+ export { SellerLiveView } from "./SellerLiveView";
125
+ export type { SellerLiveViewProps } from "./SellerLiveView";
@@ -52,3 +52,16 @@ export { SellerAddressesView as StoreAddressesView } from "./SellerAddressesView
52
52
  export { SellerBidsView as StoreBidsView } from "./SellerBidsView";
53
53
  export { CategoryInlineSelect } from "./CategoryInlineSelect";
54
54
  export { BrandInlineSelect } from "./BrandInlineSelect";
55
+ export { SellerGroupedListingsView } from "./SellerGroupedListingsView";
56
+ export { SellerGroupedListingsView as StoreGroupedListingsView } from "./SellerGroupedListingsView";
57
+ export { SellerAnalyticsAlertsView } from "./SellerAnalyticsAlertsView";
58
+ export { SellerAnalyticsAlertsView as StoreAnalyticsAlertsView } from "./SellerAnalyticsAlertsView";
59
+ export { SellerStoreCategoriesView } from "./SellerStoreCategoriesView";
60
+ export { SellerTemplatesView } from "./SellerTemplatesView";
61
+ export { SellerPayoutMethodsView } from "./SellerPayoutMethodsView";
62
+ export { SellerShippingConfigsView } from "./SellerShippingConfigsView";
63
+ export { SellerGoogleReviewsView } from "./SellerGoogleReviewsView";
64
+ export { SellerBundlesView } from "./SellerBundlesView";
65
+ export { SellerClassifiedView } from "./SellerClassifiedView";
66
+ export { SellerDigitalCodesView } from "./SellerDigitalCodesView";
67
+ export { SellerLiveView } from "./SellerLiveView";
@@ -0,0 +1,7 @@
1
+ export declare const INPUT_CLS = "w-full rounded border border-[var(--appkit-color-border)] bg-transparent px-2 py-1.5 text-sm";
2
+ export declare const FILTER_LABEL_CLS = "text-xs font-semibold text-[var(--appkit-color-text-muted)] uppercase tracking-wide";
3
+ export declare const CARD_BORDER = "border-[var(--appkit-color-border)]";
4
+ export declare const CARD_BORDER_ACTIVE = "border-[var(--appkit-color-primary)]";
5
+ export declare const CARD_GRID_CLS = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3";
6
+ export declare const CARD_LIST_CLS = "flex flex-col gap-2";
7
+ export declare const KIND_BADGE_VARIANT: Record<string, "default" | "primary" | "secondary" | "success" | "warning" | "danger">;
@@ -0,0 +1,14 @@
1
+ // S-STORE — shared style constants for SellerProductsView and its sub-components.
2
+ // Token-based — no raw hex / breakpoint literals.
3
+ export const INPUT_CLS = "w-full rounded border border-[var(--appkit-color-border)] bg-transparent px-2 py-1.5 text-sm";
4
+ export const FILTER_LABEL_CLS = "text-xs font-semibold text-[var(--appkit-color-text-muted)] uppercase tracking-wide";
5
+ export const CARD_BORDER = "border-[var(--appkit-color-border)]";
6
+ export const CARD_BORDER_ACTIVE = "border-[var(--appkit-color-primary)]";
7
+ export const CARD_GRID_CLS = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3";
8
+ export const CARD_LIST_CLS = "flex flex-col gap-2";
9
+ export const KIND_BADGE_VARIANT = {
10
+ auction: "warning",
11
+ "pre-order": "secondary",
12
+ "prize-draw": "primary",
13
+ standard: "default",
14
+ };
@@ -26,6 +26,12 @@ export interface FormShellProps {
26
26
  * A draft banner and "← Back to Edit" button are injected automatically.
27
27
  */
28
28
  previewSlot?: () => ReactNode;
29
+ /**
30
+ * S-STORE-3-D — when true AND previewSlot is provided, desktop renders a
31
+ * 60/40 split with form on the left and live preview on the right.
32
+ * On mobile the preview reverts to a toggleable modal-style view.
33
+ */
34
+ splitPreview?: boolean;
29
35
  children: ReactNode;
30
36
  }
31
37
  export interface UseFormShellResult {
@@ -34,4 +40,4 @@ export interface UseFormShellResult {
34
40
  markClean: () => void;
35
41
  }
36
42
  export declare function useFormShell(): UseFormShellResult;
37
- export declare function FormShell({ isOpen, onClose, title, breadcrumb, isDirty, isLoading, sections, onSaveDraft, onPublish, saveLabel, publishLabel, renderBottomBar, previewSlot, children, }: FormShellProps): import("react/jsx-runtime").JSX.Element | null;
43
+ export declare function FormShell({ isOpen, onClose, title, breadcrumb, isDirty, isLoading, sections, onSaveDraft, onPublish, saveLabel, publishLabel, renderBottomBar, previewSlot, splitPreview, children, }: FormShellProps): import("react/jsx-runtime").JSX.Element | null;
@@ -15,7 +15,7 @@ export function useFormShell() {
15
15
  };
16
16
  }
17
17
  const FOCUSABLE = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
18
- export function FormShell({ isOpen, onClose, title, breadcrumb, isDirty = false, isLoading = false, sections, onSaveDraft, onPublish, saveLabel = FORM_ACTION_META[FORM_ACTION_ID.SAVE_DRAFT].label, publishLabel = FORM_ACTION_META[FORM_ACTION_ID.PUBLISH].label, renderBottomBar, previewSlot, children, }) {
18
+ export function FormShell({ isOpen, onClose, title, breadcrumb, isDirty = false, isLoading = false, sections, onSaveDraft, onPublish, saveLabel = FORM_ACTION_META[FORM_ACTION_ID.SAVE_DRAFT].label, publishLabel = FORM_ACTION_META[FORM_ACTION_ID.PUBLISH].label, renderBottomBar, previewSlot, splitPreview = false, children, }) {
19
19
  const panelRef = useRef(null);
20
20
  const bodyRef = useRef(null);
21
21
  const [showUnsaved, setShowUnsaved] = useState(false);
@@ -102,5 +102,8 @@ export function FormShell({ isOpen, onClose, title, breadcrumb, isDirty = false,
102
102
  };
103
103
  if (!isOpen)
104
104
  return null;
105
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/50 backdrop-blur-[2px]", style: { zIndex: "calc(var(--appkit-z-modal) - 1)" }, "aria-hidden": "true", onClick: attemptClose }), _jsxs("div", { ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 bottom-0 left-0 right-0 lg:left-[10%] lg:right-[10%] flex flex-col bg-[var(--appkit-color-surface)] shadow-2xl", style: { zIndex: "var(--appkit-z-modal)" }, children: [_jsxs("div", { className: "flex-shrink-0 sticky top-0 z-10 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-5 py-3 flex items-center gap-3", children: [previewMode ? (_jsxs("button", { type: "button", onClick: () => setPreviewMode(false), "aria-label": "Back to edit", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors flex-shrink-0 flex items-center gap-1.5 text-sm", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Back to Edit" })] })) : (_jsx("button", { type: "button", onClick: attemptClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors flex-shrink-0", children: _jsx(X, { className: "w-5 h-5" }) })), _jsxs("div", { className: "flex-1 min-w-0", children: [breadcrumb && (_jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] truncate mb-0.5", children: breadcrumb })), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] truncate", children: previewMode ? `Preview — ${title}` : title })] }), _jsx("div", { className: "flex items-center gap-2 flex-shrink-0", children: previewMode ? null : (_jsxs(_Fragment, { children: [previewSlot && (_jsxs("button", { type: "button", onClick: () => setPreviewMode(true), className: "flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors border border-[var(--appkit-color-border)]", children: [_jsx(Eye, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Preview" })] })), onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsx(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: publishLabel }))] })) })] }), _jsxs("div", { className: "flex flex-1 overflow-hidden", children: [sections && sections.length > 0 && !previewMode && (_jsx("nav", { "aria-label": "Form sections", className: "hidden lg:flex flex-col flex-shrink-0 w-48 border-r border-[var(--appkit-color-border)] py-4 px-3 gap-1 overflow-y-auto", children: sections.map((sec) => (_jsx("button", { type: "button", onClick: () => scrollToSection(sec.id), className: "text-left text-sm px-3 py-2 rounded-lg text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: sec.label }, sec.id))) })), sections && sections.length > 0 && !previewMode && (_jsx("div", { className: "lg:hidden fixed top-[var(--form-shell-topbar-h,57px)] left-0 right-0 z-10 flex overflow-x-auto gap-1 px-5 py-2 bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]", children: sections.map((sec) => (_jsx("button", { type: "button", onClick: () => scrollToSection(sec.id), className: "flex-shrink-0 text-xs px-3 py-1.5 rounded-full border border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] transition-colors whitespace-nowrap", children: sec.label }, sec.id))) })), _jsx("div", { ref: bodyRef, className: classNames("flex-1 overflow-y-auto", sections && sections.length > 0 && !previewMode ? "pt-0 lg:pt-0" : ""), children: previewMode && previewSlot ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sticky top-0 z-10 flex items-center gap-2 bg-[var(--appkit-color-warning-surface)] border-b border-[var(--appkit-color-warning)] px-4 py-2 text-sm text-[var(--appkit-color-warning-text,var(--appkit-color-warning))]", children: [_jsx(Eye, { className: "w-4 h-4 flex-shrink-0" }), _jsx("span", { children: "Preview \u2014 not visible to buyers until published" })] }), _jsx("div", { className: "py-4", children: previewSlot() })] })) : (_jsx("div", { className: "max-w-3xl mx-auto px-5 py-6 sm:px-6", children: children })) })] }), !previewMode && renderBottomBar ? (renderBottomBar()) : !previewMode && (onSaveDraft || onPublish) ? (_jsxs("div", { className: "flex-shrink-0 sticky bottom-0 z-10 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-5 py-3 flex items-center justify-between", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: attemptClose, disabled: isLoading, children: FORM_ACTION_META[FORM_ACTION_ID.DISCARD].label }), _jsxs("div", { className: "flex items-center gap-2", children: [onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsxs(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: [publishLabel, " \u2192"] }))] })] })) : null] }), showUnsaved && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/60", style: { zIndex: "calc(var(--appkit-z-modal) + 5)" }, onClick: () => setShowUnsaved(false) }), _jsxs("div", { className: "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] max-w-sm bg-[var(--appkit-color-surface)] rounded-xl shadow-2xl p-6", style: { zIndex: "calc(var(--appkit-z-modal) + 5)" }, children: [_jsxs("div", { className: "flex gap-3 mb-4", children: [_jsx("span", { className: "flex-shrink-0 w-10 h-10 rounded-full bg-[var(--appkit-color-warning-surface)] flex items-center justify-center", children: _jsx(AlertTriangle, { className: "w-5 h-5 text-[var(--appkit-color-warning)]" }) }), _jsxs(_Fragment, { children: [_jsx(Text, { className: "font-semibold text-[var(--appkit-color-text)]", children: "Unsaved changes" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mt-1", children: "You have unsaved changes. Leave without saving?" })] })] }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowUnsaved(false), children: "Stay" }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => { setShowUnsaved(false); onClose(); }, children: "Leave" })] })] })] }))] }));
105
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/50 backdrop-blur-[2px]", style: { zIndex: "calc(var(--appkit-z-modal) - 1)" }, "aria-hidden": "true", onClick: attemptClose }), _jsxs("div", { ref: panelRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 bottom-0 left-0 right-0 lg:left-[10%] lg:right-[10%] flex flex-col bg-[var(--appkit-color-surface)] shadow-2xl", style: { zIndex: "var(--appkit-z-modal)" }, children: [_jsxs("div", { className: "flex-shrink-0 sticky top-0 z-10 border-b border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-5 py-3 flex items-center gap-3", children: [previewMode ? (_jsxs("button", { type: "button", onClick: () => setPreviewMode(false), "aria-label": "Back to edit", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors flex-shrink-0 flex items-center gap-1.5 text-sm", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Back to Edit" })] })) : (_jsx("button", { type: "button", onClick: attemptClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors flex-shrink-0", children: _jsx(X, { className: "w-5 h-5" }) })), _jsxs("div", { className: "flex-1 min-w-0", children: [breadcrumb && (_jsx(Text, { className: "text-xs text-[var(--appkit-color-text-muted)] truncate mb-0.5", children: breadcrumb })), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] truncate", children: previewMode ? `Preview — ${title}` : title })] }), _jsx("div", { className: "flex items-center gap-2 flex-shrink-0", children: previewMode ? null : (_jsxs(_Fragment, { children: [previewSlot && (_jsxs("button", { type: "button", onClick: () => setPreviewMode(true), className: "flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors border border-[var(--appkit-color-border)]", children: [_jsx(Eye, { className: "w-4 h-4" }), _jsx("span", { className: "hidden sm:inline", children: "Preview" })] })), onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsx(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: publishLabel }))] })) })] }), _jsxs("div", { className: "flex flex-1 overflow-hidden", children: [sections && sections.length > 0 && !previewMode && (_jsx("nav", { "aria-label": "Form sections", className: "hidden lg:flex flex-col flex-shrink-0 w-48 border-r border-[var(--appkit-color-border)] py-4 px-3 gap-1 overflow-y-auto", children: sections.map((sec) => (_jsx("button", { type: "button", onClick: () => scrollToSection(sec.id), className: "text-left text-sm px-3 py-2 rounded-lg text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: sec.label }, sec.id))) })), sections && sections.length > 0 && !previewMode && (_jsx("div", { className: "lg:hidden fixed top-[var(--form-shell-topbar-h,57px)] left-0 right-0 z-10 flex overflow-x-auto gap-1 px-5 py-2 bg-[var(--appkit-color-surface)] border-b border-[var(--appkit-color-border)]", children: sections.map((sec) => (_jsx("button", { type: "button", onClick: () => scrollToSection(sec.id), className: "flex-shrink-0 text-xs px-3 py-1.5 rounded-full border border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:border-[var(--appkit-color-primary)] hover:text-[var(--appkit-color-primary)] transition-colors whitespace-nowrap", children: sec.label }, sec.id))) })), _jsx("div", { ref: bodyRef, className: classNames("flex-1 overflow-y-auto", sections && sections.length > 0 && !previewMode ? "pt-0 lg:pt-0" : ""), children: previewMode && previewSlot ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "sticky top-0 z-10 flex items-center gap-2 bg-[var(--appkit-color-warning-surface)] border-b border-[var(--appkit-color-warning)] px-4 py-2 text-sm text-[var(--appkit-color-warning-text,var(--appkit-color-warning))]", children: [_jsx(Eye, { className: "w-4 h-4 flex-shrink-0" }), _jsx("span", { children: "Preview \u2014 not visible to buyers until published" })] }), _jsx("div", { className: "py-4", children: previewSlot() })] })) : splitPreview && previewSlot ? (
106
+ // S-STORE-3-D — desktop 60/40 split: form left, preview right.
107
+ // Below `lg`, falls back to single-column form (preview-as-modal via existing toggle).
108
+ _jsxs("div", { className: "lg:grid lg:grid-cols-[3fr_2fr] lg:gap-6 lg:px-6 lg:py-6 lg:max-w-[1400px] lg:mx-auto", children: [_jsx("div", { className: "max-w-3xl mx-auto px-5 py-6 sm:px-6 lg:max-w-none lg:px-0 lg:py-0", children: children }), _jsxs("div", { className: "hidden lg:block sticky lg:top-4 lg:self-start lg:max-h-[calc(100vh-6rem)] overflow-y-auto border border-[var(--appkit-color-border)] rounded-lg bg-[var(--appkit-color-surface-raised)] p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-3 text-xs font-semibold uppercase tracking-wide text-[var(--appkit-color-text-muted)]", children: [_jsx(Eye, { className: "w-3.5 h-3.5" }), _jsx("span", { children: "Live preview" })] }), previewSlot()] })] })) : (_jsx("div", { className: "max-w-3xl mx-auto px-5 py-6 sm:px-6", children: children })) })] }), !previewMode && renderBottomBar ? (renderBottomBar()) : !previewMode && (onSaveDraft || onPublish) ? (_jsxs("div", { className: "flex-shrink-0 sticky bottom-0 z-10 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-5 py-3 flex items-center justify-between", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: attemptClose, disabled: isLoading, children: FORM_ACTION_META[FORM_ACTION_ID.DISCARD].label }), _jsxs("div", { className: "flex items-center gap-2", children: [onSaveDraft && (_jsx(Button, { variant: "outline", size: "sm", onClick: handleSaveDraft, disabled: isLoading || savingDraft || publishing, isLoading: savingDraft, children: saveLabel })), onPublish && (_jsxs(Button, { variant: "primary", size: "sm", onClick: handlePublish, disabled: isLoading || savingDraft || publishing, isLoading: publishing, children: [publishLabel, " \u2192"] }))] })] })) : null] }), showUnsaved && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 bg-black/60", style: { zIndex: "calc(var(--appkit-z-modal) + 5)" }, onClick: () => setShowUnsaved(false) }), _jsxs("div", { className: "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] max-w-sm bg-[var(--appkit-color-surface)] rounded-xl shadow-2xl p-6", style: { zIndex: "calc(var(--appkit-z-modal) + 5)" }, children: [_jsxs("div", { className: "flex gap-3 mb-4", children: [_jsx("span", { className: "flex-shrink-0 w-10 h-10 rounded-full bg-[var(--appkit-color-warning-surface)] flex items-center justify-center", children: _jsx(AlertTriangle, { className: "w-5 h-5 text-[var(--appkit-color-warning)]" }) }), _jsxs(_Fragment, { children: [_jsx(Text, { className: "font-semibold text-[var(--appkit-color-text)]", children: "Unsaved changes" }), _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)] mt-1", children: "You have unsaved changes. Leave without saving?" })] })] }), _jsxs("div", { className: "flex gap-2 justify-end", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setShowUnsaved(false), children: "Stay" }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => { setShowUnsaved(false); onClose(); }, children: "Leave" })] })] })] }))] }));
106
109
  }
@@ -0,0 +1,4 @@
1
+ export * from "./schemas/firestore";
2
+ export * from "./schemas/rbac";
3
+ export * from "./repository/store-extensions.repositories";
4
+ export * from "./repository/rbac.repositories";
@@ -0,0 +1,4 @@
1
+ export * from "./schemas/firestore";
2
+ export * from "./schemas/rbac";
3
+ export * from "./repository/store-extensions.repositories";
4
+ export * from "./repository/rbac.repositories";
@@ -0,0 +1,23 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { type AdminNotificationDocument, type CustomRoleDocument, type RoleOverrideDocument } from "../schemas/rbac";
3
+ export declare class RoleOverridesRepository extends BaseRepository<RoleOverrideDocument> {
4
+ constructor();
5
+ listForUser(userId: string): Promise<{
6
+ items: RoleOverrideDocument[];
7
+ }>;
8
+ }
9
+ export declare class CustomRolesRepository extends BaseRepository<CustomRoleDocument> {
10
+ constructor();
11
+ listActive(): Promise<{
12
+ items: CustomRoleDocument[];
13
+ }>;
14
+ }
15
+ export declare class AdminNotificationsRepository extends BaseRepository<AdminNotificationDocument> {
16
+ constructor();
17
+ listUnread(): Promise<{
18
+ items: AdminNotificationDocument[];
19
+ }>;
20
+ }
21
+ export declare const roleOverridesRepository: RoleOverridesRepository;
22
+ export declare const customRolesRepository: CustomRolesRepository;
23
+ export declare const adminNotificationsRepository: AdminNotificationsRepository;
@@ -0,0 +1,32 @@
1
+ import { BaseRepository } from "../../../providers/db-firebase";
2
+ import { ADMIN_NOTIFICATIONS_COLLECTION, CUSTOM_ROLES_COLLECTION, ROLE_OVERRIDES_COLLECTION, } from "../schemas/rbac";
3
+ export class RoleOverridesRepository extends BaseRepository {
4
+ constructor() {
5
+ super(ROLE_OVERRIDES_COLLECTION);
6
+ }
7
+ async listForUser(userId) {
8
+ const items = await this.findBy("userId", userId);
9
+ return { items };
10
+ }
11
+ }
12
+ export class CustomRolesRepository extends BaseRepository {
13
+ constructor() {
14
+ super(CUSTOM_ROLES_COLLECTION);
15
+ }
16
+ async listActive() {
17
+ const all = await this.findAll(50);
18
+ return { items: all.filter((d) => d.isActive !== false) };
19
+ }
20
+ }
21
+ export class AdminNotificationsRepository extends BaseRepository {
22
+ constructor() {
23
+ super(ADMIN_NOTIFICATIONS_COLLECTION);
24
+ }
25
+ async listUnread() {
26
+ const items = await this.findBy("isRead", false);
27
+ return { items };
28
+ }
29
+ }
30
+ export const roleOverridesRepository = new RoleOverridesRepository();
31
+ export const customRolesRepository = new CustomRolesRepository();
32
+ export const adminNotificationsRepository = new AdminNotificationsRepository();
@@ -0,0 +1,91 @@
1
+ /**
2
+ * S-STORE Extension Repositories
3
+ *
4
+ * 11 lightweight BaseRepository subclasses. None of these collections store
5
+ * PII directly so no `createWithId` override is required (Pattern #9). Uses
6
+ * the BaseRepository public API only: `findById`, `findBy`, `findAll`,
7
+ * `create`, `update`, `delete`.
8
+ */
9
+ import { BaseRepository } from "../../../providers/db-firebase";
10
+ import { type AnalyticsAlertDocument, type AnalyticsCardDocument, type ItemRequestDocument, type ListingTemplateDocument, type ModerationQueueDocument, type PayoutMethodDocument, type ReportDocument, type ShippingConfigDocument, type StoreCategoryDocument, type StoreGoogleConfigDocument, type StoreWhatsAppConfigDocument } from "../schemas/firestore";
11
+ export declare class PayoutMethodsRepository extends BaseRepository<PayoutMethodDocument> {
12
+ constructor();
13
+ listByStore(storeId: string): Promise<{
14
+ items: PayoutMethodDocument[];
15
+ }>;
16
+ }
17
+ export declare class ShippingConfigsRepository extends BaseRepository<ShippingConfigDocument> {
18
+ constructor();
19
+ listByStore(storeId: string): Promise<{
20
+ items: ShippingConfigDocument[];
21
+ }>;
22
+ }
23
+ export declare class AnalyticsCardsRepository extends BaseRepository<AnalyticsCardDocument> {
24
+ constructor();
25
+ listForOwner(scope: "seller" | "admin", ownerId: string): Promise<{
26
+ items: AnalyticsCardDocument[];
27
+ }>;
28
+ }
29
+ export declare class AnalyticsAlertsRepository extends BaseRepository<AnalyticsAlertDocument> {
30
+ constructor();
31
+ listForOwner(scope: "seller" | "admin", ownerId: string): Promise<{
32
+ items: AnalyticsAlertDocument[];
33
+ }>;
34
+ }
35
+ export declare class StoreCategoriesRepository extends BaseRepository<StoreCategoryDocument> {
36
+ constructor();
37
+ listByStore(storeId: string): Promise<{
38
+ items: StoreCategoryDocument[];
39
+ }>;
40
+ }
41
+ export declare class ListingTemplatesRepository extends BaseRepository<ListingTemplateDocument> {
42
+ constructor();
43
+ listByStore(storeId: string): Promise<{
44
+ items: ListingTemplateDocument[];
45
+ }>;
46
+ }
47
+ export declare class ModerationQueueRepository extends BaseRepository<ModerationQueueDocument> {
48
+ constructor();
49
+ listPending(): Promise<{
50
+ items: ModerationQueueDocument[];
51
+ }>;
52
+ }
53
+ export declare class ReportsRepository extends BaseRepository<ReportDocument> {
54
+ constructor();
55
+ listForEntity(entityType: string, entityId: string): Promise<{
56
+ items: ReportDocument[];
57
+ }>;
58
+ listPending(): Promise<{
59
+ items: ReportDocument[];
60
+ }>;
61
+ }
62
+ export declare class ItemRequestsRepository extends BaseRepository<ItemRequestDocument> {
63
+ constructor();
64
+ listOpen(opts?: {
65
+ limit?: number;
66
+ }): Promise<{
67
+ items: ItemRequestDocument[];
68
+ }>;
69
+ listByOwner(opUserId: string): Promise<{
70
+ items: ItemRequestDocument[];
71
+ }>;
72
+ }
73
+ export declare class StoreWhatsAppConfigRepository extends BaseRepository<StoreWhatsAppConfigDocument> {
74
+ constructor();
75
+ getByStore(storeId: string): Promise<StoreWhatsAppConfigDocument | null>;
76
+ }
77
+ export declare class StoreGoogleConfigRepository extends BaseRepository<StoreGoogleConfigDocument> {
78
+ constructor();
79
+ getByStore(storeId: string): Promise<StoreGoogleConfigDocument | null>;
80
+ }
81
+ export declare const payoutMethodsRepository: PayoutMethodsRepository;
82
+ export declare const shippingConfigsRepository: ShippingConfigsRepository;
83
+ export declare const analyticsCardsRepository: AnalyticsCardsRepository;
84
+ export declare const analyticsAlertsRepository: AnalyticsAlertsRepository;
85
+ export declare const storeCategoriesRepository: StoreCategoriesRepository;
86
+ export declare const listingTemplatesRepository: ListingTemplatesRepository;
87
+ export declare const moderationQueueRepository: ModerationQueueRepository;
88
+ export declare const reportsRepository: ReportsRepository;
89
+ export declare const itemRequestsRepository: ItemRequestsRepository;
90
+ export declare const storeWhatsAppConfigRepository: StoreWhatsAppConfigRepository;
91
+ export declare const storeGoogleConfigRepository: StoreGoogleConfigRepository;