@mohasinac/appkit 2.7.58 → 2.7.59
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.
- package/dist/_internal/server/features/scams/og.js +1 -1
- package/dist/_internal/shared/actions/bulk-helpers.d.ts +3 -0
- package/dist/_internal/shared/actions/bulk-helpers.js +19 -0
- package/dist/_internal/shared/config/schema.d.ts +4 -4
- package/dist/client.d.ts +3 -0
- package/dist/client.js +2 -0
- package/dist/configs/next.js +11 -0
- package/dist/features/account/components/AddressesIndexListing.js +3 -4
- package/dist/features/account/components/UserSidebar.js +14 -10
- package/dist/features/admin/components/AdminAllEventEntriesView.js +3 -9
- package/dist/features/admin/components/AdminBidsView.js +2 -8
- package/dist/features/admin/components/AdminBlogView.js +4 -9
- package/dist/features/admin/components/AdminBrandEditorView.js +14 -17
- package/dist/features/admin/components/AdminBrandsView.js +4 -9
- package/dist/features/admin/components/AdminBundlesView.js +39 -58
- package/dist/features/admin/components/AdminCarouselView.js +7 -20
- package/dist/features/admin/components/AdminCartsView.js +3 -4
- package/dist/features/admin/components/AdminCategoriesView.js +4 -9
- package/dist/features/admin/components/AdminCategoryEditorView.js +1 -5
- package/dist/features/admin/components/AdminContactView.js +6 -24
- package/dist/features/admin/components/AdminCouponEditorView.js +14 -17
- package/dist/features/admin/components/AdminCouponsView.js +8 -16
- package/dist/features/admin/components/AdminFaqEditorView.js +14 -17
- package/dist/features/admin/components/AdminFaqsView.js +18 -69
- package/dist/features/admin/components/AdminFeatureEditorView.js +7 -9
- package/dist/features/admin/components/AdminNewsletterView.js +4 -14
- package/dist/features/admin/components/AdminNotificationsView.js +2 -8
- package/dist/features/admin/components/AdminOrdersView.js +2 -8
- package/dist/features/admin/components/AdminPayoutsView.js +2 -8
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -9
- package/dist/features/admin/components/AdminProductEditorView.js +1 -5
- package/dist/features/admin/components/AdminProductsView.js +4 -9
- package/dist/features/admin/components/AdminReviewsView.js +2 -8
- package/dist/features/admin/components/AdminScammersView.js +2 -8
- package/dist/features/admin/components/AdminSessionsView.js +2 -8
- package/dist/features/admin/components/AdminStoresView.js +2 -8
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -6
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +17 -20
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -8
- package/dist/features/admin/components/AdminTeamView.js +5 -14
- package/dist/features/admin/components/AdminUsersView.js +2 -8
- package/dist/features/admin/hooks/useAdminListing.d.ts +58 -0
- package/dist/features/admin/hooks/useAdminListing.js +71 -0
- package/dist/features/auth/schemas/firestore.d.ts +1 -0
- package/dist/features/blog/components/BlogIndexListing.js +3 -6
- package/dist/features/categories/components/CategoriesIndexListing.js +3 -6
- package/dist/features/events/components/AdminEventsView.js +4 -4
- package/dist/features/events/components/EventsIndexListing.js +3 -4
- package/dist/features/homepage/components/AnnouncementBar.d.ts +4 -1
- package/dist/features/homepage/components/AnnouncementBar.js +51 -4
- package/dist/features/homepage/components/MarketplaceHomepageView.d.ts +3 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
- package/dist/features/homepage/components/index.d.ts +1 -1
- package/dist/features/homepage/components/index.js +1 -1
- package/dist/features/homepage/lib/section-renderer.d.ts +2 -2
- package/dist/features/homepage/lib/section-renderer.js +2 -2
- package/dist/features/orders/repository/orders.repository.d.ts +0 -1
- package/dist/features/orders/repository/orders.repository.js +0 -1
- package/dist/features/orders/server.d.ts +1 -1
- package/dist/features/orders/server.js +1 -1
- package/dist/features/products/components/GroupSettingsPanel.js +64 -48
- package/dist/features/products/repository/products.repository.d.ts +0 -2
- package/dist/features/products/repository/products.repository.js +0 -2
- package/dist/features/products/server.d.ts +1 -1
- package/dist/features/products/server.js +1 -1
- package/dist/features/promotions/components/CouponCard.js +5 -5
- package/dist/features/promotions/components/CouponsIndexListing.js +3 -2
- package/dist/features/reviews/components/ReviewsIndexListing.js +3 -4
- package/dist/features/reviews/repository/reviews.repository.d.ts +0 -1
- package/dist/features/reviews/repository/reviews.repository.js +0 -1
- package/dist/features/reviews/server.d.ts +1 -1
- package/dist/features/reviews/server.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +10 -20
- package/dist/features/seller/components/SellerAuctionsView.js +13 -22
- package/dist/features/seller/components/SellerBidsView.js +5 -5
- package/dist/features/seller/components/SellerBundlesView.js +11 -19
- package/dist/features/seller/components/SellerClassifiedView.js +11 -19
- package/dist/features/seller/components/SellerCouponsView.js +14 -20
- package/dist/features/seller/components/SellerDigitalCodesView.js +11 -19
- package/dist/features/seller/components/SellerGroupedListingsView.js +6 -10
- package/dist/features/seller/components/SellerLiveView.js +11 -19
- package/dist/features/seller/components/SellerOffersView.js +3 -4
- package/dist/features/seller/components/SellerOrdersView.js +8 -22
- package/dist/features/seller/components/SellerPayoutMethodsView.js +11 -23
- package/dist/features/seller/components/SellerPayoutsView.js +3 -4
- package/dist/features/seller/components/SellerPreOrdersView.js +13 -22
- package/dist/features/seller/components/SellerPrizeDrawsView.js +13 -22
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +1 -1
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -6
- package/dist/features/seller/components/SellerProductsView.js +9 -22
- package/dist/features/seller/components/SellerShippingConfigsView.js +11 -23
- package/dist/features/seller/components/SellerStoreCategoriesView.js +14 -33
- package/dist/features/seller/components/SellerTemplatesView.js +25 -41
- package/dist/features/stores/components/StoreReviewsListing.js +3 -4
- package/dist/features/stores/components/StoresIndexListing.js +3 -4
- package/dist/index.d.ts +2 -6
- package/dist/index.js +3 -12
- package/dist/react/contexts/SessionContext.d.ts +2 -0
- package/dist/react/contexts/SessionContext.js +2 -0
- package/dist/react/hooks/useEntityDelete.d.ts +15 -0
- package/dist/react/hooks/useEntityDelete.js +39 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/dist/repositories/index.d.ts +3 -3
- package/dist/repositories/index.js +3 -3
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/ListingFilterDrawer.d.ts +10 -0
- package/dist/ui/components/ListingFilterDrawer.js +8 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/package.json +1 -1
|
@@ -55,5 +55,5 @@ export function renderScamOgImage(data, siteName) {
|
|
|
55
55
|
fontSize: 22,
|
|
56
56
|
color: "#f87171",
|
|
57
57
|
fontWeight: 600,
|
|
58
|
-
}, children: scamTypeLabel }), reportCount > 0 && (_jsxs("div", { style: { fontSize: 22, color: "#64748b" }, children: [reportCount, " ", reportCount === 1 ? "report" : "reports"] }))] }),
|
|
58
|
+
}, children: scamTypeLabel }), reportCount > 0 && (_jsxs("div", { style: { fontSize: 22, color: "#64748b" }, children: [reportCount, " ", reportCount === 1 ? "report" : "reports"] }))] }), _jsxs("div", { style: { fontSize: 20, color: "#64748b", marginTop: 8 }, children: ["Do not transact with this individual. Verified by the ", siteName, " community."] })] })] }));
|
|
59
59
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ActionDef } from "./action-registry";
|
|
2
|
+
import type { BulkActionItem } from "../../../ui/components/BulkActionBar";
|
|
3
|
+
export declare function buildBulkAction(actionDef: ActionDef, onClick: () => void | Promise<void>, overrides?: Partial<Pick<BulkActionItem, "id" | "variant" | "icon" | "disabled" | "loading">>): BulkActionItem;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const KIND_TO_VARIANT = {
|
|
2
|
+
primary: "primary",
|
|
3
|
+
secondary: "secondary",
|
|
4
|
+
danger: "danger",
|
|
5
|
+
ghost: "ghost",
|
|
6
|
+
link: "ghost",
|
|
7
|
+
chip: "ghost",
|
|
8
|
+
};
|
|
9
|
+
export function buildBulkAction(actionDef, onClick, overrides) {
|
|
10
|
+
return {
|
|
11
|
+
id: overrides?.id ?? `bulk-${actionDef.id}`,
|
|
12
|
+
label: actionDef.label,
|
|
13
|
+
variant: overrides?.variant ?? KIND_TO_VARIANT[actionDef.kind] ?? "secondary",
|
|
14
|
+
onClick,
|
|
15
|
+
...(overrides?.icon !== undefined && { icon: overrides.icon }),
|
|
16
|
+
...(overrides?.disabled !== undefined && { disabled: overrides.disabled }),
|
|
17
|
+
...(overrides?.loading !== undefined && { loading: overrides.loading }),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* baseUrl: "http://localhost:3000",
|
|
14
14
|
* locales: ["en"],
|
|
15
15
|
* routes: {
|
|
16
|
-
* smoke: [{ path: "/", expect: ["
|
|
16
|
+
* smoke: [{ path: "/", expect: ["MyBrand"] }],
|
|
17
17
|
* },
|
|
18
18
|
* };
|
|
19
19
|
* export default config;
|
|
@@ -84,7 +84,7 @@ export interface AppkitFirebaseConfig {
|
|
|
84
84
|
extensions?: AppkitFirebaseExtensions;
|
|
85
85
|
}
|
|
86
86
|
export interface AppkitBrandConfig {
|
|
87
|
-
/** Display name of the brand, e.g. "
|
|
87
|
+
/** Display name of the brand, e.g. "Acme Store" */
|
|
88
88
|
name: string;
|
|
89
89
|
/** Short / abbreviated name, e.g. "LT" */
|
|
90
90
|
shortName?: string;
|
|
@@ -102,7 +102,7 @@ export interface AppkitBrandConfig {
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
export interface AppkitSeoConfig {
|
|
105
|
-
/** Canonical site URL, e.g. "https://
|
|
105
|
+
/** Canonical site URL, e.g. "https://example.com" */
|
|
106
106
|
siteUrl: string;
|
|
107
107
|
/** Default page <title> */
|
|
108
108
|
defaultTitle?: string;
|
|
@@ -112,7 +112,7 @@ export interface AppkitSeoConfig {
|
|
|
112
112
|
defaultImage?: string;
|
|
113
113
|
/** OG site name */
|
|
114
114
|
siteName?: string;
|
|
115
|
-
/** Twitter/X handle including @, e.g. "@
|
|
115
|
+
/** Twitter/X handle including @, e.g. "@mybrand" */
|
|
116
116
|
twitterHandle?: string;
|
|
117
117
|
/** BCP 47 locale tag for OG/schema.org, e.g. "en-IN" */
|
|
118
118
|
locale?: string;
|
package/dist/client.d.ts
CHANGED
|
@@ -59,6 +59,8 @@ export { useUrlTable } from "./react/hooks/useUrlTable";
|
|
|
59
59
|
export type { UseUrlTableOptions } from "./react/hooks/useUrlTable";
|
|
60
60
|
export { useBulkSelection } from "./react/hooks/useBulkSelection";
|
|
61
61
|
export type { UseBulkSelectionOptions, UseBulkSelectionReturn } from "./react/hooks/useBulkSelection";
|
|
62
|
+
export { useEntityDelete } from "./react/hooks/useEntityDelete";
|
|
63
|
+
export type { UseEntityDeleteOptions, UseEntityDeleteReturn } from "./react/hooks/useEntityDelete";
|
|
62
64
|
export { ROUTES, PUBLIC_ROUTES, PROTECTED_ROUTES, AUTH_ROUTES } from "./constants/index";
|
|
63
65
|
export { Container, Grid, Row, Stack } from "./ui/components/Layout";
|
|
64
66
|
export { Div } from "./ui/components/Div";
|
|
@@ -225,6 +227,7 @@ export { isListingTypeEnabled, isCategoryTypeEnabled, enabledListingTypes, enabl
|
|
|
225
227
|
export { actionTracker, setActionTrackerSink, resetActionTrackerSink, type ActionEvent, type ActionTrackerSink, } from "./_internal/shared/listing-types/action-tracker";
|
|
226
228
|
export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_internal/shared/listing-types/cart-shipping";
|
|
227
229
|
export { ACTIONS, action, act, canPerformAction, actionsForListingType, actionLabel, type ActionDef, type ActionKind, type ActionResource, type ActionTree, type ActionConfirmation, } from "./_internal/shared/actions/action-registry";
|
|
230
|
+
export { buildBulkAction } from "./_internal/shared/actions/bulk-helpers";
|
|
228
231
|
export { isAdminUser, isSellerUser, isModeratorUser, isEmployeeUser, isBuyerUser, } from "./features/auth/role-predicates";
|
|
229
232
|
export { DashboardLayoutClient, RoleGuard } from "./_internal/client/features/layout/index";
|
|
230
233
|
export type { DashboardLayoutClientProps, RoleGuardProps, } from "./_internal/client/features/layout/index";
|
package/dist/client.js
CHANGED
|
@@ -122,6 +122,7 @@ export { SessionProvider, useSession } from "./react/contexts/SessionContext";
|
|
|
122
122
|
export { ThemeProvider } from "./react/contexts/ThemeContext";
|
|
123
123
|
export { useUrlTable } from "./react/hooks/useUrlTable";
|
|
124
124
|
export { useBulkSelection } from "./react/hooks/useBulkSelection";
|
|
125
|
+
export { useEntityDelete } from "./react/hooks/useEntityDelete";
|
|
125
126
|
// Client-safe constants, UI primitives, and views
|
|
126
127
|
export { ROUTES, PUBLIC_ROUTES, PROTECTED_ROUTES, AUTH_ROUTES } from "./constants/index";
|
|
127
128
|
export { Container, Grid, Row, Stack } from "./ui/components/Layout";
|
|
@@ -239,6 +240,7 @@ export { cartRequiresShipping, cartIsDigitalOnly, cartIsChatOnly, } from "./_int
|
|
|
239
240
|
// SB-UNI-W-1 2026-05-13 — CTA action registry shell (client-safe).
|
|
240
241
|
// Phase 7 W-2..W-4 sweeps fill the per-resource buckets surface by surface.
|
|
241
242
|
export { ACTIONS, action, act, canPerformAction, actionsForListingType, actionLabel, } from "./_internal/shared/actions/action-registry";
|
|
243
|
+
export { buildBulkAction } from "./_internal/shared/actions/bulk-helpers";
|
|
242
244
|
// SB-UNI-E user-role predicates (pure functions, client-safe).
|
|
243
245
|
export { isAdminUser, isSellerUser, isModeratorUser, isEmployeeUser, isBuyerUser, } from "./features/auth/role-predicates";
|
|
244
246
|
// Layout feature — client islands (unifies admin/store/user dashboard layouts).
|
package/dist/configs/next.js
CHANGED
|
@@ -90,6 +90,17 @@ export function defineNextConfig(override = {}) {
|
|
|
90
90
|
"/api/**": [
|
|
91
91
|
// Firebase Admin — entire package (lib/ + esm/, all sub-SDKs)
|
|
92
92
|
"./node_modules/firebase-admin/**",
|
|
93
|
+
// @firebase/database-compat — dynamically required by firebase-admin/database
|
|
94
|
+
"./node_modules/@firebase/database-compat/**",
|
|
95
|
+
"./node_modules/@firebase/database/**",
|
|
96
|
+
"./node_modules/@firebase/database-types/**",
|
|
97
|
+
"./node_modules/@firebase/component/**",
|
|
98
|
+
"./node_modules/@firebase/util/**",
|
|
99
|
+
"./node_modules/@firebase/logger/**",
|
|
100
|
+
"./node_modules/@firebase/app/**",
|
|
101
|
+
"./node_modules/@firebase/app-check-interop-types/**",
|
|
102
|
+
"./node_modules/@firebase/auth-interop-types/**",
|
|
103
|
+
"./node_modules/faye-websocket/**",
|
|
93
104
|
// All @google-cloud packages: firestore (incl. protos/**), storage, paginator, etc.
|
|
94
105
|
"./node_modules/@google-cloud/**",
|
|
95
106
|
// Auth libraries used by Firebase Admin token verification
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
5
|
import { useAddresses } from "../hooks/useAddresses";
|
|
7
|
-
import { ListingToolbar, Text } from "../../../ui";
|
|
6
|
+
import { ListingFilterDrawer, ListingToolbar, Text } from "../../../ui";
|
|
8
7
|
import { AddressBook } from "./AddressBook";
|
|
9
8
|
import { AddressFilters } from "./AddressFilters";
|
|
10
9
|
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
@@ -75,5 +74,5 @@ export function AddressesIndexListing({ onAdd, onEdit, onDelete, }) {
|
|
|
75
74
|
},
|
|
76
75
|
});
|
|
77
76
|
return (_jsxs("div", { className: "space-y-4", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by address, postcode or label...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, view: view, onViewChange: (v) => { if (v === "table")
|
|
78
|
-
return; setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx("div", { className: "px-3 sm:px-4", children: isLoading ? (_jsx("div", { className: "grid sm:grid-cols-2 gap-4", children: Array.from({ length: 4 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 animate-pulse p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : addresses.length === 0 ? (_jsx(Text, { className: "py-8 text-center text-sm text-zinc-500 dark:text-zinc-400", children: table.get(TABLE_KEYS.QUERY) ? `No addresses matching "${table.get(TABLE_KEYS.QUERY)}"` : "No saved addresses." })) : (_jsx(AddressBook, { addresses: addresses, onEdit: onEdit, onDelete: onDelete, onAdd: onAdd })) }),
|
|
77
|
+
return; setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx("div", { className: "px-3 sm:px-4", children: isLoading ? (_jsx("div", { className: "grid sm:grid-cols-2 gap-4", children: Array.from({ length: 4 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-200 dark:border-slate-700 animate-pulse p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" })] }, i))) })) : addresses.length === 0 ? (_jsx(Text, { className: "py-8 text-center text-sm text-zinc-500 dark:text-zinc-400", children: table.get(TABLE_KEYS.QUERY) ? `No addresses matching "${table.get(TABLE_KEYS.QUERY)}"` : "No saved addresses." })) : (_jsx(AddressBook, { addresses: addresses, onEdit: onEdit, onDelete: onDelete, onAdd: onAdd })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(AddressFilters, { table: pendingTable }) })] }));
|
|
79
78
|
}
|
|
@@ -4,26 +4,30 @@ import { useState, useCallback, useEffect } from "react";
|
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
import Link from "next/link";
|
|
6
6
|
import { usePathname } from "next/navigation";
|
|
7
|
-
import { Li, Nav, Span, Ul } from "../../../ui";
|
|
7
|
+
import { ConfirmDeleteModal, Li, Nav, Span, Ul } from "../../../ui";
|
|
8
8
|
import { BottomSheet } from "../../layout/BottomSheet";
|
|
9
9
|
import { SidebarCollapseToggle } from "../../../_internal/client/features/layout/SidebarCollapseToggle";
|
|
10
10
|
function isNavItemActive(item, activeHref) {
|
|
11
11
|
return activeHref === item.href || activeHref.startsWith(item.href + "/");
|
|
12
12
|
}
|
|
13
13
|
function NavLink({ item, isActive, onClick }) {
|
|
14
|
+
const [showConfirm, setShowConfirm] = useState(false);
|
|
14
15
|
const handleClick = (e) => {
|
|
15
|
-
if (item.confirm
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
16
|
+
if (item.confirm) {
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
setShowConfirm(true);
|
|
19
|
+
return;
|
|
21
20
|
}
|
|
22
21
|
onClick?.();
|
|
23
22
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const linkClass = `flex items-center gap-2.5 rounded-lg px-3 py-2 text-[0.8125rem] font-medium leading-tight transition-colors ${isActive
|
|
24
|
+
? "bg-primary-50 dark:bg-primary-900/25 text-primary-700 dark:text-primary-300"
|
|
25
|
+
: "text-zinc-500 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:bg-slate-800 hover:text-zinc-900 dark:hover:text-zinc-100"}`;
|
|
26
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Link, { href: item.href, onClick: handleClick, className: linkClass, children: [item.icon && _jsx(Span, { className: "shrink-0 text-base opacity-70", children: item.icon }), _jsx(Span, { className: "flex-1 truncate", children: item.label })] }), showConfirm && item.confirm && (_jsx(ConfirmDeleteModal, { isOpen: true, title: item.confirm.title ?? "Confirm", message: item.confirm.message, confirmText: "Continue", variant: "warning", onConfirm: () => {
|
|
27
|
+
setShowConfirm(false);
|
|
28
|
+
onClick?.();
|
|
29
|
+
window.location.href = item.href;
|
|
30
|
+
}, onClose: () => setShowConfirm(false) }))] }));
|
|
27
31
|
}
|
|
28
32
|
function DrawerContent({ groups, items, activeHref, onItemClick, }) {
|
|
29
33
|
const [openGroups, setOpenGroups] = useState(() => {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, useToast } from "../../../ui";
|
|
7
|
+
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, useToast, ListingFilterDrawer } from "../../../ui";
|
|
9
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
10
|
import { ADMIN_EVENT_ENTRY_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -20,11 +19,6 @@ const SORT_OPTIONS = [
|
|
|
20
19
|
{ value: "submittedAt", label: "Oldest" },
|
|
21
20
|
];
|
|
22
21
|
const STATUS_OPTIONS = ADMIN_EVENT_ENTRY_STATUS_TABS;
|
|
23
|
-
function EntriesFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
24
|
-
if (!filterOpen)
|
|
25
|
-
return null;
|
|
26
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
27
|
-
}
|
|
28
22
|
export function AdminAllEventEntriesView({ children, ...props }) {
|
|
29
23
|
const hasChildren = React.Children.count(children) > 0;
|
|
30
24
|
const [view, setView] = useState("table");
|
|
@@ -108,5 +102,5 @@ export function AdminAllEventEntriesView({ children, ...props }) {
|
|
|
108
102
|
{ label: ACTIONS.ADMIN["waitlist-entry"].label, onClick: () => updateMutation.mutate({ id: er.id, status: "WAITLISTED" }) },
|
|
109
103
|
{ label: ACTIONS.ADMIN["cancel-entry"].label, destructive: true, onClick: () => updateMutation.mutate({ id: er.id, status: "CANCELLED" }) },
|
|
110
104
|
] }));
|
|
111
|
-
} })] }), _jsx(
|
|
105
|
+
} })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }));
|
|
112
106
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
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
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, ConfirmDeleteModal, RowActionMenu, useToast } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, ConfirmDeleteModal, RowActionMenu, useToast, ListingFilterDrawer } from "../../../ui";
|
|
9
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
9
|
import { ADMIN_BID_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
@@ -23,11 +22,6 @@ const SORT_OPTIONS = [
|
|
|
23
22
|
{ value: "-amount", label: "Highest amount" },
|
|
24
23
|
];
|
|
25
24
|
const STATUS_OPTIONS = ADMIN_BID_STATUS_TABS;
|
|
26
|
-
function BidsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
27
|
-
if (!filterOpen)
|
|
28
|
-
return null;
|
|
29
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
30
|
-
}
|
|
31
25
|
export function AdminBidsView({ children, ...props }) {
|
|
32
26
|
const hasChildren = React.Children.count(children) > 0;
|
|
33
27
|
const [view, setView] = useState("table");
|
|
@@ -127,6 +121,6 @@ export function AdminBidsView({ children, ...props }) {
|
|
|
127
121
|
},
|
|
128
122
|
},
|
|
129
123
|
] }));
|
|
130
|
-
} })] }), _jsx(
|
|
124
|
+
} })] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) })] }), _jsx(ConfirmDeleteModal, { isOpen: cancelOpen, onClose: () => { setCancelOpen(false); setSelectedRow(null); }, onConfirm: () => { if (selectedRow)
|
|
131
125
|
cancelMutation.mutate(selectedRow.id); }, isDeleting: cancelMutation.isPending, title: "Cancel this bid?", message: "This will mark the bid as cancelled and notify the bidder. The auction will continue with remaining active bids.", confirmText: "Cancel bid", variant: "warning" })] }));
|
|
132
126
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { Plus
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, Button, FilterChipGroup, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Button, FilterChipGroup, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, ListingFilterDrawer } from "../../../ui";
|
|
9
9
|
import { AdminViewCards } from "./AdminViewCards";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { ADMIN_BLOG_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -28,11 +28,6 @@ const FEATURED_TABS = [
|
|
|
28
28
|
{ id: "", label: "All" },
|
|
29
29
|
{ id: "true", label: "Featured only" },
|
|
30
30
|
];
|
|
31
|
-
function BlogFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
32
|
-
if (!filterOpen)
|
|
33
|
-
return null;
|
|
34
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Featured", tabs: FEATURED_TABS, value: pendingFilters.isFeatured ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, isFeatured: id })), allId: "" })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
35
|
-
}
|
|
36
31
|
export function AdminBlogView({ children, getRowHref, ...props }) {
|
|
37
32
|
const hasChildren = React.Children.count(children) > 0;
|
|
38
33
|
const [view, setView] = useState("table");
|
|
@@ -113,5 +108,5 @@ export function AdminBlogView({ children, getRowHref, ...props }) {
|
|
|
113
108
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Blog Posts" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search articles, authors, or tags", 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, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "New Post"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
114
109
|
{ id: "publish", label: ACTIONS.ADMIN["publish-blog"].label, variant: "primary", onClick: () => { selection.clearSelection(); } },
|
|
115
110
|
{ id: "draft", label: ACTIONS.ADMIN["draft-blog"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
116
|
-
] }), 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-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No blog posts found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No blog posts found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }),
|
|
111
|
+
] }), 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-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No blog posts found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No blog posts found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Featured", tabs: FEATURED_TABS, value: pendingFilters.isFeatured ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, isFeatured: id })), allId: "" })] }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "New Post" : "Edit Post", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminBlogEditorView, { postId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
117
112
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
5
|
-
import { Button, Form, Input, StackedViewShell, Toggle, useToast, } from "../../../ui";
|
|
5
|
+
import { Button, ConfirmDeleteModal, Form, Input, StackedViewShell, Toggle, useToast, } from "../../../ui";
|
|
6
6
|
import { FieldInput, FormShellContext, useFormShellState } from "../../../ui/forms";
|
|
7
7
|
import { ImageUpload } from "../../media/upload/ImageUpload";
|
|
8
8
|
import { useMediaUpload } from "../../media";
|
|
@@ -23,6 +23,7 @@ export function AdminBrandEditorView({ brandId, onSaved, onDeleted, embedded, ..
|
|
|
23
23
|
const [website, setWebsite] = React.useState("");
|
|
24
24
|
const [isActive, setIsActive] = React.useState(true);
|
|
25
25
|
const [displayOrder, setDisplayOrder] = React.useState("");
|
|
26
|
+
const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
|
|
26
27
|
const { showToast } = useToast();
|
|
27
28
|
const { shellCtx, setFieldError, clearErrors } = useFormShellState();
|
|
28
29
|
const brandQuery = useQuery({
|
|
@@ -90,22 +91,18 @@ export function AdminBrandEditorView({ brandId, onSaved, onDeleted, embedded, ..
|
|
|
90
91
|
});
|
|
91
92
|
const { upload } = useMediaUpload();
|
|
92
93
|
const isSubmitting = saveMutation.isPending || brandQuery.isLoading;
|
|
93
|
-
const formSection = (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (confirm("Delete this brand? This cannot be undone.")) {
|
|
106
|
-
deleteMutation.mutate();
|
|
107
|
-
}
|
|
108
|
-
}, children: "Delete brand" }))] })] }, "brand-form") }));
|
|
94
|
+
const formSection = (_jsxs(FormShellContext.Provider, { value: shellCtx, children: [_jsxs(Form, { onSubmit: (e) => {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
clearErrors();
|
|
97
|
+
if (!name.trim()) {
|
|
98
|
+
setFieldError("name", "Brand name is required");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
saveMutation.mutate();
|
|
102
|
+
}, className: "space-y-4", children: [_jsxs("div", { className: "grid sm:grid-cols-2 gap-4", children: [_jsx(FieldInput, { name: "name", label: "Brand name", value: name, onChange: (v) => handleNameChange(v), required: true, placeholder: "e.g. Hot Wheels" }), _jsx(Input, { label: "Slug", value: slug, onChange: (e) => {
|
|
103
|
+
setSlug(e.target.value);
|
|
104
|
+
setSlugManual(true);
|
|
105
|
+
}, placeholder: "brand-hot-wheels", helperText: "Auto-generated from name. Must start with 'brand-'." })] }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Brief description of the brand" }), _jsxs("div", { className: "grid sm:grid-cols-2 gap-4", children: [_jsx(ImageUpload, { label: "Logo", currentImage: logoURL, onUpload: (file) => upload(file, "brands", true, { type: "brand-logo", brand: name || slug }), onChange: setLogoURL }), _jsx(ImageUpload, { label: "Banner", currentImage: bannerURL, onUpload: (file) => upload(file, "brands", true, { type: "brand-banner", brand: name || slug }), onChange: setBannerURL })] }), _jsxs("div", { className: "grid sm:grid-cols-2 gap-4", children: [_jsx(Input, { label: "Website", value: website, onChange: (e) => setWebsite(e.target.value), placeholder: "https://brand.com", type: "url" }), _jsx(Input, { label: "Display order", value: displayOrder, onChange: (e) => setDisplayOrder(e.target.value), type: "number", min: 0, placeholder: "0" })] }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsxs("div", { className: "flex gap-3 pt-2", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !name || isSubmitting, children: isEdit ? "Save changes" : "Create brand" }), isEdit && (_jsx(Button, { type: "button", variant: "danger", onClick: () => setDeleteConfirmOpen(true), children: "Delete brand" }))] })] }, "brand-form"), deleteConfirmOpen && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Brand", message: "Delete this brand? This cannot be undone.", onConfirm: () => { deleteMutation.mutate(); setDeleteConfirmOpen(false); }, onClose: () => setDeleteConfirmOpen(false), isDeleting: deleteMutation.isPending }))] }));
|
|
109
106
|
if (embedded) {
|
|
110
107
|
return _jsx("div", { className: "overflow-y-auto p-4", children: formSection });
|
|
111
108
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { Plus
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { BulkActionBar, Button, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Button, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text, ListingFilterDrawer } from "../../../ui";
|
|
9
9
|
import { AdminViewCards } from "./AdminViewCards";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
@@ -22,11 +22,6 @@ const SORT_OPTIONS = [
|
|
|
22
22
|
{ value: "-name", label: "Name Z–A" },
|
|
23
23
|
{ value: "-createdAt", label: "Newest" },
|
|
24
24
|
];
|
|
25
|
-
function BrandsFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
26
|
-
if (!filterOpen)
|
|
27
|
-
return null;
|
|
28
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
29
|
-
}
|
|
30
25
|
export function AdminBrandsView({ children, ...props }) {
|
|
31
26
|
const hasChildren = React.Children.count(children) > 0;
|
|
32
27
|
const [view, setView] = useState("table");
|
|
@@ -93,5 +88,5 @@ export function AdminBrandsView({ children, ...props }) {
|
|
|
93
88
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search brands by name or slug", 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, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Brand"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
94
89
|
{ id: "edit", label: ACTIONS.ADMIN["edit-brand"].label, variant: "primary", onClick: () => { const id = selection.selectedIds[0]; if (id)
|
|
95
90
|
openEditPanel(id); selection.clearSelection(); } },
|
|
96
|
-
] }), 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-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No brands found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No brands found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(
|
|
91
|
+
] }), 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-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No brands found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No brands found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Brand" : "Edit Brand", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminBrandEditorView, { brandId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
97
92
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
|
-
import { Plus
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import { Badge, BulkActionBar, Button, Heading, ListingToolbar, Pagination, Stack, Text, useToast, } from "../../../ui";
|
|
7
|
+
import { Badge, BulkActionBar, Button, Heading, ListingToolbar, Pagination, Stack, Text, useToast, ListingFilterDrawer } from "../../../ui";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
10
10
|
import { DataTable } from "./DataTable";
|
|
11
11
|
import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy";
|
|
12
12
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
13
|
+
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
13
14
|
import { useBottomActions } from "../../layout";
|
|
14
15
|
const PAGE_SIZE = 25;
|
|
15
16
|
const FILTER_KEYS = ["isActive", "bundleStockStatus"];
|
|
@@ -51,22 +52,6 @@ const COLUMNS = [
|
|
|
51
52
|
},
|
|
52
53
|
{ key: "updatedAt", header: "Updated", className: "w-28" },
|
|
53
54
|
];
|
|
54
|
-
function BundlesFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearFilters, pendingFilters, setPendingFilters, applyFilters, }) {
|
|
55
|
-
if (!filterOpen)
|
|
56
|
-
return null;
|
|
57
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
|
|
58
|
-
{ label: "All", value: "" },
|
|
59
|
-
{ label: "Active", value: "true" },
|
|
60
|
-
{ label: "Inactive", value: "false" },
|
|
61
|
-
].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
|
|
62
|
-
? "bg-primary text-white border-primary"
|
|
63
|
-
: "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Stock" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
|
|
64
|
-
{ label: "All", value: "" },
|
|
65
|
-
{ label: "Sold out", value: "out_of_stock" },
|
|
66
|
-
].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, bundleStockStatus: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.bundleStockStatus || "") === opt.value
|
|
67
|
-
? "bg-primary text-white border-primary"
|
|
68
|
-
: "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] })] }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
69
|
-
}
|
|
70
55
|
export function AdminBundlesView({ getEditHref, newHref }) {
|
|
71
56
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
72
57
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
@@ -151,49 +136,45 @@ export function AdminBundlesView({ getEditHref, newHref }) {
|
|
|
151
136
|
}
|
|
152
137
|
}, [refetch, toast]);
|
|
153
138
|
const bulkActions = [
|
|
154
|
-
{
|
|
155
|
-
id
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
})));
|
|
178
|
-
selection.clearSelection();
|
|
179
|
-
refetch();
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
id: "delete",
|
|
184
|
-
label: ACTIONS.ADMIN["delete-bundle"].label,
|
|
185
|
-
variant: "danger",
|
|
186
|
-
onClick: async () => {
|
|
187
|
-
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), { method: "DELETE" })));
|
|
188
|
-
selection.clearSelection();
|
|
189
|
-
refetch();
|
|
190
|
-
},
|
|
191
|
-
},
|
|
139
|
+
buildBulkAction(ACTIONS.ADMIN["activate-bundle"], async () => {
|
|
140
|
+
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), {
|
|
141
|
+
method: "PUT",
|
|
142
|
+
headers: { "Content-Type": "application/json" },
|
|
143
|
+
body: JSON.stringify({ isActive: true }),
|
|
144
|
+
})));
|
|
145
|
+
selection.clearSelection();
|
|
146
|
+
refetch();
|
|
147
|
+
}),
|
|
148
|
+
buildBulkAction(ACTIONS.ADMIN["deactivate-bundle"], async () => {
|
|
149
|
+
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), {
|
|
150
|
+
method: "PUT",
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
body: JSON.stringify({ isActive: false }),
|
|
153
|
+
})));
|
|
154
|
+
selection.clearSelection();
|
|
155
|
+
refetch();
|
|
156
|
+
}),
|
|
157
|
+
buildBulkAction(ACTIONS.ADMIN["delete-bundle"], async () => {
|
|
158
|
+
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), { method: "DELETE" })));
|
|
159
|
+
selection.clearSelection();
|
|
160
|
+
refetch();
|
|
161
|
+
}),
|
|
192
162
|
];
|
|
193
163
|
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions } } : {});
|
|
194
164
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Bundles" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search bundles by name or slug\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
195
165
|
table.set("sort", v);
|
|
196
166
|
}, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsx(Button, { asChild: true, size: "sm", variant: "primary", children: _jsxs("a", { href: newHref, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), BUNDLE_COPY.adminList.newButton] }) }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActions }), 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-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), _jsx(DataTable, { columns: COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: BUNDLE_COPY.adminList.empty, getRowHref: getEditHref, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
|
|
197
167
|
? selection.setSelectedIds(rows.map((r) => r.id))
|
|
198
|
-
: selection.clearSelection(), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", isLoading: rebuildingId === row.id, disabled: rebuildingId === row.id, onClick: () => void handleRebuild(row.id), children: ACTIONS.ADMIN["rebuild-bundle"].label })) })] }),
|
|
168
|
+
: selection.clearSelection(), renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", isLoading: rebuildingId === row.id, disabled: rebuildingId === row.id, onClick: () => void handleRebuild(row.id), children: ACTIONS.ADMIN["rebuild-bundle"].label })) })] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
|
|
169
|
+
{ label: "All", value: "" },
|
|
170
|
+
{ label: "Active", value: "true" },
|
|
171
|
+
{ label: "Inactive", value: "false" },
|
|
172
|
+
].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
|
|
173
|
+
? "bg-primary text-white border-primary"
|
|
174
|
+
: "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Stock" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [
|
|
175
|
+
{ label: "All", value: "" },
|
|
176
|
+
{ label: "Sold out", value: "out_of_stock" },
|
|
177
|
+
].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, bundleStockStatus: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.bundleStockStatus || "") === opt.value
|
|
178
|
+
? "bg-primary text-white border-primary"
|
|
179
|
+
: "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] })] })] }));
|
|
199
180
|
}
|