@mohasinac/appkit 2.7.35 → 2.7.37

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 (186) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +12 -0
  3. package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +49 -0
  4. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +7 -0
  5. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +32 -0
  6. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +28 -0
  7. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +33 -0
  8. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +12 -0
  9. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +30 -0
  10. package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +12 -0
  11. package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +102 -0
  12. package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +12 -0
  13. package/dist/_internal/client/features/seller/print-center/PrintGrid.js +13 -0
  14. package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +19 -0
  15. package/dist/_internal/client/features/seller/print-center/StoreCard.js +30 -0
  16. package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +11 -0
  17. package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +25 -0
  18. package/dist/_internal/client/features/seller/print-center/index.d.ts +11 -0
  19. package/dist/_internal/client/features/seller/print-center/index.js +10 -0
  20. package/dist/_internal/client/features/seller/print-center/types.d.ts +20 -0
  21. package/dist/_internal/client/features/seller/print-center/types.js +20 -0
  22. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +18 -0
  23. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +76 -0
  24. package/dist/_internal/server/features/auctions/og.d.ts +1 -0
  25. package/dist/_internal/server/features/auctions/og.js +2 -1
  26. package/dist/_internal/server/features/blog/og.d.ts +1 -0
  27. package/dist/_internal/server/features/blog/og.js +3 -2
  28. package/dist/_internal/server/features/brands/og.d.ts +1 -0
  29. package/dist/_internal/server/features/brands/og.js +2 -1
  30. package/dist/_internal/server/features/bundles/og.d.ts +1 -0
  31. package/dist/_internal/server/features/bundles/og.js +2 -1
  32. package/dist/_internal/server/features/categories/og.d.ts +1 -0
  33. package/dist/_internal/server/features/categories/og.js +2 -1
  34. package/dist/_internal/server/features/checkout/actions.js +13 -5
  35. package/dist/_internal/server/features/classified/og.d.ts +2 -0
  36. package/dist/_internal/server/features/classified/og.js +2 -1
  37. package/dist/_internal/server/features/digital-code/og.d.ts +2 -0
  38. package/dist/_internal/server/features/digital-code/og.js +2 -1
  39. package/dist/_internal/server/features/events/og.d.ts +1 -0
  40. package/dist/_internal/server/features/events/og.js +3 -1
  41. package/dist/_internal/server/features/live/og.d.ts +2 -0
  42. package/dist/_internal/server/features/live/og.js +2 -1
  43. package/dist/_internal/server/features/pre-orders/og.d.ts +1 -0
  44. package/dist/_internal/server/features/pre-orders/og.js +2 -1
  45. package/dist/_internal/server/features/products/og.d.ts +3 -1
  46. package/dist/_internal/server/features/products/og.js +2 -1
  47. package/dist/_internal/server/features/seo/index.d.ts +1 -1
  48. package/dist/_internal/server/features/seo/index.js +1 -1
  49. package/dist/_internal/server/features/seo/og.d.ts +8 -1
  50. package/dist/_internal/server/features/seo/og.js +28 -8
  51. package/dist/_internal/server/features/stores/og.d.ts +1 -0
  52. package/dist/_internal/server/features/stores/og.js +3 -2
  53. package/dist/_internal/server/features/sublisting-categories/og.d.ts +1 -0
  54. package/dist/_internal/server/features/sublisting-categories/og.js +2 -1
  55. package/dist/_internal/server/jobs/core/draftPrune.d.ts +2 -0
  56. package/dist/_internal/server/jobs/core/draftPrune.js +15 -0
  57. package/dist/_internal/server/jobs/core/onBidPlaced.js +1 -1
  58. package/dist/_internal/server/jobs/core/onScamReportCreate.js +18 -14
  59. package/dist/_internal/server/jobs/core/onScamReportRejected.js +10 -7
  60. package/dist/_internal/server/jobs/core/onScamReportVerified.js +10 -7
  61. package/dist/_internal/server/jobs/core/promotions.d.ts +1 -0
  62. package/dist/_internal/server/jobs/core/promotions.js +21 -6
  63. package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +18 -1
  64. package/dist/_internal/server/jobs/handlers/draftPrune.d.ts +2 -0
  65. package/dist/_internal/server/jobs/handlers/draftPrune.js +2 -0
  66. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  67. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  68. package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -1
  69. package/dist/_internal/shared/actions/action-registry.js +32 -0
  70. package/dist/_internal/shared/features/blog/schema.d.ts +8 -8
  71. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +10 -10
  72. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  73. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  74. package/dist/_internal/shared/features/promotions/schema.d.ts +6 -6
  75. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  76. package/dist/client.d.ts +13 -1
  77. package/dist/client.js +7 -1
  78. package/dist/configs/next.js +1 -0
  79. package/dist/constants/api-endpoints.d.ts +15 -0
  80. package/dist/constants/api-endpoints.js +5 -0
  81. package/dist/core/server-action.d.ts +4 -0
  82. package/dist/errors/error-codes.d.ts +18 -0
  83. package/dist/errors/error-codes.js +18 -0
  84. package/dist/errors/index.d.ts +1 -1
  85. package/dist/errors/index.js +1 -1
  86. package/dist/errors/messages.d.ts +1 -0
  87. package/dist/errors/messages.js +1 -0
  88. package/dist/features/account/components/UserSidebar.js +1 -1
  89. package/dist/features/account/schemas/index.d.ts +12 -12
  90. package/dist/features/admin/components/AdminBundlesView.js +20 -2
  91. package/dist/features/admin/components/AdminPayoutsView.js +2 -1
  92. package/dist/features/admin/components/AdminProductsView.js +13 -0
  93. package/dist/features/admin/components/AdminSidebar.js +1 -1
  94. package/dist/features/admin/components/AdminStoresView.js +43 -5
  95. package/dist/features/admin/components/AdminUsersView.js +53 -6
  96. package/dist/features/admin/schemas/firestore.d.ts +1 -1
  97. package/dist/features/auctions/actions/bid-actions.js +13 -6
  98. package/dist/features/auctions/components/PlaceBidFormClient.js +10 -2
  99. package/dist/features/auctions/repository/bid.repository.d.ts +7 -0
  100. package/dist/features/auctions/repository/bid.repository.js +16 -0
  101. package/dist/features/auctions/schemas/index.d.ts +18 -18
  102. package/dist/features/auth/schemas/index.d.ts +2 -2
  103. package/dist/features/before-after/schemas/index.d.ts +2 -2
  104. package/dist/features/blog/actions/blog-actions.d.ts +32 -32
  105. package/dist/features/blog/components/BlogPostForm.js +2 -1
  106. package/dist/features/blog/schemas/index.d.ts +38 -38
  107. package/dist/features/categories/components/CategoryForm.js +1 -0
  108. package/dist/features/categories/schemas/index.d.ts +10 -10
  109. package/dist/features/collections/schemas/index.d.ts +4 -4
  110. package/dist/features/consultation/schemas/index.d.ts +9 -9
  111. package/dist/features/corporate/schemas/index.d.ts +4 -4
  112. package/dist/features/events/schemas/index.d.ts +50 -50
  113. package/dist/features/faq/actions/faq-actions.d.ts +6 -6
  114. package/dist/features/faq/schemas/index.d.ts +8 -8
  115. package/dist/features/homepage/actions/homepage-section-actions.d.ts +4 -4
  116. package/dist/features/homepage/components/SectionCarousel.js +1 -8
  117. package/dist/features/layout/AppLayoutShell.js +1 -1
  118. package/dist/features/layout/AutoBreadcrumbs.js +1 -1
  119. package/dist/features/media/AvatarUpload.js +1 -1
  120. package/dist/features/media/types/index.d.ts +4 -4
  121. package/dist/features/media/upload/ImageUpload.js +18 -9
  122. package/dist/features/media/upload/MediaUploadList.js +2 -2
  123. package/dist/features/orders/schemas/firestore.d.ts +6 -0
  124. package/dist/features/orders/schemas/index.d.ts +10 -10
  125. package/dist/features/orders/types/index.d.ts +5 -0
  126. package/dist/features/payments/schemas/index.d.ts +4 -4
  127. package/dist/features/pre-orders/schemas/index.d.ts +8 -8
  128. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  129. package/dist/features/products/components/ProductForm.js +20 -2
  130. package/dist/features/products/repository/products.repository.d.ts +3 -1
  131. package/dist/features/products/repository/products.repository.js +12 -1
  132. package/dist/features/products/schemas/firestore.d.ts +7 -0
  133. package/dist/features/products/schemas/index.d.ts +34 -34
  134. package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
  135. package/dist/features/products/types/index.d.ts +6 -0
  136. package/dist/features/promotions/schemas/index.d.ts +14 -14
  137. package/dist/features/reviews/schemas/index.d.ts +18 -18
  138. package/dist/features/seller/actions/offer-actions.js +10 -1
  139. package/dist/features/seller/components/PhysicalLocationModal.d.ts +12 -0
  140. package/dist/features/seller/components/PhysicalLocationModal.js +30 -0
  141. package/dist/features/seller/components/PrintCenterView.d.ts +40 -0
  142. package/dist/features/seller/components/PrintCenterView.js +6 -0
  143. package/dist/features/seller/components/SellerOrdersView.js +48 -3
  144. package/dist/features/seller/components/SellerProductShell.d.ts +1 -0
  145. package/dist/features/seller/components/SellerProductShell.js +26 -4
  146. package/dist/features/seller/components/SellerProductsView.js +54 -13
  147. package/dist/features/seller/components/SellerSidebar.js +1 -1
  148. package/dist/features/seller/components/index.d.ts +1 -0
  149. package/dist/features/seller/components/index.js +1 -0
  150. package/dist/features/seller/schemas/firestore.d.ts +2 -0
  151. package/dist/features/seller/schemas/firestore.js +1 -0
  152. package/dist/features/seller/schemas/index.d.ts +22 -22
  153. package/dist/features/shell/FormShell.js +1 -1
  154. package/dist/features/shell/StepForm.d.ts +3 -1
  155. package/dist/features/shell/StepForm.js +3 -3
  156. package/dist/features/stores/schemas/index.d.ts +4 -4
  157. package/dist/index.d.ts +9 -1
  158. package/dist/index.js +6 -1
  159. package/dist/next/routing/route-map.d.ts +8 -0
  160. package/dist/next/routing/route-map.js +3 -0
  161. package/dist/seed/homepage-sections-seed-data.js +10 -10
  162. package/dist/server.d.ts +1 -1
  163. package/dist/server.js +1 -1
  164. package/dist/tailwind-utilities.css +1 -1
  165. package/dist/ui/components/FormField.js +1 -0
  166. package/dist/ui/components/RowActionMenu.js +45 -10
  167. package/dist/ui/components/SideDrawer.style.css +2 -2
  168. package/dist/ui/forms/FieldCheckbox.d.ts +13 -0
  169. package/dist/ui/forms/FieldCheckbox.js +25 -0
  170. package/dist/ui/forms/FieldInput.d.ts +11 -0
  171. package/dist/ui/forms/FieldInput.js +25 -0
  172. package/dist/ui/forms/FieldSelect.d.ts +17 -0
  173. package/dist/ui/forms/FieldSelect.js +25 -0
  174. package/dist/ui/forms/FormShell.d.ts +48 -0
  175. package/dist/ui/forms/FormShell.js +176 -0
  176. package/dist/ui/forms/FormShell.style.css +287 -0
  177. package/dist/ui/forms/index.d.ts +8 -0
  178. package/dist/ui/forms/index.js +4 -0
  179. package/dist/ui/index.d.ts +9 -0
  180. package/dist/ui/index.js +5 -0
  181. package/dist/utils/action-response.d.ts +6 -0
  182. package/dist/utils/action-response.js +22 -0
  183. package/dist/utils/index.d.ts +1 -0
  184. package/dist/utils/index.js +1 -0
  185. package/dist/validation/schemas.d.ts +10 -10
  186. package/package.json +2 -2
@@ -2,13 +2,16 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import React, { useState, useCallback } from "react";
4
4
  import { X } from "lucide-react";
5
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
5
6
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
7
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu } from "../../../ui";
8
+ import { BulkActionBar, Button, FilterChipGroup, Form, FormActions, Input, ListingToolbar, Modal, Pagination, ListingViewShell, RowActionMenu, Text as AppText, useToast } from "../../../ui";
8
9
  import { AdminViewCards } from "./AdminViewCards";
9
10
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
11
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
10
12
  import { ADMIN_USER_STATUS_TABS, ADMIN_USER_ROLE_TABS } from "../constants/filter-tabs";
11
13
  import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
14
+ import { apiClient } from "../../../http";
12
15
  import { DataTable } from "./DataTable";
13
16
  import { AdminUserEditorView } from "./AdminUserEditorView";
14
17
  const PAGE_SIZE = 25;
@@ -29,12 +32,40 @@ function UsersFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clear
29
32
  export function AdminUsersView({ children, ...props }) {
30
33
  const hasChildren = React.Children.count(children) > 0;
31
34
  const [view, setView] = useState("table");
35
+ const toast = useToast();
36
+ const queryClient = useQueryClient();
32
37
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
33
38
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
34
39
  const [filterOpen, setFilterOpen] = useState(false);
35
40
  const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
36
41
  const [drawerOpen, setDrawerOpen] = useState(false);
37
42
  const [selectedRow, setSelectedRow] = useState(null);
43
+ const [banModalOpen, setBanModalOpen] = useState(false);
44
+ const [banTargetId, setBanTargetId] = useState(null);
45
+ const [banReason, setBanReason] = useState("");
46
+ const banUser = useMutation({
47
+ mutationFn: () => {
48
+ if (!banTargetId)
49
+ throw new Error("No user selected");
50
+ return apiClient.post(ADMIN_ENDPOINTS.USER_HARD_BAN(banTargetId), { reason: banReason.trim() });
51
+ },
52
+ onSuccess: () => {
53
+ toast.showToast("User has been banned.", "success");
54
+ setBanModalOpen(false);
55
+ setBanTargetId(null);
56
+ setBanReason("");
57
+ void queryClient.invalidateQueries({ queryKey: ["admin", "users", "listing"] });
58
+ },
59
+ onError: () => { toast.showToast("Failed to ban user.", "error"); },
60
+ });
61
+ const unbanUser = useMutation({
62
+ mutationFn: (uid) => apiClient.post(ADMIN_ENDPOINTS.USER_UNBAN(uid), {}),
63
+ onSuccess: () => {
64
+ toast.showToast("Ban lifted.", "success");
65
+ void queryClient.invalidateQueries({ queryKey: ["admin", "users", "listing"] });
66
+ },
67
+ onError: () => { toast.showToast("Failed to lift ban.", "error"); },
68
+ });
38
69
  const openFilters = useCallback(() => {
39
70
  setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
40
71
  setFilterOpen(true);
@@ -117,10 +148,26 @@ export function AdminUsersView({ children, ...props }) {
117
148
  }
118
149
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search users, email, or seller handles", 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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
119
150
  { id: "manage", label: "Manage Selected", variant: "primary", onClick: () => { setSelectedRow(rows.find(r => r.id === selection.selectedIds[0]) ?? null); setDrawerOpen(true); selection.clearSelection(); } },
120
- ] }), 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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _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 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No users found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [{
121
- label: "Manage",
122
- onClick: () => { setSelectedRow(row); setDrawerOpen(true); },
123
- }] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No users found", onRowClick: (row) => { setSelectedRow(row); setDrawerOpen(true); }, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(UsersFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _jsx(AdminUserEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), userId: selectedRow?.id, displayName: selectedRow?.primary, currentRole: toStringValue(selectedRow?._raw?.role, "user"), currentEmailVerified: Boolean(selectedRow?._raw?.emailVerified), ownedStoreId: toStringValue(selectedRow?._raw?.storeId, "") || undefined, ownedStoreName: toStringValue(selectedRow?._raw?.storeName, "") || undefined, currentIsHardBanned: Boolean((selectedRow?._raw?.isDisabled ?? selectedRow?._raw?.disabled) &&
151
+ ] }), 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: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _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 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No users found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => {
152
+ const ur = row;
153
+ const isBanned = ur.status === "Hard banned";
154
+ return (_jsx(RowActionMenu, { actions: [
155
+ {
156
+ label: "Manage",
157
+ onClick: () => { setSelectedRow(ur); setDrawerOpen(true); },
158
+ },
159
+ {
160
+ label: ACTIONS.ADMIN["ban-user"].label,
161
+ onClick: () => { setBanTargetId(ur.id); setBanReason(""); setBanModalOpen(true); },
162
+ disabled: isBanned,
163
+ },
164
+ {
165
+ label: ACTIONS.ADMIN["unban-user"].label,
166
+ onClick: () => unbanUser.mutate(ur.id),
167
+ disabled: !isBanned || unbanUser.isPending,
168
+ },
169
+ ] }));
170
+ } })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No users found", onRowClick: (row) => { setSelectedRow(row); setDrawerOpen(true); }, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(UsersFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }), _jsx(AdminUserEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), userId: selectedRow?.id, displayName: selectedRow?.primary, currentRole: toStringValue(selectedRow?._raw?.role, "user"), currentEmailVerified: Boolean(selectedRow?._raw?.emailVerified), ownedStoreId: toStringValue(selectedRow?._raw?.storeId, "") || undefined, ownedStoreName: toStringValue(selectedRow?._raw?.storeName, "") || undefined, currentIsHardBanned: Boolean((selectedRow?._raw?.isDisabled ?? selectedRow?._raw?.disabled) &&
124
171
  selectedRow?._raw?.hardBanReason), currentHardBanReason: toStringValue(selectedRow?._raw?.hardBanReason, "") || undefined, currentSoftBans: Array.isArray(selectedRow?._raw?.softBans)
125
172
  ? selectedRow._raw.softBans.map((b) => ({
126
173
  action: toStringValue(b.action, ""),
@@ -135,5 +182,5 @@ export function AdminUsersView({ children, ...props }) {
135
182
  : null,
136
183
  bannedBy: toStringValue(b.bannedBy, ""),
137
184
  }))
138
- : undefined })] }));
185
+ : undefined }), _jsxs(Modal, { isOpen: banModalOpen, onClose: () => { setBanModalOpen(false); setBanTargetId(null); setBanReason(""); }, title: ACTIONS.ADMIN["ban-user"].confirmation.title, children: [_jsx(AppText, { size: "sm", color: "muted", className: "mb-4", children: ACTIONS.ADMIN["ban-user"].confirmation.body }), _jsxs(Form, { onSubmit: (e) => { e.preventDefault(); banUser.mutate(); }, children: [_jsx(Input, { label: "Reason", value: banReason, onChange: (e) => setBanReason(e.target.value), placeholder: "e.g. Repeated fraud, scam activity\u2026", required: true }), _jsxs(FormActions, { children: [_jsx(Button, { type: "button", variant: "secondary", onClick: () => { setBanModalOpen(false); setBanTargetId(null); setBanReason(""); }, children: "Cancel" }), _jsx(Button, { type: "submit", variant: "danger", isLoading: banUser.isPending, disabled: !banReason.trim() || banUser.isPending, children: ACTIONS.ADMIN["ban-user"].confirmation.confirmLabel })] })] })] })] }));
139
186
  }
@@ -17,7 +17,7 @@ export interface NotificationDocument {
17
17
  isRead: boolean;
18
18
  readAt?: Date;
19
19
  relatedId?: string;
20
- relatedType?: "order" | "product" | "bid" | "review" | "blog" | "user" | "offer" | "support_ticket";
20
+ relatedType?: "order" | "product" | "bid" | "review" | "blog" | "user" | "offer" | "support_ticket" | "scammer";
21
21
  createdAt: Date;
22
22
  updatedAt: Date;
23
23
  }
@@ -14,6 +14,8 @@ import { storeRepository } from "../../stores/repository/store.repository";
14
14
  import { getAdminRealtimeDb } from "../../../providers/db-firebase";
15
15
  import { maskPublicBid } from "../../../security";
16
16
  import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, } from "../../../errors";
17
+ import { BID_ERROR_CODES } from "../../../errors/error-codes";
18
+ import { increment } from "../../../contracts/field-ops";
17
19
  import { getDefaultCurrency } from "../../../core/baseline-resolver";
18
20
  import { resolveDate } from "../../../utils";
19
21
  // --- Domain Functions ----------------------------------------------------------
@@ -27,7 +29,7 @@ export async function placeBid(userId, userEmail, input) {
27
29
  if (product.auctionEndDate) {
28
30
  const endDate = resolveDate(product.auctionEndDate);
29
31
  if (endDate && endDate.getTime() < Date.now()) {
30
- throw new ValidationError(ERROR_MESSAGES.BID.AUCTION_ENDED);
32
+ throw new ValidationError(ERROR_MESSAGES.BID.AUCTION_ENDED, { code: BID_ERROR_CODES.AUCTION_ENDED });
31
33
  }
32
34
  }
33
35
  if (product.storeId) {
@@ -36,11 +38,15 @@ export async function placeBid(userId, userEmail, input) {
36
38
  throw new AuthorizationError(ERROR_MESSAGES.BID.OWN_AUCTION);
37
39
  }
38
40
  }
39
- const minimumBid = (product.currentBid ?? 0) > 0
41
+ const baseBid = (product.currentBid ?? 0) > 0
40
42
  ? product.currentBid
41
43
  : (product.startingBid ?? product.price);
42
- if (bidAmount <= minimumBid) {
43
- throw new ValidationError(ERROR_MESSAGES.BID.BID_TOO_LOW);
44
+ if (bidAmount <= baseBid) {
45
+ throw new ValidationError(ERROR_MESSAGES.BID.BID_TOO_LOW, { code: BID_ERROR_CODES.TOO_LOW });
46
+ }
47
+ const minIncrement = product.minBidIncrement ?? 100;
48
+ if (bidAmount < baseBid + minIncrement) {
49
+ throw new ValidationError(ERROR_MESSAGES.BID.INCREMENT_TOO_LOW, { code: BID_ERROR_CODES.INCREMENT_VIOLATED });
44
50
  }
45
51
  const profile = await userRepository.findById(userId);
46
52
  const bid = await bidRepository.create({
@@ -64,14 +70,15 @@ export async function placeBid(userId, userEmail, input) {
64
70
  }
65
71
  unitOfWork.products.updateInBatch(batch, productId, {
66
72
  currentBid: bidAmount,
67
- bidCount: (product.bidCount ?? 0) + 1,
73
+ bidCount: increment(1),
74
+ leadingBidderId: userId,
75
+ bidsHaveStarted: true,
68
76
  });
69
77
  });
70
78
  try {
71
79
  const rtdb = getAdminRealtimeDb();
72
80
  await rtdb.ref(`/auction-bids/${productId}`).set({
73
81
  currentBid: bidAmount,
74
- bidCount: (product.bidCount ?? 0) + 1,
75
82
  lastBid: {
76
83
  amount: bidAmount,
77
84
  bidderName: "Bidder",
@@ -4,6 +4,13 @@ import { useState, useTransition } from "react";
4
4
  import { formatCurrency } from "../../../utils/number.formatter";
5
5
  import { isAuthError } from "../../../utils/auth-error";
6
6
  import { Button, Div, Input, LoginRequiredModal, Row, Span, Stack, Text } from "../../../ui";
7
+ const BID_ERROR_DISPLAY = {
8
+ BID_AUCTION_ENDED: "This auction has closed. No more bids are accepted.",
9
+ BID_AMOUNT_TOO_LOW: "Your bid must exceed the current winning bid.",
10
+ BID_INCREMENT_TOO_LOW: "Your bid does not meet the minimum increment requirement.",
11
+ BID_SELF_BID: "You cannot bid on your own listing.",
12
+ BID_USER_BANNED: "Your account is currently restricted from bidding. Contact support.",
13
+ };
7
14
  export function PlaceBidFormClient({ productId, currentBid, startingBid, minBidIncrement, currency, isEnded, buyNowPrice, bidCount, tags = [], onPlaceBid, }) {
8
15
  const minBid = currentBid + minBidIncrement;
9
16
  const [bidAmount, setBidAmount] = useState(String(minBid));
@@ -27,8 +34,9 @@ export function PlaceBidFormClient({ productId, currentBid, startingBid, minBidI
27
34
  typeof result === "object" &&
28
35
  "ok" in result &&
29
36
  result.ok === false) {
30
- const msg = result.error;
31
- setError(msg || "Failed to place bid. Please try again.");
37
+ const r = result;
38
+ const display = (r.code && BID_ERROR_DISPLAY[r.code]) ?? r.error ?? "Failed to place bid. Please try again.";
39
+ setError(display);
32
40
  return;
33
41
  }
34
42
  setSuccess(true);
@@ -23,6 +23,13 @@ export declare class BidRepository extends BaseRepository<BidDocument> {
23
23
  * Find bids by user ID
24
24
  */
25
25
  findByUser(userId: string): Promise<BidDocument[]>;
26
+ /**
27
+ * Paginated bids by user — returns at most pageSize bids, sorted by createdAt desc.
28
+ */
29
+ findByUserPaginated(userId: string, pageSize?: number, startAfterDoc?: import("firebase-admin/firestore").QueryDocumentSnapshot): Promise<{
30
+ items: BidDocument[];
31
+ hasMore: boolean;
32
+ }>;
26
33
  /**
27
34
  * Find bids by status
28
35
  */
@@ -56,6 +56,22 @@ export class BidRepository extends BaseRepository {
56
56
  async findByUser(userId) {
57
57
  return this.findBy(BID_FIELDS.USER_ID, userId);
58
58
  }
59
+ /**
60
+ * Paginated bids by user — returns at most pageSize bids, sorted by createdAt desc.
61
+ */
62
+ async findByUserPaginated(userId, pageSize = 25, startAfterDoc) {
63
+ let query = this.db
64
+ .collection(this.collection)
65
+ .where(BID_FIELDS.USER_ID, "==", userId)
66
+ .orderBy("createdAt", "desc")
67
+ .limit(pageSize + 1);
68
+ if (startAfterDoc)
69
+ query = query.startAfter(startAfterDoc);
70
+ const snap = await query.get();
71
+ const hasMore = snap.docs.length > pageSize;
72
+ const docs = hasMore ? snap.docs.slice(0, pageSize) : snap.docs;
73
+ return { items: docs.map((d) => this.mapDoc(d)), hasMore };
74
+ }
59
75
  /**
60
76
  * Find bids by status
61
77
  */
@@ -29,18 +29,18 @@ export declare const auctionItemSchema: z.ZodObject<{
29
29
  source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
30
30
  youtubeId: z.ZodOptional<z.ZodString>;
31
31
  }, "strip", z.ZodTypeAny, {
32
- type: "video" | "image" | "file";
33
32
  url: string;
34
- source?: "upload" | "youtube" | "external" | undefined;
33
+ type: "image" | "video" | "file";
35
34
  alt?: string | undefined;
36
35
  thumbnailUrl?: string | undefined;
36
+ source?: "upload" | "youtube" | "external" | undefined;
37
37
  youtubeId?: string | undefined;
38
38
  }, {
39
- type: "video" | "image" | "file";
40
39
  url: string;
41
- source?: "upload" | "youtube" | "external" | undefined;
40
+ type: "image" | "video" | "file";
42
41
  alt?: string | undefined;
43
42
  thumbnailUrl?: string | undefined;
43
+ source?: "upload" | "youtube" | "external" | undefined;
44
44
  youtubeId?: string | undefined;
45
45
  }>, "many">>;
46
46
  currency: z.ZodString;
@@ -59,18 +59,18 @@ export declare const auctionItemSchema: z.ZodObject<{
59
59
  updatedAt: z.ZodString;
60
60
  }, "strip", z.ZodTypeAny, {
61
61
  currency: string;
62
- id: string;
63
- createdAt: string;
64
- updatedAt: string;
65
62
  status: "draft" | "published" | "archived" | "sold";
66
- title: string;
67
63
  featured: boolean;
68
64
  listingType: "auction";
65
+ createdAt: string;
66
+ id: string;
67
+ title: string;
69
68
  slug: string;
70
69
  price: number;
71
70
  auctionEndDate: string;
72
71
  startingBid: number;
73
72
  bidCount: number;
73
+ updatedAt: string;
74
74
  sellerId: string;
75
75
  storeId?: string | undefined;
76
76
  description?: string | undefined;
@@ -78,28 +78,28 @@ export declare const auctionItemSchema: z.ZodObject<{
78
78
  images?: string[] | undefined;
79
79
  currentBid?: number | undefined;
80
80
  media?: {
81
- type: "video" | "image" | "file";
82
81
  url: string;
83
- source?: "upload" | "youtube" | "external" | undefined;
82
+ type: "image" | "video" | "file";
84
83
  alt?: string | undefined;
85
84
  thumbnailUrl?: string | undefined;
85
+ source?: "upload" | "youtube" | "external" | undefined;
86
86
  youtubeId?: string | undefined;
87
87
  }[] | undefined;
88
88
  storeSlug?: string | undefined;
89
89
  }, {
90
90
  currency: string;
91
- id: string;
92
- createdAt: string;
93
- updatedAt: string;
94
91
  status: "draft" | "published" | "archived" | "sold";
95
- title: string;
96
92
  featured: boolean;
97
93
  listingType: "auction";
94
+ createdAt: string;
95
+ id: string;
96
+ title: string;
98
97
  slug: string;
99
98
  price: number;
100
99
  auctionEndDate: string;
101
100
  startingBid: number;
102
101
  bidCount: number;
102
+ updatedAt: string;
103
103
  sellerId: string;
104
104
  storeId?: string | undefined;
105
105
  description?: string | undefined;
@@ -107,11 +107,11 @@ export declare const auctionItemSchema: z.ZodObject<{
107
107
  images?: string[] | undefined;
108
108
  currentBid?: number | undefined;
109
109
  media?: {
110
- type: "video" | "image" | "file";
111
110
  url: string;
112
- source?: "upload" | "youtube" | "external" | undefined;
111
+ type: "image" | "video" | "file";
113
112
  alt?: string | undefined;
114
113
  thumbnailUrl?: string | undefined;
114
+ source?: "upload" | "youtube" | "external" | undefined;
115
115
  youtubeId?: string | undefined;
116
116
  }[] | undefined;
117
117
  storeSlug?: string | undefined;
@@ -152,14 +152,14 @@ export declare const auctionListParamsSchema: z.ZodObject<{
152
152
  filters: z.ZodOptional<z.ZodString>;
153
153
  }, "strip", z.ZodTypeAny, {
154
154
  sort?: string | undefined;
155
- pageSize?: number | undefined;
156
155
  page?: number | undefined;
156
+ pageSize?: number | undefined;
157
157
  filters?: string | undefined;
158
158
  storeSlug?: string | undefined;
159
159
  }, {
160
160
  sort?: string | undefined;
161
- pageSize?: number | undefined;
162
161
  page?: number | undefined;
162
+ pageSize?: number | undefined;
163
163
  filters?: string | undefined;
164
164
  storeSlug?: string | undefined;
165
165
  }>;
@@ -57,16 +57,16 @@ export declare const authUserSchema: z.ZodObject<{
57
57
  isEmailVerified: z.ZodOptional<z.ZodBoolean>;
58
58
  }, "strip", z.ZodTypeAny, {
59
59
  id: string;
60
- displayName?: string | undefined;
61
60
  role?: "user" | "seller" | "moderator" | "employee" | "admin" | undefined;
62
61
  email?: string | undefined;
62
+ displayName?: string | undefined;
63
63
  photoURL?: string | undefined;
64
64
  isEmailVerified?: boolean | undefined;
65
65
  }, {
66
66
  id: string;
67
- displayName?: string | undefined;
68
67
  role?: "user" | "seller" | "moderator" | "employee" | "admin" | undefined;
69
68
  email?: string | undefined;
69
+ displayName?: string | undefined;
70
70
  photoURL?: string | undefined;
71
71
  isEmailVerified?: boolean | undefined;
72
72
  }>;
@@ -23,8 +23,8 @@ export declare const beforeAfterItemSchema: z.ZodObject<{
23
23
  createdAt: z.ZodOptional<z.ZodString>;
24
24
  }, "strip", z.ZodTypeAny, {
25
25
  id: string;
26
- sortOrder: number;
27
26
  title: string;
27
+ sortOrder: number;
28
28
  isActive: boolean;
29
29
  createdAt?: string | undefined;
30
30
  description?: string | undefined;
@@ -37,8 +37,8 @@ export declare const beforeAfterItemSchema: z.ZodObject<{
37
37
  id: string;
38
38
  title: string;
39
39
  createdAt?: string | undefined;
40
- sortOrder?: number | undefined;
41
40
  description?: string | undefined;
41
+ sortOrder?: number | undefined;
42
42
  isActive?: boolean | undefined;
43
43
  productId?: string | undefined;
44
44
  concern?: string | undefined;
@@ -16,13 +16,13 @@ export declare const createBlogPostSchema: z.ZodObject<{
16
16
  alt: z.ZodOptional<z.ZodString>;
17
17
  thumbnailUrl: z.ZodOptional<z.ZodString>;
18
18
  }, "strip", z.ZodTypeAny, {
19
- type: "video" | "image" | "file";
20
19
  url: string;
20
+ type: "image" | "video" | "file";
21
21
  alt?: string | undefined;
22
22
  thumbnailUrl?: string | undefined;
23
23
  }, {
24
- type: "video" | "image" | "file";
25
24
  url: string;
25
+ type: "image" | "video" | "file";
26
26
  alt?: string | undefined;
27
27
  thumbnailUrl?: string | undefined;
28
28
  }>, z.ZodEffects<z.ZodString, {
@@ -35,13 +35,13 @@ export declare const createBlogPostSchema: z.ZodObject<{
35
35
  alt: z.ZodOptional<z.ZodString>;
36
36
  thumbnailUrl: z.ZodOptional<z.ZodString>;
37
37
  }, "strip", z.ZodTypeAny, {
38
- type: "video" | "image" | "file";
39
38
  url: string;
39
+ type: "image" | "video" | "file";
40
40
  alt?: string | undefined;
41
41
  thumbnailUrl?: string | undefined;
42
42
  }, {
43
- type: "video" | "image" | "file";
44
43
  url: string;
44
+ type: "image" | "video" | "file";
45
45
  alt?: string | undefined;
46
46
  thumbnailUrl?: string | undefined;
47
47
  }>, "many">>>;
@@ -51,13 +51,13 @@ export declare const createBlogPostSchema: z.ZodObject<{
51
51
  alt: z.ZodOptional<z.ZodString>;
52
52
  thumbnailUrl: z.ZodOptional<z.ZodString>;
53
53
  }, "strip", z.ZodTypeAny, {
54
- type: "video" | "image" | "file";
55
54
  url: string;
55
+ type: "image" | "video" | "file";
56
56
  alt?: string | undefined;
57
57
  thumbnailUrl?: string | undefined;
58
58
  }, {
59
- type: "video" | "image" | "file";
60
59
  url: string;
60
+ type: "image" | "video" | "file";
61
61
  alt?: string | undefined;
62
62
  thumbnailUrl?: string | undefined;
63
63
  }>, "many">>>;
@@ -70,30 +70,30 @@ export declare const createBlogPostSchema: z.ZodObject<{
70
70
  metaDescription: z.ZodOptional<z.ZodString>;
71
71
  }, "strip", z.ZodTypeAny, {
72
72
  status: "draft" | "published" | "archived";
73
- title: string;
74
- content: string;
75
73
  category: "news" | "tips" | "guides" | "updates" | "community";
74
+ title: string;
76
75
  slug: string;
77
76
  tags: string[];
77
+ content: string;
78
78
  isFeatured: boolean;
79
79
  excerpt: string;
80
80
  readTimeMinutes: number;
81
81
  contentImages: {
82
- type: "video" | "image" | "file";
83
82
  url: string;
83
+ type: "image" | "video" | "file";
84
84
  alt?: string | undefined;
85
85
  thumbnailUrl?: string | undefined;
86
86
  }[];
87
87
  additionalImages: {
88
- type: "video" | "image" | "file";
89
88
  url: string;
89
+ type: "image" | "video" | "file";
90
90
  alt?: string | undefined;
91
91
  thumbnailUrl?: string | undefined;
92
92
  }[];
93
93
  publishedAt?: string | undefined;
94
94
  coverImage?: {
95
- type: "video" | "image" | "file";
96
95
  url: string;
96
+ type: "image" | "video" | "file";
97
97
  alt?: string | undefined;
98
98
  thumbnailUrl?: string | undefined;
99
99
  } | {
@@ -106,18 +106,18 @@ export declare const createBlogPostSchema: z.ZodObject<{
106
106
  metaTitle?: string | undefined;
107
107
  metaDescription?: string | undefined;
108
108
  }, {
109
- title: string;
110
- content: string;
111
109
  category: "news" | "tips" | "guides" | "updates" | "community";
110
+ title: string;
112
111
  slug: string;
112
+ content: string;
113
113
  excerpt: string;
114
114
  status?: "draft" | "published" | "archived" | undefined;
115
115
  tags?: string[] | undefined;
116
116
  isFeatured?: boolean | undefined;
117
117
  publishedAt?: string | undefined;
118
118
  coverImage?: string | {
119
- type: "video" | "image" | "file";
120
119
  url: string;
120
+ type: "image" | "video" | "file";
121
121
  alt?: string | undefined;
122
122
  thumbnailUrl?: string | undefined;
123
123
  } | null | undefined;
@@ -125,14 +125,14 @@ export declare const createBlogPostSchema: z.ZodObject<{
125
125
  authorName?: string | undefined;
126
126
  readTimeMinutes?: number | undefined;
127
127
  contentImages?: {
128
- type: "video" | "image" | "file";
129
128
  url: string;
129
+ type: "image" | "video" | "file";
130
130
  alt?: string | undefined;
131
131
  thumbnailUrl?: string | undefined;
132
132
  }[] | undefined;
133
133
  additionalImages?: {
134
- type: "video" | "image" | "file";
135
134
  url: string;
135
+ type: "image" | "video" | "file";
136
136
  alt?: string | undefined;
137
137
  thumbnailUrl?: string | undefined;
138
138
  }[] | undefined;
@@ -155,13 +155,13 @@ export declare const updateBlogPostSchema: z.ZodObject<{
155
155
  alt: z.ZodOptional<z.ZodString>;
156
156
  thumbnailUrl: z.ZodOptional<z.ZodString>;
157
157
  }, "strip", z.ZodTypeAny, {
158
- type: "video" | "image" | "file";
159
158
  url: string;
159
+ type: "image" | "video" | "file";
160
160
  alt?: string | undefined;
161
161
  thumbnailUrl?: string | undefined;
162
162
  }, {
163
- type: "video" | "image" | "file";
164
163
  url: string;
164
+ type: "image" | "video" | "file";
165
165
  alt?: string | undefined;
166
166
  thumbnailUrl?: string | undefined;
167
167
  }>, z.ZodEffects<z.ZodString, {
@@ -174,13 +174,13 @@ export declare const updateBlogPostSchema: z.ZodObject<{
174
174
  alt: z.ZodOptional<z.ZodString>;
175
175
  thumbnailUrl: z.ZodOptional<z.ZodString>;
176
176
  }, "strip", z.ZodTypeAny, {
177
- type: "video" | "image" | "file";
178
177
  url: string;
178
+ type: "image" | "video" | "file";
179
179
  alt?: string | undefined;
180
180
  thumbnailUrl?: string | undefined;
181
181
  }, {
182
- type: "video" | "image" | "file";
183
182
  url: string;
183
+ type: "image" | "video" | "file";
184
184
  alt?: string | undefined;
185
185
  thumbnailUrl?: string | undefined;
186
186
  }>, "many">>>>;
@@ -190,13 +190,13 @@ export declare const updateBlogPostSchema: z.ZodObject<{
190
190
  alt: z.ZodOptional<z.ZodString>;
191
191
  thumbnailUrl: z.ZodOptional<z.ZodString>;
192
192
  }, "strip", z.ZodTypeAny, {
193
- type: "video" | "image" | "file";
194
193
  url: string;
194
+ type: "image" | "video" | "file";
195
195
  alt?: string | undefined;
196
196
  thumbnailUrl?: string | undefined;
197
197
  }, {
198
- type: "video" | "image" | "file";
199
198
  url: string;
199
+ type: "image" | "video" | "file";
200
200
  alt?: string | undefined;
201
201
  thumbnailUrl?: string | undefined;
202
202
  }>, "many">>>>;
@@ -209,16 +209,16 @@ export declare const updateBlogPostSchema: z.ZodObject<{
209
209
  metaDescription: z.ZodOptional<z.ZodOptional<z.ZodString>>;
210
210
  }, "strip", z.ZodTypeAny, {
211
211
  status?: "draft" | "published" | "archived" | undefined;
212
- title?: string | undefined;
213
- content?: string | undefined;
214
212
  category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
213
+ title?: string | undefined;
215
214
  slug?: string | undefined;
216
215
  tags?: string[] | undefined;
216
+ content?: string | undefined;
217
217
  isFeatured?: boolean | undefined;
218
218
  publishedAt?: string | undefined;
219
219
  coverImage?: {
220
- type: "video" | "image" | "file";
221
220
  url: string;
221
+ type: "image" | "video" | "file";
222
222
  alt?: string | undefined;
223
223
  thumbnailUrl?: string | undefined;
224
224
  } | {
@@ -230,14 +230,14 @@ export declare const updateBlogPostSchema: z.ZodObject<{
230
230
  authorName?: string | undefined;
231
231
  readTimeMinutes?: number | undefined;
232
232
  contentImages?: {
233
- type: "video" | "image" | "file";
234
233
  url: string;
234
+ type: "image" | "video" | "file";
235
235
  alt?: string | undefined;
236
236
  thumbnailUrl?: string | undefined;
237
237
  }[] | undefined;
238
238
  additionalImages?: {
239
- type: "video" | "image" | "file";
240
239
  url: string;
240
+ type: "image" | "video" | "file";
241
241
  alt?: string | undefined;
242
242
  thumbnailUrl?: string | undefined;
243
243
  }[] | undefined;
@@ -246,16 +246,16 @@ export declare const updateBlogPostSchema: z.ZodObject<{
246
246
  metaDescription?: string | undefined;
247
247
  }, {
248
248
  status?: "draft" | "published" | "archived" | undefined;
249
- title?: string | undefined;
250
- content?: string | undefined;
251
249
  category?: "news" | "tips" | "guides" | "updates" | "community" | undefined;
250
+ title?: string | undefined;
252
251
  slug?: string | undefined;
253
252
  tags?: string[] | undefined;
253
+ content?: string | undefined;
254
254
  isFeatured?: boolean | undefined;
255
255
  publishedAt?: string | undefined;
256
256
  coverImage?: string | {
257
- type: "video" | "image" | "file";
258
257
  url: string;
258
+ type: "image" | "video" | "file";
259
259
  alt?: string | undefined;
260
260
  thumbnailUrl?: string | undefined;
261
261
  } | null | undefined;
@@ -264,14 +264,14 @@ export declare const updateBlogPostSchema: z.ZodObject<{
264
264
  authorName?: string | undefined;
265
265
  readTimeMinutes?: number | undefined;
266
266
  contentImages?: {
267
- type: "video" | "image" | "file";
268
267
  url: string;
268
+ type: "image" | "video" | "file";
269
269
  alt?: string | undefined;
270
270
  thumbnailUrl?: string | undefined;
271
271
  }[] | undefined;
272
272
  additionalImages?: {
273
- type: "video" | "image" | "file";
274
273
  url: string;
274
+ type: "image" | "video" | "file";
275
275
  alt?: string | undefined;
276
276
  thumbnailUrl?: string | undefined;
277
277
  }[] | undefined;
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { Div, Input, Label, RichTextEditor, Select, Stack, TagInput, Text, Textarea, } from "../../../ui";
3
4
  import { Checkbox } from "../../forms/index";
@@ -10,7 +11,7 @@ function renderBlogPostMetaFields(props) {
10
11
  }
11
12
  function renderBlogPostMediaFields(props) {
12
13
  const { value, update, isReadonly, labels, onUploadCover, onUploadContentImage, onUploadAdditionalImage, onAbort, coverImageUrl, contentImages, additionalImages } = props;
13
- return (_jsxs(_Fragment, { children: [_jsx(MediaUploadField, { label: labels.coverImage, value: coverImageUrl || "", onChange: (url) => update({ coverImage: url ? { url, type: "image" } : null }), onChangeField: (media) => update({ coverImage: media }), onUpload: onUploadCover, onAbort: onAbort, accept: "image/*", maxSizeMB: 10, disabled: isReadonly, helperText: labels.coverImageHelper }), _jsx(MediaUploadList, { label: labels.contentImages, value: contentImages, onChange: (media) => update({ contentImages: media }), onUpload: onUploadContentImage, onAbort: onAbort, accept: "image/*", maxItems: 10, maxSizeMB: 10, disabled: isReadonly, helperText: labels.contentImagesHelper }), _jsx(MediaUploadList, { label: labels.additionalImages, value: additionalImages, onChange: (media) => update({ additionalImages: media }), onUpload: onUploadAdditionalImage, onAbort: onAbort, accept: "image/*", maxItems: 5, maxSizeMB: 10, disabled: isReadonly, helperText: labels.additionalImagesHelper }), isReadonly && coverImageUrl && _jsx(Text, { size: "xs", variant: "secondary", children: coverImageUrl })] }));
14
+ return (_jsxs(_Fragment, { children: [_jsx(MediaUploadField, { label: labels.coverImage, value: coverImageUrl || "", onChange: (url) => update({ coverImage: url ? { url, type: "image" } : null }), onChangeField: (media) => update({ coverImage: media }), onUpload: onUploadCover, onAbort: onAbort, accept: "image/*", maxSizeMB: 10, disabled: isReadonly, helperText: labels.coverImageHelper }), _jsx(MediaUploadList, { label: labels.contentImages, value: contentImages, onChange: (media) => update({ contentImages: media }), onUpload: onUploadContentImage, onAbort: onAbort, accept: "image/*,video/*", maxItems: 10, maxSizeMB: 10, disabled: isReadonly, helperText: labels.contentImagesHelper }), _jsx(MediaUploadList, { label: labels.additionalImages, value: additionalImages, onChange: (media) => update({ additionalImages: media }), onUpload: onUploadAdditionalImage, onAbort: onAbort, accept: "image/*,video/*", maxItems: 5, maxSizeMB: 10, disabled: isReadonly, helperText: labels.additionalImagesHelper }), isReadonly && coverImageUrl && _jsx(Text, { size: "xs", variant: "secondary", children: coverImageUrl })] }));
14
15
  }
15
16
  export function BlogPostForm({ value, onChange, categoryOptions, statusOptions, labels, onUploadCover, onUploadContentImage, onUploadAdditionalImage, onAbort, isReadonly = false, renderContentField, renderContentReadonly, }) {
16
17
  const coverImageUrl = getMediaUrl(value.coverImage) ?? null;