@mohasinac/appkit 3.6.5 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +17 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
  3. package/dist/_internal/client/features/tour/TourProvider.d.ts +5 -2
  4. package/dist/_internal/client/features/tour/TourProvider.js +125 -12
  5. package/dist/_internal/client/theme/ThemeProvider.js +1 -1
  6. package/dist/_internal/server/features/faqs/og.js +5 -1
  7. package/dist/_internal/server/features/reviews/og.js +5 -1
  8. package/dist/_internal/server/features/seo/manifest.js +2 -1
  9. package/dist/_internal/server/features/seo/og.js +7 -1
  10. package/dist/_internal/server/features/tester/visibility.d.ts +13 -0
  11. package/dist/_internal/server/features/tester/visibility.js +28 -0
  12. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  13. package/dist/_internal/server/functions/scheduled.js +9 -1
  14. package/dist/_internal/server/jobs/core/jobRunners.js +5 -0
  15. package/dist/_internal/server/jobs/core/resetOtpVerification.d.ts +17 -0
  16. package/dist/_internal/server/jobs/core/resetOtpVerification.js +88 -0
  17. package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +6 -0
  18. package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +35 -0
  19. package/dist/_internal/server/jobs/handlers/_helpers.d.ts +6 -0
  20. package/dist/_internal/server/jobs/handlers/_helpers.js +12 -0
  21. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  22. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  23. package/dist/_internal/server/jobs/handlers/messages.d.ts +2 -0
  24. package/dist/_internal/server/jobs/handlers/messages.js +2 -0
  25. package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +2 -0
  26. package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +2 -0
  27. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  28. package/dist/_internal/shared/actions/action-registry.js +48 -0
  29. package/dist/_internal/shared/tokens/index.d.ts +12 -12
  30. package/dist/client.d.ts +15 -3
  31. package/dist/client.js +10 -2
  32. package/dist/constants/api-endpoints.d.ts +36 -12
  33. package/dist/constants/api-endpoints.js +16 -8
  34. package/dist/constants/field-names.d.ts +9 -0
  35. package/dist/constants/field-names.js +12 -0
  36. package/dist/constants/index.d.ts +1 -1
  37. package/dist/constants/index.js +1 -1
  38. package/dist/features/account/components/LinkedAccountsSection.d.ts +11 -0
  39. package/dist/features/account/components/LinkedAccountsSection.js +11 -0
  40. package/dist/features/account/components/UserSidebar.d.ts +3 -1
  41. package/dist/features/account/components/UserSidebar.js +9 -13
  42. package/dist/features/account/hooks/useCollapsedSections.d.ts +20 -0
  43. package/dist/features/account/hooks/useCollapsedSections.js +63 -0
  44. package/dist/features/account/hooks/useProfile.d.ts +8 -0
  45. package/dist/features/account/index.d.ts +1 -0
  46. package/dist/features/account/index.js +1 -0
  47. package/dist/features/admin/components/AdminAddressEditorView.js +2 -2
  48. package/dist/features/admin/components/AdminCarouselView.js +2 -2
  49. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  50. package/dist/features/admin/components/AdminProductEditorView.js +5 -2
  51. package/dist/features/admin/components/AdminSectionsView.js +3 -18
  52. package/dist/features/admin/components/AdminSidebar.js +8 -6
  53. package/dist/features/admin/components/AdminSiteSettingsView.js +133 -127
  54. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.d.ts +9 -0
  55. package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +129 -0
  56. package/dist/features/admin/components/AdminTesterChecklistView.d.ts +6 -0
  57. package/dist/features/admin/components/AdminTesterChecklistView.js +93 -0
  58. package/dist/features/admin/components/AdminUserEditorView.d.ts +3 -1
  59. package/dist/features/admin/components/AdminUserEditorView.js +7 -1
  60. package/dist/features/admin/components/AdminUsersView.js +1 -1
  61. package/dist/features/admin/components/DataListingView.js +2 -2
  62. package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +8 -0
  63. package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +41 -0
  64. package/dist/features/admin/components/index.d.ts +6 -0
  65. package/dist/features/admin/components/index.js +3 -0
  66. package/dist/features/admin/schemas/firestore.d.ts +0 -7
  67. package/dist/features/admin/schemas/firestore.js +6 -6
  68. package/dist/features/analytics/components/PageViewTracker.d.ts +12 -0
  69. package/dist/features/analytics/components/PageViewTracker.js +22 -0
  70. package/dist/features/analytics/repository/page-views.repository.d.ts +32 -0
  71. package/dist/features/analytics/repository/page-views.repository.js +43 -0
  72. package/dist/features/analytics/types.d.ts +6 -0
  73. package/dist/features/analytics/types.js +13 -0
  74. package/dist/features/auth/components/SocialAuthButtons.js +1 -1
  75. package/dist/features/auth/hooks/useAuth.d.ts +12 -0
  76. package/dist/features/auth/hooks/useAuth.js +109 -0
  77. package/dist/features/auth/permissions/constants.d.ts +1 -1
  78. package/dist/features/auth/schemas/firestore.d.ts +17 -1
  79. package/dist/features/auth/schemas/firestore.js +2 -0
  80. package/dist/features/blog/components/BlogIndexListing.js +2 -2
  81. package/dist/features/blog/schemas/firestore.d.ts +4 -0
  82. package/dist/features/cart/actions/cart-actions.js +20 -2
  83. package/dist/features/categories/components/CategoriesIndexListing.js +2 -2
  84. package/dist/features/categories/components/CategoryBundlesListing.js +2 -2
  85. package/dist/features/categories/components/CategoryDetailPageView.js +3 -1
  86. package/dist/features/categories/components/CategoryProductsListing.js +2 -2
  87. package/dist/features/categories/schemas/firestore.d.ts +4 -0
  88. package/dist/features/classified/components/ClassifiedIndexListing.js +2 -2
  89. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -2
  90. package/dist/features/email/primitives.d.ts +2 -2
  91. package/dist/features/email/primitives.js +2 -2
  92. package/dist/features/events/components/EventsIndexListing.js +2 -2
  93. package/dist/features/events/schemas/firestore.d.ts +4 -0
  94. package/dist/features/layout/BackToTop.d.ts +6 -1
  95. package/dist/features/layout/BackToTop.js +31 -6
  96. package/dist/features/live/components/LiveItemsIndexListing.js +2 -2
  97. package/dist/features/media/MediaVideo.js +5 -1
  98. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -2
  99. package/dist/features/products/components/AuctionsIndexListing.js +2 -2
  100. package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -2
  101. package/dist/features/products/components/ProductsIndexListing.js +2 -2
  102. package/dist/features/products/constants/action-defs.d.ts +2 -0
  103. package/dist/features/products/constants/action-defs.js +2 -0
  104. package/dist/features/products/schemas/firestore.d.ts +4 -0
  105. package/dist/features/promotions/components/CouponsIndexListing.js +2 -2
  106. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  107. package/dist/features/seller/actions/seller-actions.js +11 -6
  108. package/dist/features/seller/components/SellerAddressesView.js +2 -2
  109. package/dist/features/seller/components/SellerBidsView.js +2 -2
  110. package/dist/features/seller/components/SellerOrdersView.js +2 -2
  111. package/dist/features/seller/components/SellerProductsView.js +2 -2
  112. package/dist/features/seller/components/SellerSidebar.d.ts +3 -1
  113. package/dist/features/seller/components/SellerSidebar.js +9 -8
  114. package/dist/features/seller/components/SellerStoreCategoriesView.js +2 -2
  115. package/dist/features/seller/components/SellerTemplatesView.js +2 -2
  116. package/dist/features/shell/StepForm.js +4 -4
  117. package/dist/features/stores/actions/store-query-actions.d.ts +5 -4
  118. package/dist/features/stores/actions/store-query-actions.js +11 -8
  119. package/dist/features/stores/components/StoreAuctionsListing.js +2 -2
  120. package/dist/features/stores/components/StoreClassifiedsListing.js +2 -2
  121. package/dist/features/stores/components/StoreDigitalCodesListing.js +2 -2
  122. package/dist/features/stores/components/StoreLiveItemsListing.js +2 -2
  123. package/dist/features/stores/components/StorePreOrdersListing.js +2 -2
  124. package/dist/features/stores/components/StoreProductsListing.js +2 -2
  125. package/dist/features/stores/components/StoreReviewsListing.js +2 -2
  126. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  127. package/dist/features/stores/schemas/firestore.d.ts +4 -0
  128. package/dist/features/tester/actions/checklist-item-actions.d.ts +69 -0
  129. package/dist/features/tester/actions/checklist-item-actions.js +42 -0
  130. package/dist/features/tester/actions/index.d.ts +1 -0
  131. package/dist/features/tester/actions/index.js +1 -0
  132. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.d.ts +2 -0
  133. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +22 -0
  134. package/dist/features/tester/components/AdminTesterFeedbackListView.d.ts +3 -0
  135. package/dist/features/tester/components/AdminTesterFeedbackListView.js +100 -0
  136. package/dist/features/tester/components/AdminTesterFeedbackReportView.d.ts +2 -0
  137. package/dist/features/tester/components/AdminTesterFeedbackReportView.js +30 -0
  138. package/dist/features/tester/components/AdminTesterFeedbackView.d.ts +2 -0
  139. package/dist/features/tester/components/AdminTesterFeedbackView.js +9 -0
  140. package/dist/features/tester/components/TesterChecklistStepRow.d.ts +18 -0
  141. package/dist/features/tester/components/TesterChecklistStepRow.js +29 -0
  142. package/dist/features/tester/components/TesterFeedbackChart.d.ts +14 -0
  143. package/dist/features/tester/components/TesterFeedbackChart.js +18 -0
  144. package/dist/features/tester/components/TesterHubView.d.ts +5 -0
  145. package/dist/features/tester/components/TesterHubView.js +71 -0
  146. package/dist/features/tester/components/index.d.ts +11 -0
  147. package/dist/features/tester/components/index.js +7 -0
  148. package/dist/features/tester/index.d.ts +2 -0
  149. package/dist/features/tester/index.js +2 -0
  150. package/dist/features/tester/repository/index.d.ts +2 -0
  151. package/dist/features/tester/repository/index.js +2 -0
  152. package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +13 -0
  153. package/dist/features/tester/repository/tester-checklist-item.repository.js +55 -0
  154. package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +42 -0
  155. package/dist/features/tester/repository/tester-checklist-response.repository.js +98 -0
  156. package/dist/features/tester/schemas/firestore.d.ts +85 -0
  157. package/dist/features/tester/schemas/firestore.js +93 -0
  158. package/dist/features/tester/schemas/index.d.ts +1 -0
  159. package/dist/features/tester/schemas/index.js +1 -0
  160. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +2 -0
  161. package/dist/features/tester/seed-data/blog-tester-seed-data.js +40 -0
  162. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +2 -0
  163. package/dist/features/tester/seed-data/categories-tester-seed-data.js +177 -0
  164. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +2 -0
  165. package/dist/features/tester/seed-data/events-tester-seed-data.js +50 -0
  166. package/dist/features/tester/seed-data/index.d.ts +6 -0
  167. package/dist/features/tester/seed-data/index.js +6 -0
  168. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +2 -0
  169. package/dist/features/tester/seed-data/products-tester-seed-data.js +151 -0
  170. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +2 -0
  171. package/dist/features/tester/seed-data/stores-tester-seed-data.js +40 -0
  172. package/dist/features/tester/seed-data/tester-checklist-seed-data.d.ts +2 -0
  173. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +248 -0
  174. package/dist/features/tester/seed-data/tester-ttl.d.ts +4 -0
  175. package/dist/features/tester/seed-data/tester-ttl.js +6 -0
  176. package/dist/features/tester/server.d.ts +9 -0
  177. package/dist/features/tester/server.js +8 -0
  178. package/dist/index.d.ts +28 -2
  179. package/dist/index.js +51 -1
  180. package/dist/next/api/api-response.js +5 -1
  181. package/dist/next/api/routeHandler.js +10 -1
  182. package/dist/next/middleware/chain.js +1 -1
  183. package/dist/next/routing/route-map.d.ts +6 -7
  184. package/dist/next/routing/route-map.js +3 -7
  185. package/dist/providers/payment-manual/index.d.ts +2 -2
  186. package/dist/providers/payment-manual/index.js +2 -2
  187. package/dist/react/contexts/SessionContext.d.ts +4 -0
  188. package/dist/react/contexts/SessionContext.js +4 -0
  189. package/dist/repositories/index.d.ts +4 -0
  190. package/dist/repositories/index.js +3 -0
  191. package/dist/security/pii-encrypt.js +12 -2
  192. package/dist/seed/addresses-seed-data.js +1 -1
  193. package/dist/seed/bids-seed-data.js +1 -1
  194. package/dist/seed/blog-posts-seed-data.js +1 -1
  195. package/dist/seed/carousel-slides-seed-data.js +22 -16
  196. package/dist/seed/carousels-seed-data.js +1 -1
  197. package/dist/seed/cart-seed-data.js +1 -1
  198. package/dist/seed/categories-seed-data.js +1 -1
  199. package/dist/seed/claimed-coupons-seed-data.js +1 -1
  200. package/dist/seed/conversations-seed-data.js +1 -1
  201. package/dist/seed/coupon-usage-seed-data.js +1 -1
  202. package/dist/seed/coupons-seed-data.js +1 -1
  203. package/dist/seed/events-seed-data.js +1 -1
  204. package/dist/seed/faq-seed-data.js +119 -3
  205. package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
  206. package/dist/seed/grouped-listings-seed-data.js +1 -1
  207. package/dist/seed/history-seed-data.js +1 -1
  208. package/dist/seed/homepage-sections-seed-data.js +1 -1
  209. package/dist/seed/index.d.ts +2 -2
  210. package/dist/seed/index.js +7 -3
  211. package/dist/seed/lottery-entries-seed-data.js +1 -1
  212. package/dist/seed/manifest.d.ts +4 -3
  213. package/dist/seed/manifest.js +13 -6
  214. package/dist/seed/notifications-seed-data.js +1 -1
  215. package/dist/seed/offers-seed-data.js +1 -1
  216. package/dist/seed/orders-seed-data.js +1 -1
  217. package/dist/seed/payouts-seed-data.js +1 -1
  218. package/dist/seed/products-art-seed-data.js +1 -1
  219. package/dist/seed/products-auctions-seed-data.js +1 -1
  220. package/dist/seed/products-classifieds-seed-data.js +1 -1
  221. package/dist/seed/products-digital-codes-seed-data.js +1 -1
  222. package/dist/seed/products-live-items-seed-data.js +1 -1
  223. package/dist/seed/products-preorders-seed-data.d.ts +2 -0
  224. package/dist/seed/products-preorders-seed-data.js +1 -1
  225. package/dist/seed/products-prize-draws-seed-data.js +1 -1
  226. package/dist/seed/products-standard-seed-data.js +1 -1
  227. package/dist/seed/products-stickers-seed-data.js +1 -1
  228. package/dist/seed/reviews-seed-data.js +1 -1
  229. package/dist/seed/scammers-seed-data.js +1 -1
  230. package/dist/seed/sessions-seed-data.js +1 -1
  231. package/dist/seed/site-settings-seed-data.js +22 -16
  232. package/dist/seed/store-addresses-seed-data.js +1 -1
  233. package/dist/seed/store-extensions-seed-data.js +1 -1
  234. package/dist/seed/stores-seed-data.js +1 -1
  235. package/dist/seed/support-tickets-seed-data.js +1 -1
  236. package/dist/seed/tester-checklist-seed-data.d.ts +2 -0
  237. package/dist/seed/tester-checklist-seed-data.js +248 -0
  238. package/dist/seed/users-seed-data.js +1 -1
  239. package/dist/seed/wishlists-seed-data.js +1 -1
  240. package/dist/server.d.ts +7 -2
  241. package/dist/server.js +19 -12
  242. package/dist/styles.css +163 -147
  243. package/dist/tailwind-utilities.css +1 -1
  244. package/dist/tokens/color-pairs.d.ts +46 -0
  245. package/dist/tokens/color-pairs.js +93 -0
  246. package/dist/tokens/index.d.ts +60 -60
  247. package/dist/tokens/index.js +30 -30
  248. package/dist/tokens/themes/cobalt-night.d.ts +3 -2
  249. package/dist/tokens/themes/cobalt-night.js +3 -2
  250. package/dist/tokens/themes/default-dark.d.ts +4 -1
  251. package/dist/tokens/themes/default-dark.js +44 -41
  252. package/dist/tokens/themes/default-light.d.ts +5 -1
  253. package/dist/tokens/themes/default-light.js +39 -35
  254. package/dist/tokens/themes/sunset.js +4 -4
  255. package/dist/tokens/tokens.css +110 -104
  256. package/dist/ui/components/CollapsibleSection.d.ts +15 -0
  257. package/dist/ui/components/CollapsibleSection.js +13 -0
  258. package/dist/ui/components/ListingToolbar.js +2 -1
  259. package/dist/ui/components/Semantic.style.css +3 -3
  260. package/dist/ui/components/SideDrawer.style.css +19 -19
  261. package/dist/ui/components/Slider.js +1 -1
  262. package/dist/ui/components/StickyToolbar.d.ts +36 -7
  263. package/dist/ui/components/StickyToolbar.js +63 -5
  264. package/dist/ui/components/Tabs.js +17 -1
  265. package/dist/ui/components/Tabs.style.css +10 -0
  266. package/dist/ui/components/Typography.js +7 -0
  267. package/dist/ui/components/surface-tokens.d.ts +0 -7
  268. package/dist/ui/components/surface-tokens.js +7 -0
  269. package/dist/ui/index.d.ts +6 -0
  270. package/dist/ui/index.js +3 -0
  271. package/dist/ui/rich-text/RichText.style.css +20 -20
  272. package/dist/utils/id-generators.d.ts +13 -0
  273. package/dist/utils/id-generators.js +17 -0
  274. package/package.json +1 -1
  275. package/scripts/seed-cli-loader.mjs +15 -0
  276. package/scripts/seed-cli.mjs +71 -17
@@ -0,0 +1,20 @@
1
+ export interface UseCollapsedSectionsOptions {
2
+ /** Every section ID this page manages, in display order. IDs must be
3
+ * globally unique across pages (prefix with a page key, e.g.
4
+ * "admin-dashboard:alerts") — uiPreferences.collapsedSections is a single
5
+ * flat list shared by the whole user document, not namespaced server-side.
6
+ * A section ID absent from the saved list defaults to collapsed, which is
7
+ * also the state for a user who has never touched this page. */
8
+ sectionIds: string[];
9
+ }
10
+ export interface UseCollapsedSectionsResult {
11
+ isCollapsed: (id: string) => boolean;
12
+ /** Accordion within this hook's own sectionIds — expanding one collapses
13
+ * every other section this call manages; collapsing the open one leaves
14
+ * everything collapsed. */
15
+ toggle: (id: string) => void;
16
+ }
17
+ /** Persisted, per-user, accordion-scoped collapse state for a page's
18
+ * dashboard sections. Debounces writes to PATCH /api/user/profile so rapid
19
+ * toggling doesn't fire a request per click. */
20
+ export declare function useCollapsedSections({ sectionIds, }: UseCollapsedSectionsOptions): UseCollapsedSectionsResult;
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useCurrentProfile, useUpdateCurrentProfile } from "./useProfile";
4
+ const PERSIST_DEBOUNCE_MS = 600;
5
+ /** Persisted, per-user, accordion-scoped collapse state for a page's
6
+ * dashboard sections. Debounces writes to PATCH /api/user/profile so rapid
7
+ * toggling doesn't fire a request per click. */
8
+ export function useCollapsedSections({ sectionIds, }) {
9
+ const { data: profile } = useCurrentProfile();
10
+ const updateProfile = useUpdateCurrentProfile();
11
+ // Full server-side list, including other pages' entries this hook must
12
+ // never clobber when it persists its own page's next state.
13
+ const savedFullListRef = useRef([]);
14
+ const [localCollapsed, setLocalCollapsed] = useState(null);
15
+ const debounceRef = useRef(null);
16
+ const sectionIdsRef = useRef(sectionIds);
17
+ sectionIdsRef.current = sectionIds;
18
+ useEffect(() => {
19
+ if (!profile)
20
+ return;
21
+ const serverList = profile.uiPreferences?.collapsedSections;
22
+ savedFullListRef.current = serverList ?? [];
23
+ setLocalCollapsed((prev) => {
24
+ if (prev !== null)
25
+ return prev; // local interaction already in progress — don't clobber it
26
+ const known = new Set(serverList ?? []);
27
+ const initial = new Set();
28
+ for (const id of sectionIdsRef.current) {
29
+ // Never-set user (serverList undefined) → default every section collapsed.
30
+ if (serverList === undefined || known.has(id))
31
+ initial.add(id);
32
+ }
33
+ return initial;
34
+ });
35
+ }, [profile]);
36
+ useEffect(() => () => {
37
+ if (debounceRef.current)
38
+ clearTimeout(debounceRef.current);
39
+ }, []);
40
+ const persist = useCallback((nextForThisPage) => {
41
+ if (debounceRef.current)
42
+ clearTimeout(debounceRef.current);
43
+ debounceRef.current = setTimeout(() => {
44
+ const ids = sectionIdsRef.current;
45
+ const others = savedFullListRef.current.filter((id) => !ids.includes(id));
46
+ const merged = [...others, ...nextForThisPage];
47
+ savedFullListRef.current = merged;
48
+ updateProfile.mutate({ uiPreferences: { collapsedSections: merged } });
49
+ }, PERSIST_DEBOUNCE_MS);
50
+ }, [updateProfile]);
51
+ const isCollapsed = useCallback((id) => localCollapsed?.has(id) ?? true, [localCollapsed]);
52
+ const toggle = useCallback((id) => {
53
+ setLocalCollapsed((prev) => {
54
+ const wasCollapsed = prev?.has(id) ?? true;
55
+ const next = wasCollapsed
56
+ ? new Set(sectionIdsRef.current.filter((s) => s !== id)) // opening id collapses every sibling
57
+ : new Set(sectionIdsRef.current); // closing the open one — back to all-collapsed
58
+ persist(next);
59
+ return next;
60
+ });
61
+ }, [persist]);
62
+ return { isCollapsed, toggle };
63
+ }
@@ -9,6 +9,11 @@ export interface UserProfile {
9
9
  emailVerified: boolean;
10
10
  createdAt: Date;
11
11
  updatedAt: Date;
12
+ googleLinked?: boolean;
13
+ googleLinkedEmail?: string | null;
14
+ uiPreferences?: {
15
+ collapsedSections?: string[];
16
+ };
12
17
  }
13
18
  export interface UpdateCurrentProfileInput {
14
19
  displayName?: string;
@@ -24,6 +29,9 @@ export interface UpdateCurrentProfileInput {
24
29
  };
25
30
  bio?: string;
26
31
  profileIsPublic?: boolean;
32
+ uiPreferences?: {
33
+ collapsedSections?: string[];
34
+ };
27
35
  }
28
36
  export declare function useCurrentProfile(options?: {
29
37
  enabled?: boolean;
@@ -7,6 +7,7 @@ export * from "./hooks/useAddressForm";
7
7
  export * from "./hooks/useAddressSelector";
8
8
  export * from "./hooks/useNotifications";
9
9
  export * from "./hooks/useProfileStats";
10
+ export * from "./hooks/useCollapsedSections";
10
11
  export { useCurrentProfile as useProfile, useUpdateCurrentProfile as useUpdateProfile, } from "./hooks/useProfile";
11
12
  export * from "./hooks/usePublicProfile";
12
13
  export * from "./constants/addresses";
@@ -7,6 +7,7 @@ export * from "./hooks/useAddressForm";
7
7
  export * from "./hooks/useAddressSelector";
8
8
  export * from "./hooks/useNotifications";
9
9
  export * from "./hooks/useProfileStats";
10
+ export * from "./hooks/useCollapsedSections";
10
11
  export { useCurrentProfile as useProfile, useUpdateCurrentProfile as useUpdateProfile, } from "./hooks/useProfile";
11
12
  export * from "./hooks/usePublicProfile";
12
13
  export * from "./constants/addresses";
@@ -4,7 +4,7 @@ import { Row } from "@mohasinac/appkit/ui";
4
4
  import { useApiMutation } from "@mohasinac/appkit/client";
5
5
  import React from "react";
6
6
  import { useQuery } from "@tanstack/react-query";
7
- import { Button, Card, CardBody, Checkbox, ConfirmDeleteModal, Div, Form, Input, Select, Span, Stack, StackedViewShell, Text, Toggle, useToast } from "../../../ui";
7
+ import { Button, Card, CardBody, Checkbox, ConfirmDeleteModal, Div, Form, Input, Select, Span, Stack, StackedViewShell, Text, Toggle, useToast, Show, StickyToolbar } from "../../../ui";
8
8
  import { apiClient } from "../../../http";
9
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
@@ -114,6 +114,6 @@ export function AdminAddressEditorView({ addressId, onSaved, onDeleted, ...rest
114
114
  }, className: "accent-[var(--appkit-color-primary)]" }), _jsx(Span, { size: "sm", className: "text-[var(--appkit-color-text)]", transform: "capitalize", children: type })] }, type))) }), _jsx(Input, { label: form.ownerType === "user" ? "User ID" : "Store ID (slug)", value: form.ownerId, onChange: (e) => set("ownerId", e.target.value), required: true, placeholder: form.ownerType === "user" ? "user-mohsin-c" : "store-pokemon-palace", helperText: form.ownerType === "user"
115
115
  ? "Firebase Auth UID or user slug"
116
116
  : "Store slug (e.g. store-pokemon-palace)" })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(SectionHeading, { children: "Contact & Location" }), _jsxs(Stack, { gap: "md", children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Label", value: form.label, onChange: (e) => set("label", e.target.value), required: true, placeholder: "Home / Office / Pickup" }), _jsx(Input, { label: "Full Name", value: form.fullName, onChange: (e) => set("fullName", e.target.value), required: true, placeholder: "Recipient name (PII)" })] }), _jsx(Input, { label: "Phone", value: form.phone, onChange: (e) => set("phone", e.target.value), required: true, type: "tel", placeholder: "+91 9876543210 (PII)" }), _jsx(Input, { label: "Address Line 1", value: form.addressLine1, onChange: (e) => set("addressLine1", e.target.value), required: true, placeholder: "House/flat no., street (PII)" }), _jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Address Line 2", value: form.addressLine2 ?? "", onChange: (e) => set("addressLine2", e.target.value), placeholder: "Area, locality (optional)" }), _jsx(Input, { label: "Landmark", value: form.landmark ?? "", onChange: (e) => set("landmark", e.target.value), placeholder: "Near hospital, etc. (optional)" })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-3", children: [_jsx(Input, { label: "City", value: form.city, onChange: (e) => set("city", e.target.value), required: true, placeholder: "Mumbai" }), _jsx(Select, { label: "State", value: form.state, onValueChange: (val) => set("state", val), options: STATE_OPTIONS, placeholder: "Select state", required: true }), _jsx(Input, { label: "Postal Code", value: form.postalCode, onChange: (e) => set("postalCode", e.target.value), required: true, placeholder: "400001", maxLength: 6 })] }), _jsx(Input, { label: "Country", value: form.country, onChange: (e) => set("country", e.target.value), placeholder: "India" })] })] }), _jsxs(Card, { variant: "outlined", padding: "lg", children: [_jsx(SectionHeading, { children: "Flags" }), _jsx(Toggle, { label: "Set as default address", checked: form.isDefault, onChange: (v) => set("isDefault", v) })] }), _jsxs(Row, { gap: "3", className: "lg:hidden", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, children: isEdit ? ACTIONS.ADMIN["save-changes"].label : "Create address" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", isLoading: deleteMutation.isPending, onClick: () => setDeleteOpen(true), children: "Delete address" }))] })] }, "address-form"));
117
- const twoPanel = (_jsxs(Div, { layout: "grid", gap: "6", lgAlign: "start", className: "lg:grid-cols-[1fr_280px]", children: [_jsx(CardBody, { className: "min-w-0 space-y-6 p-[var(--appkit-space-0)]", children: formContent }), _jsx(Div, { className: "hidden lg:block lg:sticky lg:top-[var(--header-height,0px)]", children: actionSidebar })] }));
117
+ const twoPanel = (_jsxs(Div, { layout: "grid", gap: "6", lgAlign: "start", className: "lg:grid-cols-[1fr_280px]", children: [_jsx(CardBody, { className: "min-w-0 space-y-6 p-[var(--appkit-space-0)]", children: formContent }), _jsx(Show, { above: "lg", children: _jsx(StickyToolbar, { offset: "header", tone: "default", border: false, padding: "none", children: actionSidebar }) })] }));
118
118
  return (_jsxs(_Fragment, { children: [_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Address" : "New Address", sections: [twoPanel] }), _jsx(ConfirmDeleteModal, { isOpen: deleteOpen, onClose: () => setDeleteOpen(false), onConfirm: () => deleteMutation.mutate(), isDeleting: deleteMutation.isPending, title: "Delete this address?", message: "This address will be permanently removed. This action cannot be undone.", confirmText: "Delete address", variant: "danger" })] }));
119
119
  }
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useApiMutation } from "@mohasinac/appkit/client";
4
4
  import React, { useState, useCallback } from "react";
5
5
  import { useQueryClient } from "@tanstack/react-query";
6
- import { Badge, BulkActionBar, Button, ConfirmDeleteModal, Div, ListingFilterDrawer, ListingLayout, ListingToolbar, Pagination, Row, Span, Stack, Text } from "../../../ui";
6
+ import { Badge, BulkActionBar, Button, ConfirmDeleteModal, Div, ListingFilterDrawer, ListingLayout, ListingToolbar, Pagination, Row, Span, Stack, Text, StickyToolbar } from "../../../ui";
7
7
  import { useBottomActions } from "../../layout";
8
8
  import { MediaImage } from "../../media/MediaImage";
9
9
  import { ADMIN_ENDPOINTS, HOMEPAGE_ENDPOINTS } from "../../../constants/api-endpoints";
@@ -145,5 +145,5 @@ export function AdminCarouselView({ children, onBulkDelete, ...props }) {
145
145
  ] })),
146
146
  },
147
147
  ];
148
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(Row, { border: "bottom", className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 backdrop-blur-sm", surface: "default", padding: "toolbar", justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", getRowHref: (row) => `/admin/carousel/${row.id}/edit` })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium border transition-colors min-h-0 h-auto ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Carousel Slide", message: "Delete this carousel slide? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(deleteTargetId); setDeleteTargetId(null); }, onClose: () => setDeleteTargetId(null), isDeleting: deleteMutation.isPending }))] }));
148
+ return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", getRowHref: (row) => `/admin/carousel/${row.id}/edit` })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx(Button, { variant: "ghost", type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium border transition-colors min-h-0 h-auto ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] }) }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Carousel Slide", message: "Delete this carousel slide? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(deleteTargetId); setDeleteTargetId(null); }, onClose: () => setDeleteTargetId(null), isDeleting: deleteMutation.isPending }))] }));
149
149
  }
@@ -5,7 +5,7 @@ import { useApiMutation } from "@mohasinac/appkit/client";
5
5
  import React from "react";
6
6
  import { useQuery } from "@tanstack/react-query";
7
7
  import { z } from "zod";
8
- import { Button, Card, CardBody, ConfirmDeleteModal, Div, Form, Input, PaginatedSelect, Stack, StackedViewShell, Text, Toggle, useToast } from "../../../ui";
8
+ import { Button, Card, CardBody, ConfirmDeleteModal, Div, Form, Input, PaginatedSelect, Stack, StackedViewShell, Text, Toggle, useToast, Show, StickyToolbar } from "../../../ui";
9
9
  import { apiClient } from "../../../http";
10
10
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
11
  import { CategoryQuickCreateForm } from "./CategoryQuickCreateForm";
@@ -131,6 +131,6 @@ export function AdminCategoryEditorView({ categoryId, onSaved, onDeleted, embedd
131
131
  if (embedded) {
132
132
  return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
133
133
  }
134
- const twoPanel = (_jsxs(Div, { layout: "grid", gap: "6", lgAlign: "start", className: "lg:grid-cols-[1fr_280px]", children: [_jsx(CardBody, { className: "min-w-0 space-y-6 p-[var(--appkit-space-0)]", children: formContent }), _jsx(Div, { className: "hidden lg:block lg:sticky lg:top-[var(--header-height,0px)]", children: actionSidebar })] }));
134
+ const twoPanel = (_jsxs(Div, { layout: "grid", gap: "6", lgAlign: "start", className: "lg:grid-cols-[1fr_280px]", children: [_jsx(CardBody, { className: "min-w-0 space-y-6 p-[var(--appkit-space-0)]", children: formContent }), _jsx(Show, { above: "lg", children: _jsx(StickyToolbar, { offset: "header", tone: "default", border: false, padding: "none", children: actionSidebar }) })] }));
135
135
  return (_jsxs(_Fragment, { children: [_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Category" : "Create Category", sections: [twoPanel] }), _jsx(ConfirmDeleteModal, { isOpen: deleteOpen, onClose: () => setDeleteOpen(false), onConfirm: () => deleteMutation.mutate(), isDeleting: deleteMutation.isPending, title: "Delete this category?", message: "Products in this category will become uncategorized. This action cannot be undone.", confirmText: "Delete category", variant: "danger" })] }));
136
136
  }
@@ -1,10 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ // audit-form-mutation-hook-multi-ok: saveMutation is the single combined save
4
+ // across all tabs; deleteMutation is a separate destructive action, not a
5
+ // second per-tab save.
3
6
  import { Row } from "@mohasinac/appkit/ui";
4
7
  import { useApiMutation } from "@mohasinac/appkit/client";
5
8
  import React from "react";
6
9
  import { useQuery } from "@tanstack/react-query";
7
- import { Button, Card, CardBody, ConfirmDeleteModal, Div, PaginatedSelect, Form, Stack, StackedViewShell, Tabs, TabsContent, TabsList, TabsTrigger, Text, useToast, } from "../../../ui";
10
+ import { Button, Card, CardBody, ConfirmDeleteModal, Div, PaginatedSelect, Form, Stack, StackedViewShell, Tabs, TabsContent, TabsList, TabsTrigger, Text, useToast, Show, StickyToolbar, } from "../../../ui";
8
11
  import { apiClient } from "../../../http";
9
12
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
13
  import { ProductForm } from "../../products/components/ProductForm";
@@ -175,6 +178,6 @@ export function AdminProductEditorView({ productId, onSaved, onDeleted, embedded
175
178
  if (embedded) {
176
179
  return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
177
180
  }
178
- const twoPanel = (_jsxs(Div, { layout: "grid", gap: "6", lgAlign: "start", className: "lg:grid-cols-[1fr_280px]", children: [_jsx(CardBody, { className: "min-w-0 space-y-6 p-[var(--appkit-space-0)]", children: formContent }), _jsx(Div, { className: "hidden lg:block lg:sticky lg:top-[var(--header-height,0px)]", children: actionSidebar })] }));
181
+ const twoPanel = (_jsxs(Div, { layout: "grid", gap: "6", lgAlign: "start", className: "lg:grid-cols-[1fr_280px]", children: [_jsx(CardBody, { className: "min-w-0 space-y-6 p-[var(--appkit-space-0)]", children: formContent }), _jsx(Show, { above: "lg", children: _jsx(StickyToolbar, { offset: "header", tone: "default", border: false, padding: "none", children: actionSidebar }) })] }));
179
182
  return (_jsxs(_Fragment, { children: [_jsx(StackedViewShell, { portal: "admin", ...rest, title: isEdit ? "Edit Product" : "Create Product", sections: [twoPanel] }), _jsx(ConfirmDeleteModal, { isOpen: deleteOpen, onClose: () => setDeleteOpen(false), onConfirm: () => deleteMutation.mutate(), isDeleting: deleteMutation.isPending, title: "Delete this product?", message: "This product will be permanently removed. This action cannot be undone.", confirmText: "Delete product", variant: "danger" })] }));
180
183
  }
@@ -6,9 +6,9 @@ import React from "react";
6
6
  import { useQuery, useQueryClient } from "@tanstack/react-query";
7
7
  import { ProductInlineSelect } from "../../seller/components/ProductInlineSelect";
8
8
  import { CategoryInlineSelect } from "../../seller/components/CategoryInlineSelect";
9
- import { Button, Checkbox, ConfirmDeleteModal, Div, Form, FormActions, Input, Modal, Row, Select, Stack, Text, Textarea, useToast } from "../../../ui";
9
+ import { Button, Checkbox, Div, Form, FormActions, Input, Modal, Row, Select, Stack, Text, Textarea, useToast } from "../../../ui";
10
10
  import { apiClient } from "../../../http";
11
- import { ADMIN_ENDPOINTS, DEMO_ENDPOINTS } from "../../../constants";
11
+ import { ADMIN_ENDPOINTS } from "../../../constants";
12
12
  import { ROUTES } from "../../../next/routing/route-map";
13
13
  import { useAdminSectionsListing } from "../hooks/useAdminSectionsListing";
14
14
  import { DataTable } from "./DataTable";
@@ -29,7 +29,6 @@ export function AdminSectionsView({ children }) {
29
29
  const hasChildren = React.Children.count(children) > 0;
30
30
  const queryClient = useQueryClient();
31
31
  const [isModalOpen, setIsModalOpen] = React.useState(false);
32
- const [seedResetOpen, setSeedResetOpen] = React.useState(false);
33
32
  const [mode, setMode] = React.useState("create");
34
33
  const [selectedSectionId, setSelectedSectionId] = React.useState("");
35
34
  const [sectionType, setSectionType] = React.useState("products");
@@ -208,20 +207,6 @@ export function AdminSectionsView({ children }) {
208
207
  toast.showToast(error instanceof Error ? error.message : "Failed to save section.", "error");
209
208
  },
210
209
  });
211
- const resetSeed = useApiMutation({
212
- mutationFn: () => apiClient.post(DEMO_ENDPOINTS.SEED, {
213
- action: "load",
214
- collections: ["homepageSections"],
215
- }),
216
- onSuccess: async () => {
217
- await queryClient.invalidateQueries({ queryKey: ["admin", "sections", "listing"] });
218
- setSeedResetOpen(false);
219
- toast.showToast("Homepage sections seed data reloaded.", "success");
220
- },
221
- onError: () => {
222
- toast.showToast("Seed reset failed.", "error");
223
- },
224
- });
225
210
  const reorderSections = useApiMutation({
226
211
  mutationFn: async () => {
227
212
  await Promise.all(reorderDraft.map((item) => apiClient.patch(`${ADMIN_ENDPOINTS.SECTIONS}/${item.id}`, {
@@ -965,7 +950,7 @@ export function AdminSectionsView({ children }) {
965
950
  }));
966
951
  const hasReorderChanges = reorderDraft.some((item) => sectionOrderMap.get(item.id) !== item.order);
967
952
  const canUndoReorderChanges = reorderUndoStack.length > 0;
968
- return (_jsxs(_Fragment, { children: [_jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsx(Text, { size: "base", weight: "semibold", color: "primary", children: "Homepage Sections" }), _jsx(Text, { size: "sm", color: "muted", children: "Manage homepage sections and their display order" })] }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setSeedResetOpen(true), children: "Reset seed data" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setIsModalOpen(true), children: "Manage Sections" })] })] }), errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No sections found" })] }), _jsxs(Stack, { className: `mt-4 ${__P.p4}`, gap: "3", rounded: "xl", surface: "default", border: "default", children: [_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Reorder Sections" }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: reindexDraft, disabled: reorderSections.isPending || reorderDraft.length === 0, children: "Reindex 1..N" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: undoReorderChange, disabled: reorderSections.isPending || !canUndoReorderChanges, children: "Undo unsaved" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: resetToServerOrder, disabled: reorderSections.isPending || !hasReorderChanges, children: "Reset to server" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => reorderSections.mutate(), disabled: !hasReorderChanges || reorderSections.isPending, children: reorderSections.isPending ? "Saving order..." : "Save order" })] })] }), reorderDraft.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No sections to reorder." })) : (_jsx(Stack, { gap: "sm", children: reorderDraft.map((item, index) => (_jsxs(Div, { layout: "grid", gap: "2", align: "center", className: "grid-cols-[auto_1fr_auto_auto_auto]", rounded: "md", padding: "xs", border: "default", draggable: true, onDragStart: () => setDragIndex(index), onDragOver: (event) => event.preventDefault(), onDrop: () => handleReorderDrop(index), onDragEnd: () => setDragIndex(null), children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "\u2261" }), _jsxs(Text, { size: "sm", color: "primary", children: [item.type, " #", item.order] }), _jsx(Input, { type: "number", min: 1, max: reorderDraft.length, value: String(item.order), onChange: (event) => updateReorderItemOrder(index, Number(event.target.value) || 1), className: "w-24" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, -1), disabled: index === 0, children: "Up" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, 1), disabled: index === reorderDraft.length - 1, children: "Down" })] }, `reorder-${item.id}`))) }))] }), _jsx(ConfirmDeleteModal, { isOpen: seedResetOpen, onClose: () => setSeedResetOpen(false), onConfirm: () => resetSeed.mutate(), title: "Reset homepage sections seed data?", message: "This will reload the 19 default homepage sections from seed data. Any manual changes made in Firestore will be overwritten.", confirmText: "Reset seed", cancelText: "Cancel", isDeleting: resetSeed.isPending, variant: "danger" }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), title: "Manage Homepage Section", size: "lg", children: _jsxs(Form, { onSubmit: (event) => {
953
+ return (_jsxs(_Fragment, { children: [_jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsx(Text, { size: "base", weight: "semibold", color: "primary", children: "Homepage Sections" }), _jsx(Text, { size: "sm", color: "muted", children: "Manage homepage sections and their display order" })] }), _jsx(Row, { align: "center", gap: "sm", children: _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => setIsModalOpen(true), children: "Manage Sections" }) })] }), errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No sections found" })] }), _jsxs(Stack, { className: `mt-4 ${__P.p4}`, gap: "3", rounded: "xl", surface: "default", border: "default", children: [_jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Reorder Sections" }), _jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: reindexDraft, disabled: reorderSections.isPending || reorderDraft.length === 0, children: "Reindex 1..N" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: undoReorderChange, disabled: reorderSections.isPending || !canUndoReorderChanges, children: "Undo unsaved" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: resetToServerOrder, disabled: reorderSections.isPending || !hasReorderChanges, children: "Reset to server" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: () => reorderSections.mutate(), disabled: !hasReorderChanges || reorderSections.isPending, children: reorderSections.isPending ? "Saving order..." : "Save order" })] })] }), reorderDraft.length === 0 ? (_jsx(Text, { size: "sm", color: "muted", children: "No sections to reorder." })) : (_jsx(Stack, { gap: "sm", children: reorderDraft.map((item, index) => (_jsxs(Div, { layout: "grid", gap: "2", align: "center", className: "grid-cols-[auto_1fr_auto_auto_auto]", rounded: "md", padding: "xs", border: "default", draggable: true, onDragStart: () => setDragIndex(index), onDragOver: (event) => event.preventDefault(), onDrop: () => handleReorderDrop(index), onDragEnd: () => setDragIndex(null), children: [_jsx(Text, { size: "sm", weight: "semibold", color: "muted", children: "\u2261" }), _jsxs(Text, { size: "sm", color: "primary", children: [item.type, " #", item.order] }), _jsx(Input, { type: "number", min: 1, max: reorderDraft.length, value: String(item.order), onChange: (event) => updateReorderItemOrder(index, Number(event.target.value) || 1), className: "w-24" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, -1), disabled: index === 0, children: "Up" }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => moveReorderItem(index, 1), disabled: index === reorderDraft.length - 1, children: "Down" })] }, `reorder-${item.id}`))) }))] }), _jsx(Modal, { isOpen: isModalOpen, onClose: () => setIsModalOpen(false), title: "Manage Homepage Section", size: "lg", children: _jsxs(Form, { onSubmit: (event) => {
969
954
  event.preventDefault();
970
955
  saveSection.mutate();
971
956
  }, spacing: "md", children: [_jsx(Select, { label: "Mode", value: mode, onValueChange: (value) => setMode(value), options: [
@@ -19,13 +19,15 @@ function NavLink({ item, isActive, onClick }) {
19
19
  : "text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]/60 hover:text-zinc-800 hover:text-[var(--appkit-color-text-muted)]"}`, children: [item.icon && _jsx(Span, { size: "base", className: "shrink-0 opacity-60", children: item.icon }), _jsx(Span, { className: "truncate", children: item.label })] }));
20
20
  }
21
21
  function GroupsContent({ groups, activePath, onItemClick, }) {
22
- const [openGroups, setOpenGroups] = useState(() => Object.fromEntries(groups.map((g) => [
23
- g.title,
24
- g.defaultOpen ?? g.items.some((i) => activePath === i.href || activePath.startsWith(i.href + "/")),
25
- ])));
26
- const toggle = useCallback((title) => setOpenGroups((p) => ({ ...p, [title]: !p[title] })), []);
22
+ // Accordion only one group open at a time. Initial pick: the first group
23
+ // whose defaultOpen is true, or that contains the active path.
24
+ const [openGroup, setOpenGroup] = useState(() => {
25
+ const match = groups.find((g) => g.defaultOpen ?? g.items.some((i) => activePath === i.href || activePath.startsWith(i.href + "/")));
26
+ return match?.title ?? null;
27
+ });
28
+ const toggle = useCallback((title) => setOpenGroup((p) => (p === title ? null : title)), []);
27
29
  return (_jsx(Nav, { "aria-label": "Admin navigation", padding: "y-xs", children: groups.map((group) => {
28
- const isOpen = openGroups[group.title] ?? false;
30
+ const isOpen = openGroup === group.title;
29
31
  const hasActive = group.items.some((i) => activePath === i.href || activePath.startsWith(i.href + "/"));
30
32
  return (_jsxs(Div, { className: "mb-0.5", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => toggle(group.title), paddingX: "md", paddingY: "sm", weight: "semibold", rounded: "none", className: `w-full text-[0.6875rem] uppercase tracking-widest transition-colors ${hasActive && !isOpen
31
33
  ? "text-[var(--appkit-color-text-muted)]"