@mohasinac/appkit 4.6.2 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +3 -0
- package/dist/client.js +2 -0
- package/dist/core/components/SyncManagerMount.d.ts +1 -0
- package/dist/core/components/SyncManagerMount.js +23 -0
- package/dist/features/admin/actions/admin-actions.d.ts +11 -0
- package/dist/features/admin/actions/admin-actions.js +41 -0
- package/dist/features/layout/BottomNavbar.js +1 -1
- package/dist/features/products/constants/action-defs.d.ts +1 -1
- package/dist/features/products/constants/action-defs.js +1 -5
- package/dist/features/search/components/Search.js +2 -2
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -194,6 +194,8 @@ export type { AuthGuardUser, ForgotPasswordViewProps, LoginFormProps, LoginFormV
|
|
|
194
194
|
export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangePassword, useRequestPasswordChangeOtp, useVerifyPasswordChangeOtp, useChangeEmail } from "./features/auth/index";
|
|
195
195
|
export type { LoginCredentials, RegisterData, ForgotPasswordData, ResetPasswordData, VerifyEmailData, ChangePasswordData, ChangeEmailData } from "./features/auth/index";
|
|
196
196
|
export { CartView, CartItemRow, CartSummary, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
197
|
+
export { getCartOps, CART_OPS_CHANGE_EVENT } from "./features/cart/utils/pending-ops";
|
|
198
|
+
export type { CartOp } from "./features/cart/utils/pending-ops";
|
|
197
199
|
export type { CartItem, CartItemMeta, CartData, GuestCartItem } from "./features/cart/index";
|
|
198
200
|
export { useAddresses, useCreateAddress, useUpdateAddress, useDeleteAddress, useSetDefaultAddress, useAddress } from "./features/account/index";
|
|
199
201
|
export type { Address, AddressFormData } from "./features/account/index";
|
|
@@ -220,6 +222,7 @@ export type { AdSlotId, AdProvider, AdSlotConfig } from "./features/homepage/ad-
|
|
|
220
222
|
export { WishlistView, useGuestWishlist, useWishlistWithGuest } from "./features/wishlist/index";
|
|
221
223
|
export type { WishlistViewProps, GuestWishlistItem, WishlistItem, WishlistResponse, WishlistProductData, EnrichedWishlistItem } from "./features/wishlist/index";
|
|
222
224
|
export { WishlistCapWatcher } from "./features/wishlist/components/WishlistCapWatcher";
|
|
225
|
+
export { SyncManagerMount } from "./core/components/SyncManagerMount";
|
|
223
226
|
export { useWishlistCount, useWishlistCountWithLimit, WISHLIST_CAP_EVENT } from "./features/wishlist/hooks/useWishlistCount";
|
|
224
227
|
export type { WishlistCapEventDetail } from "./features/wishlist/hooks/useWishlistCount";
|
|
225
228
|
export { getGuestWishlistItems, addToGuestWishlist, removeFromGuestWishlist, isInGuestWishlist, clearGuestWishlist, getGuestWishlistCount, getGuestWishlistByType, } from "./features/wishlist/utils/guest-wishlist";
|
package/dist/client.js
CHANGED
|
@@ -224,6 +224,7 @@ export { AuctionBidsTable } from "./features/auctions/components/AuctionBidsTabl
|
|
|
224
224
|
export { ProtectedRoute, AuthStatusPanel, ForgotPasswordView, LoginForm, RegisterForm, ResetPasswordView, VerifyEmailView } from "./features/auth/index";
|
|
225
225
|
export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangePassword, useRequestPasswordChangeOtp, useVerifyPasswordChangeOtp, useChangeEmail } from "./features/auth/index";
|
|
226
226
|
export { CartView, CartItemRow, CartSummary, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
227
|
+
export { getCartOps, CART_OPS_CHANGE_EVENT } from "./features/cart/utils/pending-ops";
|
|
227
228
|
export { useAddresses, useCreateAddress, useUpdateAddress, useDeleteAddress, useSetDefaultAddress, useAddress } from "./features/account/index";
|
|
228
229
|
export { AddressBook, AddressCard, AddressForm } from "./features/account/index";
|
|
229
230
|
export { useProfile, useUpdateProfile } from "./features/account/index";
|
|
@@ -239,6 +240,7 @@ export { useActiveAd } from "./features/homepage/hooks/useActiveAd";
|
|
|
239
240
|
export { registerAdSlot, registerAdSlots, unregisterAdSlot, clearAdRegistry, setAdConsentGranted, isAdConsentGranted, isAdSlotRenderable, } from "./features/homepage/ad-registry";
|
|
240
241
|
export { WishlistView, useGuestWishlist, useWishlistWithGuest } from "./features/wishlist/index";
|
|
241
242
|
export { WishlistCapWatcher } from "./features/wishlist/components/WishlistCapWatcher";
|
|
243
|
+
export { SyncManagerMount } from "./core/components/SyncManagerMount";
|
|
242
244
|
export { useWishlistCount, useWishlistCountWithLimit, WISHLIST_CAP_EVENT } from "./features/wishlist/hooks/useWishlistCount";
|
|
243
245
|
export { getGuestWishlistItems, addToGuestWishlist, removeFromGuestWishlist, isInGuestWishlist, clearGuestWishlist, getGuestWishlistCount, getGuestWishlistByType, } from "./features/wishlist/utils/guest-wishlist";
|
|
244
246
|
export { InteractiveProductCard } from "./features/products/index";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function SyncManagerMount(): null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* SyncManagerMount — mounts useSyncManager() once, globally.
|
|
4
|
+
*
|
|
5
|
+
* Root-caused 2026-08-20: useSyncManager (the hook that replays the
|
|
6
|
+
* local-first cart/wishlist op queue against the server — see
|
|
7
|
+
* ../hooks/useSyncManager.ts) was never actually called anywhere in the
|
|
8
|
+
* app. Every listing-grid "Add to Cart"/"Add to Wishlist" button writes to
|
|
9
|
+
* a localStorage op queue and shows an immediately-optimistic toast +
|
|
10
|
+
* badge-count update (via the pending-ops delta), but with no mounted
|
|
11
|
+
* useSyncManager the queued op had no code path that would ever POST it
|
|
12
|
+
* to the server — the badge looked right, but the actual /cart or
|
|
13
|
+
* /user/wishlist page (reading the real server state) stayed empty
|
|
14
|
+
* forever, not just briefly. Mount this once, high in the tree, wherever
|
|
15
|
+
* the current session's uid is known.
|
|
16
|
+
*/
|
|
17
|
+
import { useSyncManager } from "../hooks/useSyncManager";
|
|
18
|
+
import { useSession } from "../../react/contexts/SessionContext";
|
|
19
|
+
export function SyncManagerMount() {
|
|
20
|
+
const { user } = useSession();
|
|
21
|
+
useSyncManager(user?.uid);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
@@ -19,6 +19,17 @@ export declare function revokeUserSessions(adminId: string, userId: string): Pro
|
|
|
19
19
|
export declare function adminUpdateOrder(adminId: string, id: string, input: OrderAdminUpdateInput): Promise<OrderDocument>;
|
|
20
20
|
export declare function adminUpdatePayout(adminId: string, id: string, input: PayoutUpdateInput): Promise<PayoutDocument>;
|
|
21
21
|
export declare function adminUpdateUser(adminId: string, uid: string, input: UserAdminUpdateInput): Promise<UserDocument>;
|
|
22
|
+
/**
|
|
23
|
+
* Permanently deletes a user's Firestore record, all of their session
|
|
24
|
+
* records, and their Firebase Auth account — a genuine, irreversible
|
|
25
|
+
* delete (unlike hard-ban, which only disables/marks the account and
|
|
26
|
+
* stays recoverable). Deliberately narrow: only these 3 records are
|
|
27
|
+
* removed. Orders, reviews, addresses, payouts, etc. are left untouched —
|
|
28
|
+
* they're business/audit records that reference this uid and deleting
|
|
29
|
+
* them would corrupt other parties' (sellers', buyers') own history, the
|
|
30
|
+
* same reasoning testerSandboxCleanup already documents for why it leaves
|
|
31
|
+
* orders/reviews/wishlists/history alone when a test product disappears.
|
|
32
|
+
*/
|
|
22
33
|
export declare function adminDeleteUser(adminId: string, uid: string): Promise<void>;
|
|
23
34
|
export declare function adminUpdateStoreStatus(adminId: string, input: {
|
|
24
35
|
uid: string;
|
|
@@ -14,6 +14,8 @@ import { productRepository } from "../../products/repository/products.repository
|
|
|
14
14
|
import { NotFoundError, ValidationError } from "../../../errors";
|
|
15
15
|
import { finalizeStagedMediaUrl, finalizeStagedMediaField, finalizeStagedMediaArray, } from "../../media/finalize";
|
|
16
16
|
import { getProviders } from "../../../contracts";
|
|
17
|
+
import { getAdminAuth } from "../../../providers/db-firebase";
|
|
18
|
+
import { isAdminUser } from "../../auth/role-predicates";
|
|
17
19
|
import { assertPrizeDrawNotLocked, assertPrizeDrawWonItemsImmutable, } from "../../../_internal/server/features/products/service";
|
|
18
20
|
const ERR_UID_REQUIRED = "uid is required";
|
|
19
21
|
export async function revokeSession(adminId, sessionId) {
|
|
@@ -112,6 +114,17 @@ export async function adminUpdateUser(adminId, uid, input) {
|
|
|
112
114
|
});
|
|
113
115
|
return updated;
|
|
114
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Permanently deletes a user's Firestore record, all of their session
|
|
119
|
+
* records, and their Firebase Auth account — a genuine, irreversible
|
|
120
|
+
* delete (unlike hard-ban, which only disables/marks the account and
|
|
121
|
+
* stays recoverable). Deliberately narrow: only these 3 records are
|
|
122
|
+
* removed. Orders, reviews, addresses, payouts, etc. are left untouched —
|
|
123
|
+
* they're business/audit records that reference this uid and deleting
|
|
124
|
+
* them would corrupt other parties' (sellers', buyers') own history, the
|
|
125
|
+
* same reasoning testerSandboxCleanup already documents for why it leaves
|
|
126
|
+
* orders/reviews/wishlists/history alone when a test product disappears.
|
|
127
|
+
*/
|
|
115
128
|
export async function adminDeleteUser(adminId, uid) {
|
|
116
129
|
if (!uid?.trim()) {
|
|
117
130
|
throw new ValidationError(ERR_UID_REQUIRED);
|
|
@@ -123,6 +136,34 @@ export async function adminDeleteUser(adminId, uid) {
|
|
|
123
136
|
if (!existing) {
|
|
124
137
|
throw new NotFoundError("User not found");
|
|
125
138
|
}
|
|
139
|
+
if (isAdminUser(existing)) {
|
|
140
|
+
throw new ValidationError("Cannot delete an admin account");
|
|
141
|
+
}
|
|
142
|
+
// 1. Delete all session records for this user.
|
|
143
|
+
try {
|
|
144
|
+
const sessions = await sessionRepository.findAllByUser(uid, 1000);
|
|
145
|
+
await Promise.all(sessions.map((s) => sessionRepository.delete(s.id)));
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
void normalizeError(err);
|
|
149
|
+
serverLogger.warn("adminDeleteUser: session cleanup failed (non-fatal)", {
|
|
150
|
+
adminId,
|
|
151
|
+
uid,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// 2. Delete the Firebase Auth record.
|
|
155
|
+
try {
|
|
156
|
+
await getAdminAuth().deleteUser(uid);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
void normalizeError(err);
|
|
160
|
+
serverLogger.warn("adminDeleteUser: Auth delete failed (user may lack Auth record)", {
|
|
161
|
+
adminId,
|
|
162
|
+
uid,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// 3. Delete the Firestore user document — the one write that must
|
|
166
|
+
// succeed for the delete to take effect.
|
|
126
167
|
await userRepository.delete(uid);
|
|
127
168
|
serverLogger.info("adminDeleteUser", {
|
|
128
169
|
adminId,
|
|
@@ -35,6 +35,6 @@ export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishl
|
|
|
35
35
|
const visibleItems = navItems.slice(0, cartHref ? 3 : 4);
|
|
36
36
|
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [visibleItems.map((item) => (_jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: item.href, label: item.label, icon: item.icon ?? defaultIcon, isActive: pathname === item.href || (item.href !== "/" && pathname.startsWith(item.href + "/")), variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }, item.key))), cartHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) })), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onMoreToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.more, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 6h16M4 12h16M4 18h16" }) }), _jsx(Span, { className: labelClassName, children: labels.more })] }) })] }));
|
|
37
37
|
}
|
|
38
|
-
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1 w-1/5", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": labels.cart, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
38
|
+
return (_jsxs(BottomNavLayout, { ariaLabel: labels.mobileNav, children: [_jsx(Li, { className: "flex-1 w-1/5", children: _jsx(NavItem, { href: homeHref, label: labels.home, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" }) }), isActive: pathname === homeHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsx(NavItem, { href: shopHref, label: labels.products, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" }) }), isActive: pathname === shopHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName }) }), _jsx(Li, { className: "flex-1", children: _jsxs("button", { type: "button", onClick: onSearchToggle, className: `${slotClassName} ${inactiveClassName}`, "aria-label": labels.search, children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }), _jsx(Span, { className: labelClassName, children: labels.search })] }) }), _jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: cartHref, variant: "none", className: `${slotClassName} ${pathname === cartHref ? activeClassName : inactiveClassName}`, "aria-label": cartCount > 0 ? `${labels.cart}, ${cartCount} items` : labels.cart, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" }) }), cartCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: cartCount > 9 ? "9+" : cartCount }))] }), _jsx(Span, { className: labelClassName, children: labels.cart })] }) }), wishlistHref && (_jsx(Li, { className: "flex-1", children: _jsxs(TextLink, { href: wishlistHref, variant: "none", className: `${slotClassName} ${pathname === wishlistHref ? activeClassName : inactiveClassName}`, "aria-label": wishlistCount > 0 ? `${labels.wishlist}, ${wishlistCount} items` : labels.wishlist, children: [_jsxs(Span, { className: "relative", children: [_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { size: "xs", weight: "semibold", className: "absolute -right-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-[var(--appkit-color-error)] px-[var(--appkit-space-1)] leading-none text-[var(--appkit-color-text-on-primary)]", children: wishlistCount > 9 ? "9+" : wishlistCount }))] }), _jsx(Span, { className: labelClassName, children: labels.wishlist })] }) })), _jsx(Li, { className: "flex-1", children: user ? (_jsxs(TextLink, { href: profileHref, variant: "none", className: `${slotClassName} ${pathname === profileHref ? activeClassName : inactiveClassName}`, "aria-label": labels.profile, children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata ||
|
|
39
39
|
(user.photoURL ? { url: user.photoURL, position: { x: 50, y: 50 }, zoom: 1 } : null), size: "sm", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Span, { className: `text-[7px] uppercase leading-none ${getRoleBadgeClass ? getRoleBadgeClass(user.role) : "text-[var(--appkit-color-text-muted)] text-[var(--appkit-color-text-muted)]"}`, weight: "semibold", children: user.role }))] })) : (_jsx(NavItem, { href: loginHref, label: labels.profile, icon: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }), isActive: pathname === loginHref, variant: "vertical", activeClassName: activeClassName, inactiveClassName: inactiveClassName, iconClassName: iconClassName, labelClassName: labelClassName })) })] }));
|
|
40
40
|
}
|
|
@@ -343,7 +343,7 @@ export interface DashboardQuickActionMeta {
|
|
|
343
343
|
}
|
|
344
344
|
export declare const DASHBOARD_QUICK_ACTION_META: Record<DashboardQuickActionId, DashboardQuickActionMeta>;
|
|
345
345
|
export declare const DASHBOARD_QUICK_ACTIONS: {
|
|
346
|
-
readonly admin: readonly ["dqa-admin-add-product", "dqa-admin-add-
|
|
346
|
+
readonly admin: readonly ["dqa-admin-add-product", "dqa-admin-add-coupon", "dqa-admin-users", "dqa-admin-stores", "dqa-admin-orders", "dqa-admin-payouts", "dqa-admin-analytics", "dqa-admin-categories", "dqa-admin-reviews", "dqa-admin-events", "dqa-admin-faqs", "dqa-admin-carousel", "dqa-admin-sections", "dqa-admin-support", "dqa-admin-moderation", "dqa-admin-blog", "dqa-admin-scammers", "dqa-admin-bundles", "dqa-admin-prize-draws", "dqa-admin-tester-checklist", "dqa-admin-settings"];
|
|
347
347
|
readonly seller: readonly ["dqa-seller-add-product", "dqa-seller-products", "dqa-seller-view-orders", "dqa-seller-analytics", "dqa-seller-add-coupon", "dqa-seller-auctions", "dqa-seller-bundles", "dqa-seller-prize-draws", "dqa-seller-offers", "dqa-seller-grouped-listings", "dqa-seller-categories", "dqa-seller-payout-request", "dqa-seller-shipping", "dqa-seller-print-center", "dqa-seller-messages", "dqa-seller-whatsapp", "dqa-seller-reviews", "dqa-seller-storefront", "dqa-seller-settings"];
|
|
348
348
|
readonly user: readonly ["dqa-user-view-orders", "dqa-user-view-wishlist", "dqa-user-edit-profile", "dqa-user-add-address", "dqa-user-recently-viewed", "dqa-user-my-catalogue", "dqa-user-my-coupons"];
|
|
349
349
|
};
|
|
@@ -400,7 +400,7 @@ export const DASHBOARD_QUICK_ACTION_META = {
|
|
|
400
400
|
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT, label: "Add Product", variant: "primary", iconName: "Plus", requiresAuth: true, requiredRole: "admin", requiredPermission: "products.create" },
|
|
401
401
|
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER, label: "Add User", variant: "outline", iconName: "UserPlus", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.users.create" },
|
|
402
402
|
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE, label: "Add Store", variant: "outline", iconName: "Store", requiresAuth: true, requiredRole: "admin", requiredPermission: "admin.stores.create" },
|
|
403
|
-
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON, label: "
|
|
403
|
+
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON, label: "Manage Coupons", variant: "outline", iconName: "Tag", requiresAuth: true, requiredRole: "admin", requiredPermission: "coupons.create" },
|
|
404
404
|
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT, label: "Add Event", variant: "outline", iconName: "Calendar", requiresAuth: true, requiredRole: "admin", requiredPermission: "events.create" },
|
|
405
405
|
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG, label: "New Post", variant: "outline", iconName: "FileText", requiresAuth: true, requiredRole: "admin", requiredPermission: "blog.create" },
|
|
406
406
|
[DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_FAQ]: { id: DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_FAQ, label: "Add FAQ", variant: "outline", iconName: "HelpCircle", requiresAuth: true, requiredRole: "admin", requiredPermission: "faqs.create" },
|
|
@@ -454,11 +454,7 @@ export const DASHBOARD_QUICK_ACTION_META = {
|
|
|
454
454
|
export const DASHBOARD_QUICK_ACTIONS = {
|
|
455
455
|
admin: [
|
|
456
456
|
DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_PRODUCT,
|
|
457
|
-
DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_USER,
|
|
458
|
-
DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_STORE,
|
|
459
457
|
DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_COUPON,
|
|
460
|
-
DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_EVENT,
|
|
461
|
-
DASHBOARD_QUICK_ACTION_ID.ADMIN_ADD_BLOG,
|
|
462
458
|
DASHBOARD_QUICK_ACTION_ID.ADMIN_USERS,
|
|
463
459
|
DASHBOARD_QUICK_ACTION_ID.ADMIN_STORES,
|
|
464
460
|
DASHBOARD_QUICK_ACTION_ID.ADMIN_ORDERS,
|
|
@@ -216,7 +216,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
216
216
|
if (inlineBlurRef.current)
|
|
217
217
|
clearTimeout(inlineBlurRef.current);
|
|
218
218
|
inlineBlurRef.current = setTimeout(() => setIsInlineOpen(false), 120);
|
|
219
|
-
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { rounded: "full", textColor: "faint", type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-[0.125rem] hover:text-[var(--appkit-color-text-muted)] dark:hover:text-[var(--appkit-color-text)] transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0" })), deferred && (_jsx(Button, { rounded: "lg", paddingX: "sm", paddingY: "sm", type: "button", variant: "primary", onClick: handleDeferredSubmit, "aria-label": labels.ariaLabel, className: "flex-shrink-0", children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }) })), isInlineOpen && (filteredQuickLinks.length > 0 || query) && (_jsxs(Div, { rounded: "xl", border: "default", shadow: "lg", className: "absolute top-full left-0 right-0 mt-2 overflow-hidden bg-[var(--appkit-color-surface)] [z-index:var(--appkit-z-dropdown)]", onMouseDown: (event) => event.preventDefault(), children: [filteredQuickLinks.length > 0 && (_jsx(Ul, { children: filteredQuickLinks.map((link, index) => renderInlineQuickLink(link, index)) })), query && suggestionsLoading && (_jsx(Div, { padding: "inline", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })), query &&
|
|
219
|
+
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { rounded: "full", textColor: "faint", type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-[0.125rem] hover:text-[var(--appkit-color-text-muted)] dark:hover:text-[var(--appkit-color-text)] transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0 max-w-[92px] sm:max-w-[150px]" })), deferred && (_jsx(Button, { rounded: "lg", paddingX: "sm", paddingY: "sm", type: "button", variant: "primary", onClick: handleDeferredSubmit, "aria-label": labels.ariaLabel, className: "flex-shrink-0", children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }) })), isInlineOpen && (filteredQuickLinks.length > 0 || query) && (_jsxs(Div, { rounded: "xl", border: "default", shadow: "lg", className: "absolute top-full left-0 right-0 mt-2 overflow-hidden bg-[var(--appkit-color-surface)] [z-index:var(--appkit-z-dropdown)]", onMouseDown: (event) => event.preventDefault(), children: [filteredQuickLinks.length > 0 && (_jsx(Ul, { children: filteredQuickLinks.map((link, index) => renderInlineQuickLink(link, index)) })), query && suggestionsLoading && (_jsx(Div, { padding: "inline", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })), query &&
|
|
220
220
|
suggestions.slice(0, 5).map((suggestion, suggestionIndex) => {
|
|
221
221
|
const itemIndex = inlineQuickLinkItems.length + suggestionIndex;
|
|
222
222
|
const isActive = activeIndex === itemIndex;
|
|
@@ -292,7 +292,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
292
292
|
};
|
|
293
293
|
if (!isOpen)
|
|
294
294
|
return null;
|
|
295
|
-
return (_jsx(Div, { border: "bottom-subtle", className: "animate-[fadeIn_150ms_ease-out]", surface: "default", shadow: "md", children: _jsxs(Div, { className: "container mx-auto md:py-[1rem] relative", padding: "inline", children: [_jsxs(Row, { gap: "sm", className: "md:gap-[0.75rem]", children: [_jsx(Div, { className: "flex-1 relative min-w-0", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0" })), _jsxs(Button, { gap: "md", onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), labels.title] }), _jsx(Button, { rounded: "xl", variant: "ghost", onClick: () => onClose?.(), className: "p-[var(--appkit-space-2-5)] md:p-[var(--appkit-space-3)] transition-colors text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] hover:text-zinc-700 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-[var(--appkit-color-text-muted)] flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (filteredSiteLinks.length > 0 || query) && (_jsxs(Stack, { className: "absolute top-full left-0 right-0 z-50 pt-[0.5rem] pb-[1rem]", padding: "x-md", gap: "sm", children: [filteredSiteLinks.length > 0 && !suggestionsLoading && (_jsxs(Div, { className: `${__O.hidden}`, rounded: "xl", shadow: "lg", surface: "default", border: "default", children: [_jsx(Div, { border: "default", className: "border-b", paddingY: "y-xs", paddingX: "x-md", children: _jsx(Text, { variant: "secondary", size: "xs", className: "uppercase tracking-wider", weight: "semibold", children: labels.quickLinks }) }), _jsx(Ul, { children: filteredSiteLinks.map((link) => {
|
|
295
|
+
return (_jsx(Div, { border: "bottom-subtle", className: "animate-[fadeIn_150ms_ease-out]", surface: "default", shadow: "md", children: _jsxs(Div, { className: "container mx-auto md:py-[1rem] relative", padding: "inline", children: [_jsxs(Row, { gap: "sm", className: "md:gap-[0.75rem]", children: [_jsx(Div, { className: "flex-1 relative min-w-0", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0 max-w-[92px] sm:max-w-[150px]" })), _jsxs(Button, { gap: "md", onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), labels.title] }), _jsx(Button, { rounded: "xl", variant: "ghost", onClick: () => onClose?.(), className: "p-[var(--appkit-space-2-5)] md:p-[var(--appkit-space-3)] transition-colors text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] hover:text-zinc-700 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-[var(--appkit-color-text-muted)] flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (filteredSiteLinks.length > 0 || query) && (_jsxs(Stack, { className: "absolute top-full left-0 right-0 z-50 pt-[0.5rem] pb-[1rem]", padding: "x-md", gap: "sm", children: [filteredSiteLinks.length > 0 && !suggestionsLoading && (_jsxs(Div, { className: `${__O.hidden}`, rounded: "xl", shadow: "lg", surface: "default", border: "default", children: [_jsx(Div, { border: "default", className: "border-b", paddingY: "y-xs", paddingX: "x-md", children: _jsx(Text, { variant: "secondary", size: "xs", className: "uppercase tracking-wider", weight: "semibold", children: labels.quickLinks }) }), _jsx(Ul, { children: filteredSiteLinks.map((link) => {
|
|
296
296
|
const Icon = link.icon;
|
|
297
297
|
const itemIndex = quickLinkItems.findIndex((item) => item.link.href === link.href);
|
|
298
298
|
const isActive = activeIndex === itemIndex;
|