@mohasinac/appkit 3.3.3 → 3.3.5

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 (97) hide show
  1. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +1 -1
  2. package/dist/_internal/client/features/lottery/LotteryDetailView.js +2 -1
  3. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +2 -1
  4. package/dist/_internal/server/features/auth/permissions.js +4 -3
  5. package/dist/_internal/server/features/cart/actions.js +1 -1
  6. package/dist/_internal/server/features/lottery/data.d.ts +2 -3
  7. package/dist/_internal/server/features/products/actions.js +1 -1
  8. package/dist/features/about/components/AboutView.js +1 -1
  9. package/dist/features/about/components/PolicyPageView.js +1 -1
  10. package/dist/features/about/components/TrackOrderView.js +1 -1
  11. package/dist/features/account/components/AddressesIndexListing.js +1 -1
  12. package/dist/features/admin/components/AdminBlogEditorView.js +1 -1
  13. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -1
  14. package/dist/features/admin/components/AdminCarouselView.js +1 -1
  15. package/dist/features/admin/components/AdminNavEditorView.js +2 -1
  16. package/dist/features/admin/components/AdminProductsView.js +2 -2
  17. package/dist/features/admin/components/AdminSectionsView.js +11 -7
  18. package/dist/features/admin/components/AdminSiteSettingsView.js +2 -1
  19. package/dist/features/admin/components/DrawerFormFooter.js +2 -2
  20. package/dist/features/admin/components/QuickEditMenu.js +1 -1
  21. package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +1 -1
  22. package/dist/features/admin/components/sections/adminSectionsTypes.js +3 -2
  23. package/dist/features/admin/constants/filter-tabs.js +11 -0
  24. package/dist/features/auctions/columns/index.js +1 -1
  25. package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
  26. package/dist/features/auctions/schemas/index.d.ts +2 -2
  27. package/dist/features/auth/hooks/useRBAC.js +6 -1
  28. package/dist/features/blog/columns/index.js +1 -1
  29. package/dist/features/cart/components/CartDrawer.js +2 -2
  30. package/dist/features/categories/components/BrandDetailPageView.js +1 -1
  31. package/dist/features/categories/components/BreadcrumbTrail.js +2 -1
  32. package/dist/features/categories/schemas/firestore.d.ts +1 -1
  33. package/dist/features/contact/email.js +0 -1
  34. package/dist/features/events/components/EventBanner.js +2 -1
  35. package/dist/features/events/components/EventLeaderboard.js +1 -1
  36. package/dist/features/faq/components/FAQCategorySidebar.js +2 -1
  37. package/dist/features/forms/Slider.js +5 -2
  38. package/dist/features/forms/Toggle.js +4 -1
  39. package/dist/features/homepage/components/AdSlot.js +2 -2
  40. package/dist/features/homepage/components/BeforeAfterCard.js +1 -1
  41. package/dist/features/homepage/components/CTABannerSection.js +2 -1
  42. package/dist/features/homepage/components/CharacterHotspot.js +2 -1
  43. package/dist/features/homepage/components/FAQSection.js +1 -1
  44. package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
  45. package/dist/features/homepage/components/HowItWorksInfoView.js +1 -1
  46. package/dist/features/homepage/components/HowItWorksSection.js +1 -1
  47. package/dist/features/homepage/components/NewsletterBanner.js +1 -1
  48. package/dist/features/homepage/components/SecurityHighlightsSection.js +1 -1
  49. package/dist/features/homepage/components/SiteFeaturesSection.js +1 -1
  50. package/dist/features/homepage/components/TrustFeaturesSection.js +1 -1
  51. package/dist/features/homepage/components/TrustIndicatorsSection.js +1 -1
  52. package/dist/features/layout/BottomActions.js +29 -2
  53. package/dist/features/layout/BottomActionsContext.d.ts +36 -0
  54. package/dist/features/layout/BottomActionsContext.js +36 -0
  55. package/dist/features/orders/components/MarketplaceOrderCard.js +2 -2
  56. package/dist/features/orders/schemas/index.d.ts +2 -2
  57. package/dist/features/payments/schemas/index.d.ts +4 -4
  58. package/dist/features/products/components/BidHistory.js +1 -1
  59. package/dist/features/products/components/MarketplaceBundleCard.js +1 -1
  60. package/dist/features/products/components/ProductGalleryClient.js +1 -1
  61. package/dist/features/products/components/ShowGroupSection.js +1 -1
  62. package/dist/features/products/components/SublistingCarouselSection.js +1 -1
  63. package/dist/features/products/repository/products.repository.js +1 -1
  64. package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
  65. package/dist/features/promotions/repository/claimed-coupons.repository.js +1 -1
  66. package/dist/features/reviews/columns/index.js +1 -1
  67. package/dist/features/scams/components/ScamAwarenessModal.js +1 -1
  68. package/dist/features/search/components/Search.js +1 -1
  69. package/dist/features/search/schemas/index.d.ts +2 -2
  70. package/dist/features/seller/actions/seller-actions.d.ts +2 -2
  71. package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
  72. package/dist/features/seller/components/SellerGoogleReviewsView.js +1 -1
  73. package/dist/features/seller/components/SellerOrdersView.js +4 -1
  74. package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -1
  75. package/dist/features/seller/components/SellerProductsView.js +35 -31
  76. package/dist/features/seller/components/SellerReviewsView.js +1 -1
  77. package/dist/features/seller/schemas/index.d.ts +2 -2
  78. package/dist/features/shell/QuickFormDrawer.js +1 -1
  79. package/dist/features/stores/columns/index.js +1 -1
  80. package/dist/features/stores/components/StoresIndexListing.js +20 -15
  81. package/dist/features/wishlist/actions/wishlist-actions.js +1 -1
  82. package/dist/next/ErrorBoundary.js +2 -1
  83. package/dist/next/components/NotFoundView.js +17 -0
  84. package/dist/next/components/UnauthorizedView.js +13 -0
  85. package/dist/schemas/registry.d.ts +93 -94
  86. package/dist/schemas/webhooks/razorpay.d.ts +86 -86
  87. package/dist/seed/carousel-slides-seed-data.js +3 -2
  88. package/dist/seed/defaults/homepage-sections.js +2 -1
  89. package/dist/seed/factories/carousel.factory.js +2 -1
  90. package/dist/seed/homepage-sections-seed-data.js +4 -3
  91. package/dist/seed/notifications-seed-data.js +2 -1
  92. package/dist/seed/site-settings-seed-data.js +3 -2
  93. package/dist/seo/metadata.js +2 -1
  94. package/dist/ui/columns/column-renderers.js +1 -1
  95. package/dist/ui/components/Accordion.js +10 -6
  96. package/dist/ui/components/ListingLayout.js +6 -2
  97. package/package.json +1 -1
@@ -121,5 +121,5 @@ export function SellerPayoutMethodsView({ onCreateClick, onEditClick, onDelete,
121
121
  window.location.href = String(ROUTES.STORE.PAYOUT_METHODS_EDIT(id));
122
122
  }
123
123
  }, [onEditClick]);
124
- return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search payout methods...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { gap: "sm", size: "sm", onClick: handleCreate, children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx(Span, { children: "New Method" })] }) }), _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 })), isLoading ? (_jsx(Stack, { gap: "3", children: Array.from({ length: 3 }).map((_, i) => (_jsx(Div, { className: "h-20 animate-pulse", border: "subtle", surface: "muted", rounded: "xl" }, i))) })) : rows.length === 0 ? (_jsxs(Div, { className: "text-center", padding: "y-4xl", children: [_jsx(Text, { color: "faint", children: "No payout methods yet \u2014 add a UPI VPA or bank account to receive payouts" }), _jsx(Div, { className: "mt-4", children: _jsx(Button, { size: "sm", onClick: handleCreate, children: "Add payout method" }) })] })) : (_jsx(Stack, { gap: "3", children: rows.map((row) => (_jsx("div", { className: "rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-4 py-3", children: _jsxs(Row, { align: "start", justify: "between", gap: "md", children: [_jsx(Row, { className: "min-w-0", align: "start", gap: "3", children: _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsxs(Row, { align: "center", gap: "sm", wrap: true, children: [_jsx(Text, { size: "sm", weight: "medium", children: row.label }), _jsx(TypeBadge, { type: row.type }), row.isDefault && (_jsx(Span, { layout: "inline-flex", color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Default" })), _jsx(Badge, { variant: row.isActive ? "active" : "inactive", size: "xs", children: row.isActive ? "Active" : "Inactive" })] }), _jsx(Text, { className: "mt-1 font-mono", color: "muted", size: "xs", children: row.maskedIdentifier })] }) }), _jsxs(Row, { className: "shrink-0", align: "center", gap: "sm", children: [!row.isDefault && (_jsx(Button, { size: "sm", variant: "ghost", isLoading: settingDefaultId === row.id, onClick: () => handleSetDefault(row.id), children: "Set Default" })), _jsx(Button, { size: "sm", variant: "outline", onClick: () => handleEdit(row.id), children: "Edit" }), _jsx(Button, { size: "sm", variant: "danger", disabled: deletingId === row.id, onClick: () => setDeleteTargetId(row.id), children: "Delete" })] })] }) }, row.id))) }))] }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Payout Method", message: "Are you sure you want to delete this payout method? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
124
+ return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search payout methods...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { gap: "sm", size: "sm", onClick: handleCreate, children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx(Span, { children: "New Method" })] }) }), _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 })), isLoading ? (_jsx(Stack, { gap: "3", children: Array.from({ length: 3 }).map((_, i) => (_jsx(Div, { className: "h-20 animate-pulse", border: "subtle", surface: "muted", rounded: "xl" }, i))) })) : rows.length === 0 ? (_jsxs(Div, { className: "text-center", padding: "y-4xl", children: [_jsx(Text, { color: "faint", children: "No payout methods yet \u2014 add a UPI VPA or bank account to receive payouts" }), _jsx(Div, { className: "mt-4", children: _jsx(Button, { size: "sm", onClick: handleCreate, children: "Add payout method" }) })] })) : (_jsx(Stack, { gap: "3", children: rows.map((row) => (_jsx(Div, { className: "rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-4 py-3", children: _jsxs(Row, { align: "start", justify: "between", gap: "md", children: [_jsx(Row, { className: "min-w-0", align: "start", gap: "3", children: _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsxs(Row, { align: "center", gap: "sm", wrap: true, children: [_jsx(Text, { size: "sm", weight: "medium", children: row.label }), _jsx(TypeBadge, { type: row.type }), row.isDefault && (_jsx(Span, { layout: "inline-flex", color: "success", surface: "success-surface", size: "xs", weight: "medium", rounded: "full", padding: "pill-xs", children: "Default" })), _jsx(Badge, { variant: row.isActive ? "active" : "inactive", size: "xs", children: row.isActive ? "Active" : "Inactive" })] }), _jsx(Text, { className: "mt-1 font-mono", color: "muted", size: "xs", children: row.maskedIdentifier })] }) }), _jsxs(Row, { className: "shrink-0", align: "center", gap: "sm", children: [!row.isDefault && (_jsx(Button, { size: "sm", variant: "ghost", isLoading: settingDefaultId === row.id, onClick: () => handleSetDefault(row.id), children: "Set Default" })), _jsx(Button, { size: "sm", variant: "outline", onClick: () => handleEdit(row.id), children: "Edit" }), _jsx(Button, { size: "sm", variant: "danger", disabled: deletingId === row.id, onClick: () => setDeleteTargetId(row.id), children: "Delete" })] })] }) }, row.id))) }))] }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Payout Method", message: "Are you sure you want to delete this payout method? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
125
125
  }
@@ -214,6 +214,41 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
214
214
  .filter((r) => !deletedIds.has(r.id))
215
215
  .map((r) => statusOverrides.has(r.id) ? { ...r, status: statusOverrides.get(r.id) } : r);
216
216
  const selection = useBulkSelection({ items: visibleRows, keyExtractor: (r) => r.id });
217
+ // handleBulkPrintLabels/handleSetLocation (useCallback) and useBottomActions
218
+ // must run unconditionally on every render — they were previously declared
219
+ // after the `hasChildren` early return below, which skips these hook calls
220
+ // in passthrough mode, violating the Rules of Hooks.
221
+ const handleBulkPrintLabels = useCallback(() => {
222
+ const ids = selection.selectedIds.join(",");
223
+ void dispatch({
224
+ type: "NAVIGATE",
225
+ href: `${String(ROUTES.STORE.INVENTORY_PRINT)}?type=product&ids=${ids}&autoprint=1`,
226
+ });
227
+ }, [selection.selectedIds, dispatch]);
228
+ const handleSetLocation = useCallback(async (loc) => {
229
+ try {
230
+ const res = await fetch(SELLER_ENDPOINTS.PRODUCTS_BULK_LOCATION, {
231
+ method: "PATCH",
232
+ headers: { "Content-Type": "application/json" },
233
+ body: JSON.stringify({ productIds: selection.selectedIds, physicalLocation: loc }),
234
+ });
235
+ if (!res.ok) {
236
+ const body = await res.json().catch(() => null);
237
+ throw new Error(body?.error ?? "Failed to update location");
238
+ }
239
+ showToast("Location updated.", "success");
240
+ setSetLocationOpen(false);
241
+ }
242
+ catch (err) {
243
+ void normalizeError(err);
244
+ showToast(err instanceof Error ? err.message : "Failed to update location.", "error");
245
+ }
246
+ }, [selection.selectedIds, showToast]);
247
+ const bulkActions = [
248
+ buildBulkAction(ACTIONS.STORE["print-labels"], handleBulkPrintLabels, { icon: _jsx(Printer, { className: "w-4 h-4" }) }),
249
+ buildBulkAction(ACTIONS.STORE["set-location"], () => setSetLocationOpen(true), { icon: _jsx(MapPin, { className: "w-4 h-4" }) }),
250
+ ];
251
+ useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
217
252
  if (hasChildren) {
218
253
  return (_jsx(ListingLayout, { portal: "seller", ...props, children: children }));
219
254
  }
@@ -262,37 +297,6 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
262
297
  setPublishingId(null);
263
298
  }
264
299
  };
265
- const handleBulkPrintLabels = useCallback(() => {
266
- const ids = selection.selectedIds.join(",");
267
- void dispatch({
268
- type: "NAVIGATE",
269
- href: `${String(ROUTES.STORE.INVENTORY_PRINT)}?type=product&ids=${ids}&autoprint=1`,
270
- });
271
- }, [selection.selectedIds, dispatch]);
272
- const handleSetLocation = useCallback(async (loc) => {
273
- try {
274
- const res = await fetch(SELLER_ENDPOINTS.PRODUCTS_BULK_LOCATION, {
275
- method: "PATCH",
276
- headers: { "Content-Type": "application/json" },
277
- body: JSON.stringify({ productIds: selection.selectedIds, physicalLocation: loc }),
278
- });
279
- if (!res.ok) {
280
- const body = await res.json().catch(() => null);
281
- throw new Error(body?.error ?? "Failed to update location");
282
- }
283
- showToast("Location updated.", "success");
284
- setSetLocationOpen(false);
285
- }
286
- catch (err) {
287
- void normalizeError(err);
288
- showToast(err instanceof Error ? err.message : "Failed to update location.", "error");
289
- }
290
- }, [selection.selectedIds, showToast]);
291
- const bulkActions = [
292
- buildBulkAction(ACTIONS.STORE["print-labels"], handleBulkPrintLabels, { icon: _jsx(Printer, { className: "w-4 h-4" }) }),
293
- buildBulkAction(ACTIONS.STORE["set-location"], () => setSetLocationOpen(true), { icon: _jsx(MapPin, { className: "w-4 h-4" }) }),
294
- ];
295
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
296
300
  return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products by name\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, toggles: [
297
301
  { label: "Show sold", active: showSold, onChange: (next) => table.set("showSold", next ? "true" : "") },
298
302
  ], extra: onCreateClick ? (_jsx(Button, { variant: "primary", size: "sm", onClick: onCreateClick, children: "+ New Listing" })) : null }), _jsx(TypeDropdown, { active: listingKind, onChange: handleKindChange }), totalPages > 1 && (_jsx(Row, { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] py-[0.375rem]", padding: "x-sm", justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx(Div, { paddingX: "x-sm-lg-md", className: "sticky top-[calc(var(--header-height,0px)+88px)] z-20 bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)]", padding: "y-xs", children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs(Div, { paddingX: "x-sm-lg-md", padding: "y-md", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), view !== "table" && (_jsx(SellerProductsCards, { view: view, rows: visibleRows, isLoading: isLoading, listingKind: listingKind, selectedIds: selection.selectedIdSet, toggle: selection.toggle, onEdit: handleEdit, onDuplicate: (row) => void handleDuplicate(row), onDelete: onDeleteProduct ? (row) => void handleDelete(row) : undefined })), view === "table" && (_jsx(DataTable, { columns: PRODUCT_COLUMNS, rows: visibleRows, isLoading: isLoading, emptyLabel: listingKind !== "all"
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
4
  import { useCallback, useEffect, useMemo, useState } from "react";
5
- import { Alert, Badge, BulkActionBar, Button, Checkbox, Div, Modal, Row, Select, SideDrawer, Span, Stack, Text, Textarea, } from "../../../ui";
5
+ import { Alert, Badge, BulkActionBar, Button, Checkbox, Div, Modal, Row, Select, SideDrawer, Span, Stack, Text, Textarea } from "../../../ui";
6
6
  import { StackedViewShell } from "../../../ui";
7
7
  import { useBottomActions } from "../../layout";
8
8
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
@@ -167,8 +167,8 @@ export declare const payoutRecordSchema: z.ZodObject<{
167
167
  createdAt: z.ZodString;
168
168
  updatedAt: z.ZodString;
169
169
  }, "strip", z.ZodTypeAny, {
170
- status: "pending" | "paid" | "failed" | "processing";
171
170
  currency: string;
171
+ status: "pending" | "paid" | "failed" | "processing";
172
172
  createdAt: string;
173
173
  id: string;
174
174
  updatedAt: string;
@@ -184,8 +184,8 @@ export declare const payoutRecordSchema: z.ZodObject<{
184
184
  sellerId: string;
185
185
  processedAt?: string | undefined;
186
186
  }, {
187
- status: "pending" | "paid" | "failed" | "processing";
188
187
  currency: string;
188
+ status: "pending" | "paid" | "failed" | "processing";
189
189
  createdAt: string;
190
190
  id: string;
191
191
  updatedAt: string;
@@ -121,7 +121,7 @@ export function QuickFormDrawer({ isOpen, onClose, title, fields, defaultValues,
121
121
  if (!isOpen)
122
122
  return null;
123
123
  const busy = submitting || isLoading;
124
- return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 backdrop-blur-[2px] [z-index:calc(var(--appkit-z-modal)+1)]", "aria-hidden": "true", onClick: handleClose }), _jsxs("div", { ref: drawerRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed inset-y-0 right-0 flex flex-col bg-[var(--appkit-color-surface)] shadow-2xl w-full lg:w-[40%] [z-index:calc(var(--appkit-z-modal)+2)]", children: [_jsxs(Row, { className: "flex-shrink-0 border-b border-[var(--appkit-color-border)]", padding: "md", align: "center", gap: "3", children: [_jsx(Text, { className: "flex-1 text-[var(--appkit-color-text)]", size: "base", weight: "semibold", children: title }), _jsx("button", { type: "button", onClick: handleClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: _jsx(X, { className: "w-5 h-5" }) })] }), _jsxs("form", { className: "flex-1 overflow-y-auto px-4 py-5 space-y-4", onSubmit: handleSubmit, noValidate: true, children: [fields.map((field) => {
124
+ return (_jsxs(_Fragment, { children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 backdrop-blur-[2px] [z-index:calc(var(--appkit-z-modal)+1)]", "aria-hidden": "true", onClick: handleClose }), _jsxs(Div, { ref: drawerRef, role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed inset-y-0 right-0 flex flex-col bg-[var(--appkit-color-surface)] shadow-2xl w-full lg:w-[40%] [z-index:calc(var(--appkit-z-modal)+2)]", children: [_jsxs(Row, { className: "flex-shrink-0 border-b border-[var(--appkit-color-border)]", padding: "md", align: "center", gap: "3", children: [_jsx(Text, { className: "flex-1 text-[var(--appkit-color-text)]", size: "base", weight: "semibold", children: title }), _jsx("button", { type: "button", onClick: handleClose, "aria-label": "Close", className: "rounded-lg p-1.5 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-border-subtle)] transition-colors", children: _jsx(X, { className: "w-5 h-5" }) })] }), _jsxs("form", { className: "flex-1 overflow-y-auto px-4 py-5 space-y-4", onSubmit: handleSubmit, noValidate: true, children: [fields.map((field) => {
125
125
  const value = values[field.name];
126
126
  if (field.type === "toggle") {
127
127
  return (_jsxs(Stack, { gap: "xs", children: [_jsx(Toggle, { checked: !!value, onChange: (checked) => set(field.name, checked), label: field.label }), field.helperText && (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: field.helperText }))] }, field.name));
@@ -1,4 +1,4 @@
1
- import { buildColumns, renderBoolean, renderNullable, renderRating, } from "../../../ui/columns";
1
+ import { buildColumns, renderBoolean, renderNullable, renderRating } from "../../../ui/columns";
2
2
  /**
3
3
  * Default admin column definitions for a store table.
4
4
  *
@@ -121,6 +121,26 @@ export function StoresIndexListing({ initialData }) {
121
121
  filters: filterParts.length > 0 ? filterParts.join(",") : undefined,
122
122
  }, { initialData });
123
123
  const selection = useBulkSelection({ items: stores, keyExtractor: (s) => s.id ?? s.storeSlug });
124
+ // useBottomActions must run unconditionally on every render, once per
125
+ // component — it was previously mis-placed inside the grid view's
126
+ // `stores.map(...)` callback below, which fired it once per store in the
127
+ // list (a severe Rules-of-Hooks violation) instead of once for the whole
128
+ // bulk-selection bar.
129
+ useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
130
+ {
131
+ id: ACTION_ID.COMPARE,
132
+ label: "Compare",
133
+ variant: "secondary",
134
+ onClick: () => { selection.clearSelection(); },
135
+ },
136
+ {
137
+ id: "visit",
138
+ label: "Visit Store",
139
+ variant: "primary",
140
+ disabled: selection.selectedCount !== 1,
141
+ onClick: () => { selection.clearSelection(); },
142
+ },
143
+ ] } } : {});
124
144
  return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search stores...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: STORE_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: stores.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
125
145
  {
126
146
  id: ACTION_ID.COMPARE,
@@ -140,21 +160,6 @@ export function StoresIndexListing({ initialData }) {
140
160
  return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
141
161
  }) })) : (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: stores.map((store) => {
142
162
  const storeKey = store.storeSlug ?? store.id;
143
- useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
144
- {
145
- id: ACTION_ID.COMPARE,
146
- label: "Compare",
147
- variant: "secondary",
148
- onClick: () => { selection.clearSelection(); },
149
- },
150
- {
151
- id: "visit",
152
- label: "Visit Store",
153
- variant: "primary",
154
- disabled: selection.selectedCount !== 1,
155
- onClick: () => { selection.clearSelection(); },
156
- },
157
- ] } } : {});
158
163
  return (_jsx(InteractiveStoreCard, { store: store, href: String(ROUTES.PUBLIC.STORE_DETAIL(storeKey)), selectable: selection.isSelecting, isSelected: selection.isSelected(store.id ?? store.storeSlug), onSelect: (id, sel) => { void sel; selection.toggle(id); } }, storeKey));
159
164
  }) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: pendingFilterCount, children: _jsx(StoreFilters, { table: pendingTable }) })] }));
160
165
  }
@@ -5,7 +5,7 @@
5
5
  * Auth, rate-limiting, and Next.js specifics are handled by the consumer.
6
6
  */
7
7
  import { ValidationError } from "../../../errors";
8
- import { wishlistRepository, } from "../repository/user-wishlist.repository";
8
+ import { wishlistRepository } from "../repository/user-wishlist.repository";
9
9
  import { productRepository } from "../../products/repository/products.repository";
10
10
  export { WishlistFullError } from "../repository/user-wishlist.repository";
11
11
  export async function addToWishlist(userSlug, productId, extras) {
@@ -2,9 +2,10 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Component } from "react";
4
4
  import { Button } from "../ui/components/Button";
5
+ import { Div } from "../ui/components/Div";
5
6
  import { Heading, Text } from "../ui/components/Typography";
6
7
  function DefaultFallback({ error, onReset, }) {
7
- return (_jsxs("div", { className: "flex min-h-[40vh] flex-col items-center justify-center gap-4 rounded-xl border border-zinc-200 bg-[var(--appkit-color-surface)] p-6 text-center border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", "data-section": "errorboundary-div-445", children: [_jsx(Heading, { level: 2, children: "Something went wrong" }), _jsx(Text, { variant: "secondary", children: "An unexpected error occurred. Please try again." }), process.env.NODE_ENV === "development" && error ? (_jsx("pre", { className: "max-w-full overflow-auto rounded-md bg-[var(--appkit-color-surface)] p-3 text-left text-xs bg-[var(--appkit-color-surface-elevated)]", children: error.message })) : null, _jsx(Button, { type: "button", onClick: onReset, children: "Try again" })] }));
8
+ return (_jsxs(Div, { className: "flex min-h-[40vh] flex-col items-center justify-center gap-4 rounded-xl border border-zinc-200 bg-[var(--appkit-color-surface)] p-6 text-center border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", "data-section": "errorboundary-div-445", children: [_jsx(Heading, { level: 2, children: "Something went wrong" }), _jsx(Text, { variant: "secondary", children: "An unexpected error occurred. Please try again." }), process.env.NODE_ENV === "development" && error ? (_jsx("pre", { className: "max-w-full overflow-auto rounded-md bg-[var(--appkit-color-surface)] p-3 text-left text-xs bg-[var(--appkit-color-surface-elevated)]", children: error.message })) : null, _jsx(Button, { type: "button", onClick: onReset, children: "Try again" })] }));
8
9
  }
9
10
  export class ErrorBoundary extends Component {
10
11
  constructor(props) {
@@ -1,4 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * NotFoundView — Next.js not-found.tsx template.
4
+ *
5
+ * Rendered when `notFound()` is called inside a route segment, or when a
6
+ * route is not matched.
7
+ * @see https://nextjs.org/docs/app/api-reference/file-conventions/not-found
8
+ *
9
+ * Usage in your app:
10
+ * // app/[locale]/not-found.tsx
11
+ * export { NotFoundView as default } from "@mohasinac/appkit/next";
12
+ *
13
+ * // Or with customisation:
14
+ * import { NotFoundView } from "@mohasinac/appkit/next";
15
+ * export default function NotFound() {
16
+ * return <NotFoundView heading="Page not found" homeHref="/en" />;
17
+ * }
18
+ */
2
19
  import { Heading, Text } from "../../ui/components/Typography";
3
20
  import { TextLink } from "../../ui/components/TextLink";
4
21
  import { DEFAULT_ROUTE_MAP } from "../routing/route-map";
@@ -1,4 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * UnauthorizedView — 401 page template.
4
+ *
5
+ * Render this when a user tries to access a resource they are not
6
+ * authenticated for. Wire up the `loginHref` prop to your sign-in route.
7
+ *
8
+ * Usage in your app:
9
+ * // app/[locale]/unauthorized/page.tsx
10
+ * import { UnauthorizedView } from "@mohasinac/appkit/next";
11
+ * export default function UnauthorizedPage() {
12
+ * return <UnauthorizedView loginHref="/en/auth/login" />;
13
+ * }
14
+ */
2
15
  import { Row } from "../../ui/components/Layout";
3
16
  import { Heading, Text } from "../../ui/components/Typography";
4
17
  import { TextLink } from "../../ui/components/TextLink";