@mohasinac/appkit 3.6.5 → 3.8.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/_internal/client/features/layout/DashboardLayoutClient.d.ts +17 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +7 -2
- package/dist/_internal/client/features/tour/TourProvider.d.ts +5 -2
- package/dist/_internal/client/features/tour/TourProvider.js +125 -12
- package/dist/_internal/client/theme/ThemeProvider.js +1 -1
- package/dist/_internal/server/features/categories/data.js +2 -2
- package/dist/_internal/server/features/faqs/og.js +5 -1
- package/dist/_internal/server/features/reviews/og.js +5 -1
- package/dist/_internal/server/features/seo/manifest.js +2 -1
- package/dist/_internal/server/features/seo/og.js +7 -1
- package/dist/_internal/server/features/tester/visibility.d.ts +13 -0
- package/dist/_internal/server/features/tester/visibility.js +28 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +5 -0
- package/dist/_internal/server/jobs/core/resetOtpVerification.d.ts +17 -0
- package/dist/_internal/server/jobs/core/resetOtpVerification.js +88 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +6 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +35 -0
- package/dist/_internal/server/jobs/handlers/_helpers.d.ts +6 -0
- package/dist/_internal/server/jobs/handlers/_helpers.js +12 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/jobs/handlers/messages.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/messages.js +2 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +56 -0
- package/dist/_internal/shared/tokens/index.d.ts +12 -12
- package/dist/client.d.ts +15 -3
- package/dist/client.js +10 -2
- package/dist/constants/api-endpoints.d.ts +39 -12
- package/dist/constants/api-endpoints.js +17 -8
- package/dist/constants/field-names.d.ts +9 -0
- package/dist/constants/field-names.js +12 -0
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/features/account/components/LinkedAccountsSection.d.ts +11 -0
- package/dist/features/account/components/LinkedAccountsSection.js +11 -0
- package/dist/features/account/components/UserSidebar.d.ts +3 -1
- package/dist/features/account/components/UserSidebar.js +9 -13
- package/dist/features/account/hooks/useCollapsedSections.d.ts +20 -0
- package/dist/features/account/hooks/useCollapsedSections.js +63 -0
- package/dist/features/account/hooks/useProfile.d.ts +8 -0
- package/dist/features/account/index.d.ts +1 -0
- package/dist/features/account/index.js +1 -0
- package/dist/features/admin/components/AdminAddressEditorView.js +2 -2
- package/dist/features/admin/components/AdminBidsView.js +6 -6
- package/dist/features/admin/components/AdminBrandsView.js +2 -2
- package/dist/features/admin/components/AdminCarouselView.js +2 -2
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +3 -3
- package/dist/features/admin/components/AdminProductEditorView.js +5 -2
- package/dist/features/admin/components/AdminReviewsView.js +5 -5
- package/dist/features/admin/components/AdminSectionsView.js +3 -18
- package/dist/features/admin/components/AdminSidebar.js +8 -6
- package/dist/features/admin/components/AdminSiteSettingsView.js +133 -127
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.d.ts +9 -0
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +129 -0
- package/dist/features/admin/components/AdminTesterChecklistView.d.ts +6 -0
- package/dist/features/admin/components/AdminTesterChecklistView.js +93 -0
- package/dist/features/admin/components/AdminUserEditorView.d.ts +3 -1
- package/dist/features/admin/components/AdminUserEditorView.js +7 -1
- package/dist/features/admin/components/AdminUsersView.js +1 -1
- package/dist/features/admin/components/DataListingView.js +2 -2
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +8 -0
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +41 -0
- package/dist/features/admin/components/index.d.ts +6 -0
- package/dist/features/admin/components/index.js +3 -0
- package/dist/features/admin/schemas/firestore.d.ts +0 -7
- package/dist/features/admin/schemas/firestore.js +6 -6
- package/dist/features/analytics/components/PageViewTracker.d.ts +12 -0
- package/dist/features/analytics/components/PageViewTracker.js +22 -0
- package/dist/features/analytics/repository/page-views.repository.d.ts +32 -0
- package/dist/features/analytics/repository/page-views.repository.js +43 -0
- package/dist/features/analytics/types.d.ts +6 -0
- package/dist/features/analytics/types.js +13 -0
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts +12 -0
- package/dist/features/auth/hooks/useAuth.js +109 -0
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/repository/session.repository.d.ts +2 -0
- package/dist/features/auth/repository/session.repository.js +4 -1
- package/dist/features/auth/schemas/firestore.d.ts +17 -1
- package/dist/features/auth/schemas/firestore.js +2 -0
- package/dist/features/blog/components/BlogIndexListing.js +2 -2
- package/dist/features/blog/schemas/firestore.d.ts +4 -0
- package/dist/features/cart/actions/cart-actions.js +20 -2
- package/dist/features/categories/api/route.js +2 -2
- package/dist/features/categories/components/CategoriesIndexListing.js +3 -3
- package/dist/features/categories/components/CategoryBundlesListing.js +2 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +3 -1
- package/dist/features/categories/components/CategoryProductsListing.js +2 -2
- package/dist/features/categories/schemas/firestore.d.ts +4 -0
- package/dist/features/classified/components/ClassifiedIndexListing.js +2 -2
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +2 -2
- package/dist/features/email/primitives.d.ts +2 -2
- package/dist/features/email/primitives.js +2 -2
- package/dist/features/events/components/EventsIndexListing.js +2 -2
- package/dist/features/events/repository/event-entry.repository.js +1 -0
- package/dist/features/events/schemas/firestore.d.ts +7 -1
- package/dist/features/events/schemas/firestore.js +1 -0
- package/dist/features/layout/BackToTop.d.ts +6 -1
- package/dist/features/layout/BackToTop.js +31 -6
- package/dist/features/layout/BottomNavbar.js +1 -1
- package/dist/features/layout/NavItem.js +1 -1
- package/dist/features/layout/NavbarLayout.js +2 -2
- package/dist/features/live/components/LiveItemsIndexListing.js +2 -2
- package/dist/features/media/MediaVideo.js +5 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +2 -2
- package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -2
- package/dist/features/products/components/ProductsIndexListing.js +2 -2
- package/dist/features/products/constants/action-defs.d.ts +2 -0
- package/dist/features/products/constants/action-defs.js +2 -0
- package/dist/features/products/constants/sieve.d.ts +0 -6
- package/dist/features/products/constants/sieve.js +4 -2
- package/dist/features/products/repository/products.repository.d.ts +20 -0
- package/dist/features/products/repository/products.repository.js +5 -0
- package/dist/features/products/schemas/firestore.d.ts +4 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +18 -5
- package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
- package/dist/features/search/hooks/useNavSuggestions.js +20 -1
- package/dist/features/seller/actions/seller-actions.js +11 -6
- package/dist/features/seller/components/SellerAddressesView.js +2 -2
- package/dist/features/seller/components/SellerBidsView.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +1 -1
- package/dist/features/seller/components/SellerOrdersView.js +2 -2
- package/dist/features/seller/components/SellerProductsView.js +2 -2
- package/dist/features/seller/components/SellerSidebar.d.ts +3 -1
- package/dist/features/seller/components/SellerSidebar.js +9 -8
- package/dist/features/seller/components/SellerStoreCategoriesView.js +2 -2
- package/dist/features/seller/components/SellerTemplatesView.js +2 -2
- package/dist/features/shell/StepForm.js +4 -4
- package/dist/features/stores/actions/store-query-actions.d.ts +5 -4
- package/dist/features/stores/actions/store-query-actions.js +11 -8
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -2
- package/dist/features/stores/components/StoreClassifiedsListing.js +2 -2
- package/dist/features/stores/components/StoreDetailLayoutView.js +1 -1
- package/dist/features/stores/components/StoreDigitalCodesListing.js +2 -2
- package/dist/features/stores/components/StoreLiveItemsListing.js +2 -2
- package/dist/features/stores/components/StorePreOrdersListing.js +2 -2
- package/dist/features/stores/components/StoreProductsListing.js +2 -2
- package/dist/features/stores/components/StoreReviewsListing.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +2 -2
- package/dist/features/stores/repository/store.repository.js +3 -0
- package/dist/features/stores/schemas/firestore.d.ts +4 -0
- package/dist/features/tester/actions/checklist-item-actions.d.ts +69 -0
- package/dist/features/tester/actions/checklist-item-actions.js +42 -0
- package/dist/features/tester/actions/index.d.ts +1 -0
- package/dist/features/tester/actions/index.js +1 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +22 -0
- package/dist/features/tester/components/AdminTesterFeedbackListView.d.ts +3 -0
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +100 -0
- package/dist/features/tester/components/AdminTesterFeedbackReportView.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackReportView.js +30 -0
- package/dist/features/tester/components/AdminTesterFeedbackView.d.ts +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackView.js +29 -0
- package/dist/features/tester/components/TesterChecklistStepRow.d.ts +18 -0
- package/dist/features/tester/components/TesterChecklistStepRow.js +29 -0
- package/dist/features/tester/components/TesterFeedbackChart.d.ts +14 -0
- package/dist/features/tester/components/TesterFeedbackChart.js +18 -0
- package/dist/features/tester/components/TesterHubView.d.ts +5 -0
- package/dist/features/tester/components/TesterHubView.js +71 -0
- package/dist/features/tester/components/index.d.ts +11 -0
- package/dist/features/tester/components/index.js +7 -0
- package/dist/features/tester/index.d.ts +2 -0
- package/dist/features/tester/index.js +2 -0
- package/dist/features/tester/repository/index.d.ts +2 -0
- package/dist/features/tester/repository/index.js +2 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +13 -0
- package/dist/features/tester/repository/tester-checklist-item.repository.js +55 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +50 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.js +196 -0
- package/dist/features/tester/schemas/firestore.d.ts +85 -0
- package/dist/features/tester/schemas/firestore.js +93 -0
- package/dist/features/tester/schemas/index.d.ts +1 -0
- package/dist/features/tester/schemas/index.js +1 -0
- package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/blog-tester-seed-data.js +40 -0
- package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/categories-tester-seed-data.js +177 -0
- package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/events-tester-seed-data.js +50 -0
- package/dist/features/tester/seed-data/index.d.ts +6 -0
- package/dist/features/tester/seed-data/index.js +6 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.js +151 -0
- package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/stores-tester-seed-data.js +40 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +248 -0
- package/dist/features/tester/seed-data/tester-ttl.d.ts +4 -0
- package/dist/features/tester/seed-data/tester-ttl.js +6 -0
- package/dist/features/tester/server.d.ts +9 -0
- package/dist/features/tester/server.js +8 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.js +51 -1
- package/dist/next/api/api-response.js +5 -1
- package/dist/next/api/routeHandler.js +10 -1
- package/dist/next/middleware/chain.js +1 -1
- package/dist/next/routing/route-map.d.ts +6 -7
- package/dist/next/routing/route-map.js +3 -7
- package/dist/providers/payment-manual/index.d.ts +2 -2
- package/dist/providers/payment-manual/index.js +2 -2
- package/dist/react/contexts/SessionContext.d.ts +4 -0
- package/dist/react/contexts/SessionContext.js +4 -0
- package/dist/repositories/index.d.ts +4 -0
- package/dist/repositories/index.js +3 -0
- package/dist/security/pii-encrypt.js +12 -2
- package/dist/seed/addresses-seed-data.js +1 -1
- package/dist/seed/bids-seed-data.js +64 -185
- package/dist/seed/blog-posts-seed-data.js +1 -1
- package/dist/seed/carousel-slides-seed-data.js +22 -16
- package/dist/seed/carousels-seed-data.js +1 -1
- package/dist/seed/cart-seed-data.js +1 -1
- package/dist/seed/categories-seed-data.js +1 -1
- package/dist/seed/claimed-coupons-seed-data.js +1 -1
- package/dist/seed/conversations-seed-data.js +1 -1
- package/dist/seed/coupon-usage-seed-data.js +1 -1
- package/dist/seed/coupons-seed-data.js +1 -1
- package/dist/seed/events-seed-data.js +1 -1
- package/dist/seed/faq-seed-data.js +119 -3
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
- package/dist/seed/grouped-listings-seed-data.js +1 -1
- package/dist/seed/history-seed-data.js +1 -1
- package/dist/seed/homepage-sections-seed-data.js +1 -1
- package/dist/seed/index.d.ts +2 -2
- package/dist/seed/index.js +7 -3
- package/dist/seed/lottery-entries-seed-data.js +1 -1
- package/dist/seed/manifest.d.ts +4 -3
- package/dist/seed/manifest.js +13 -6
- package/dist/seed/notifications-seed-data.js +1 -1
- package/dist/seed/offers-seed-data.js +1 -1
- package/dist/seed/orders-seed-data.js +1 -1
- package/dist/seed/payouts-seed-data.js +1 -1
- package/dist/seed/products-art-seed-data.js +1 -1
- package/dist/seed/products-auctions-seed-data.js +1 -1
- package/dist/seed/products-classifieds-seed-data.js +1 -1
- package/dist/seed/products-digital-codes-seed-data.js +1 -1
- package/dist/seed/products-live-items-seed-data.js +1 -1
- package/dist/seed/products-preorders-seed-data.d.ts +2 -0
- package/dist/seed/products-preorders-seed-data.js +1 -1
- package/dist/seed/products-prize-draws-seed-data.js +1 -1
- package/dist/seed/products-standard-seed-data.js +1 -1
- package/dist/seed/products-stickers-seed-data.js +1 -1
- package/dist/seed/reviews-seed-data.js +1 -1
- package/dist/seed/scammers-seed-data.js +1 -1
- package/dist/seed/sessions-seed-data.js +1 -1
- package/dist/seed/site-settings-seed-data.js +22 -16
- package/dist/seed/store-addresses-seed-data.js +1 -1
- package/dist/seed/store-extensions-seed-data.js +1 -1
- package/dist/seed/stores-seed-data.js +1 -1
- package/dist/seed/support-tickets-seed-data.js +1 -1
- package/dist/seed/tester-checklist-seed-data.d.ts +2 -0
- package/dist/seed/tester-checklist-seed-data.js +248 -0
- package/dist/seed/users-seed-data.js +1 -1
- package/dist/seed/wishlists-seed-data.js +1 -1
- package/dist/server.d.ts +7 -2
- package/dist/server.js +19 -12
- package/dist/styles.css +181 -165
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/color-pairs.d.ts +46 -0
- package/dist/tokens/color-pairs.js +93 -0
- package/dist/tokens/index.d.ts +60 -60
- package/dist/tokens/index.js +30 -30
- package/dist/tokens/themes/cobalt-night.d.ts +3 -2
- package/dist/tokens/themes/cobalt-night.js +3 -2
- package/dist/tokens/themes/default-dark.d.ts +4 -1
- package/dist/tokens/themes/default-dark.js +53 -50
- package/dist/tokens/themes/default-light.d.ts +5 -1
- package/dist/tokens/themes/default-light.js +48 -44
- package/dist/tokens/themes/sunset.js +4 -4
- package/dist/tokens/tokens.css +128 -122
- package/dist/ui/components/CollapsibleSection.d.ts +15 -0
- package/dist/ui/components/CollapsibleSection.js +13 -0
- package/dist/ui/components/ListingToolbar.js +3 -2
- package/dist/ui/components/Semantic.style.css +3 -3
- package/dist/ui/components/SideDrawer.style.css +19 -19
- package/dist/ui/components/Slider.js +1 -1
- package/dist/ui/components/StickyToolbar.d.ts +36 -7
- package/dist/ui/components/StickyToolbar.js +63 -5
- package/dist/ui/components/Tabs.js +17 -1
- package/dist/ui/components/Tabs.style.css +10 -0
- package/dist/ui/components/Typography.js +7 -0
- package/dist/ui/components/surface-tokens.d.ts +0 -7
- package/dist/ui/components/surface-tokens.js +7 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/rich-text/RichText.style.css +20 -20
- package/dist/utils/id-generators.d.ts +13 -0
- package/dist/utils/id-generators.js +17 -0
- package/package.json +1 -1
- package/scripts/seed-cli-loader.mjs +15 -0
- package/scripts/seed-cli.mjs +71 -17
|
@@ -6,7 +6,12 @@ export interface BackToTopProps {
|
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
8
|
* BackToTop — floating scroll-to-top button.
|
|
9
|
-
* Appears after the page has scrolled past `threshold` pixels.
|
|
9
|
+
* Appears after the page has scrolled past `threshold` pixels. A small
|
|
10
|
+
* dismiss control lets the user hide it for the rest of the browser
|
|
11
|
+
* session (sessionStorage) — on narrow viewports it can sit close enough
|
|
12
|
+
* to the bottom nav / other floating controls to feel like clutter, so it
|
|
13
|
+
* needs the same "get out of my way" escape hatch as StickyToolbar.
|
|
14
|
+
* Re-appears on the next page load.
|
|
10
15
|
*/
|
|
11
16
|
export declare function BackToTop({ threshold, ariaLabel, className, }: BackToTopProps): import("react").JSX.Element | null;
|
|
12
17
|
/** SkipToMain — visually hidden link that becomes visible on focus for keyboard users. */
|
|
@@ -1,23 +1,48 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import Link from "next/link";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
|
-
import { ChevronUp } from "lucide-react";
|
|
6
|
-
import { Button } from "../../ui";
|
|
5
|
+
import { ChevronUp, X } from "lucide-react";
|
|
6
|
+
import { Button, Div } from "../../ui";
|
|
7
|
+
import { normalizeError } from "../../errors/normalize";
|
|
8
|
+
const DISMISSED_KEY = "appkit:back-to-top-dismissed";
|
|
7
9
|
/**
|
|
8
10
|
* BackToTop — floating scroll-to-top button.
|
|
9
|
-
* Appears after the page has scrolled past `threshold` pixels.
|
|
11
|
+
* Appears after the page has scrolled past `threshold` pixels. A small
|
|
12
|
+
* dismiss control lets the user hide it for the rest of the browser
|
|
13
|
+
* session (sessionStorage) — on narrow viewports it can sit close enough
|
|
14
|
+
* to the bottom nav / other floating controls to feel like clutter, so it
|
|
15
|
+
* needs the same "get out of my way" escape hatch as StickyToolbar.
|
|
16
|
+
* Re-appears on the next page load.
|
|
10
17
|
*/
|
|
11
18
|
export function BackToTop({ threshold = 400, ariaLabel = "Back to top", className = "", }) {
|
|
12
19
|
const [visible, setVisible] = useState(false);
|
|
20
|
+
const [dismissed, setDismissed] = useState(false);
|
|
13
21
|
useEffect(() => {
|
|
22
|
+
try {
|
|
23
|
+
setDismissed(window.sessionStorage.getItem(DISMISSED_KEY) === "1");
|
|
24
|
+
}
|
|
25
|
+
catch (_err) {
|
|
26
|
+
void normalizeError(_err);
|
|
27
|
+
// sessionStorage unavailable — never dismissed for this render.
|
|
28
|
+
}
|
|
14
29
|
const handleScroll = () => setVisible(window.scrollY > threshold);
|
|
15
30
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
16
31
|
return () => window.removeEventListener("scroll", handleScroll);
|
|
17
32
|
}, [threshold]);
|
|
18
|
-
if (!visible)
|
|
33
|
+
if (!visible || dismissed)
|
|
19
34
|
return null;
|
|
20
|
-
|
|
35
|
+
const handleDismiss = () => {
|
|
36
|
+
setDismissed(true);
|
|
37
|
+
try {
|
|
38
|
+
window.sessionStorage.setItem(DISMISSED_KEY, "1");
|
|
39
|
+
}
|
|
40
|
+
catch (_err) {
|
|
41
|
+
void normalizeError(_err);
|
|
42
|
+
// sessionStorage unavailable — dismissal still applies for this render.
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return (_jsxs(Div, { layout: "flex", align: "center", gap: "1", className: `fixed bottom-[calc(var(--keyboard-inset-height,0px)+var(--bottom-nav-height,4rem))] right-4 z-[var(--appkit-z-bottom-nav)] ${className}`, children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": ariaLabel, onClick: () => window.scrollTo({ top: 0, behavior: "smooth" }), className: "flex items-center justify-center w-10 h-10 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] dark:hover:bg-slate-700 transition-colors", children: _jsx(ChevronUp, { className: "w-5 h-5", "aria-hidden": "true" }) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", "aria-label": "Hide back-to-top button", onClick: handleDismiss, className: "flex items-center justify-center w-6 h-6 rounded-full bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)] shadow-md text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text)] transition-colors", children: _jsx(X, { className: "w-3 h-3", "aria-hidden": "true" }) })] }));
|
|
21
46
|
}
|
|
22
47
|
/** SkipToMain — visually hidden link that becomes visible on focus for keyboard users. */
|
|
23
48
|
export function SkipToMain({ mainId = "main-content" }) {
|
|
@@ -23,7 +23,7 @@ export function BottomNavbar({ user, userId, homeHref, shopHref, cartHref, wishl
|
|
|
23
23
|
profile: "Profile",
|
|
24
24
|
more: "More",
|
|
25
25
|
};
|
|
26
|
-
const slotClassName = "relative flex h-full w-full flex-col items-
|
|
26
|
+
const slotClassName = "relative flex h-full w-full flex-col items-center justify-center gap-[var(--appkit-space-1)] text-center transition-colors duration-200";
|
|
27
27
|
const defaultIcon = (_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: "M4 6h16M4 12h16M4 18h16" }) }));
|
|
28
28
|
if (navItems && navItems.length > 0) {
|
|
29
29
|
// When a wishlist href is available, trade one nav-item slot for a
|
|
@@ -9,7 +9,7 @@ export function NavItem({ href, label, icon, isActive = false, variant = "horizo
|
|
|
9
9
|
const stateClass = isActive ? activeClassName : inactiveClassName;
|
|
10
10
|
if (variant === "vertical") {
|
|
11
11
|
return (_jsxs(Link, { href: href, className: [
|
|
12
|
-
"relative flex h-full w-full flex-col items-
|
|
12
|
+
"relative flex h-full w-full flex-col items-center justify-center gap-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-center transition-colors",
|
|
13
13
|
stateClass,
|
|
14
14
|
className,
|
|
15
15
|
]
|
|
@@ -49,7 +49,7 @@ export function NavbarLayout({ items, activeHref, id = "main-navbar", ariaLabel
|
|
|
49
49
|
}, [updateArrows, items]);
|
|
50
50
|
const scroll = (dir) => scrollRef.current?.scrollBy({ left: dir === "left" ? -240 : 240, behavior: "smooth" });
|
|
51
51
|
if (inline) {
|
|
52
|
-
return (_jsx(Ul, { "aria-label": ariaLabel, className: "hidden xl:flex items-center justify-
|
|
52
|
+
return (_jsx(Ul, { "aria-label": ariaLabel, className: "hidden xl:flex items-center justify-center gap-[var(--appkit-space-0-5)] xl:gap-[var(--appkit-space-1)] w-full", children: items.map((item) => (_jsx(Li, { children: renderItem ? (renderItem(item, activeHref === item.href)) : (_jsx(DefaultNavItem, { item: item, isActive: activeHref === item.href })) }, item.href))) }));
|
|
53
53
|
}
|
|
54
|
-
return (_jsx(Nav, { border: "subtle", id: id, "aria-label": ariaLabel, className: "hidden lg:block bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b", children: _jsxs(Row, { className: "container mx-auto sm:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)] max-w-[1920px] h-10", padding: "x-md", align: "center", children: [_jsxs(Div, { className: `relative flex-1 ${__O.hidden}`, children: [canScrollLeft && (_jsx(NavbarChevron, { direction: "left", onClick: () => scroll("left") })), _jsx(Ul, { ref: scrollRef, className: "flex items-center justify-
|
|
54
|
+
return (_jsx(Nav, { border: "subtle", id: id, "aria-label": ariaLabel, className: "hidden lg:block bg-[var(--appkit-color-bg)]/95 backdrop-blur-md border-b", children: _jsxs(Row, { className: "container mx-auto sm:px-[var(--appkit-space-6)] lg:px-[var(--appkit-space-8)] max-w-[1920px] h-10", padding: "x-md", align: "center", children: [_jsxs(Div, { className: `relative flex-1 ${__O.hidden}`, children: [canScrollLeft && (_jsx(NavbarChevron, { direction: "left", onClick: () => scroll("left") })), _jsx(Ul, { ref: scrollRef, className: "flex items-center justify-center gap-[var(--appkit-space-0-5)] lg:gap-[var(--appkit-space-1)] overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]", children: items.map((item) => (_jsx(Li, { className: "shrink-0", children: renderItem ? (renderItem(item, activeHref === item.href)) : (_jsx(DefaultNavItem, { item: item, isActive: activeHref === item.href })) }, item.href))) }), canScrollRight && (_jsx(NavbarChevron, { direction: "right", onClick: () => scroll("right") }))] }), rightSlot && _jsx(Div, { className: "shrink-0 ml-2", children: rightSlot })] }) }));
|
|
55
55
|
}
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
6
|
-
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast } from "../../../ui";
|
|
6
|
+
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast, StickyToolbar } from "../../../ui";
|
|
7
7
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
8
8
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
9
9
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
@@ -111,5 +111,5 @@ export function LiveItemsIndexListing({ initialData }) {
|
|
|
111
111
|
useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] } } : {});
|
|
112
112
|
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live items\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
|
|
113
113
|
commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
|
|
114
|
-
setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx(
|
|
114
|
+
setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No live item listings found." })) : (_jsx(Div, { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)] rounded-xl border border-[var(--appkit-color-border-subtle)]" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.LIVE_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(LiveItemFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
115
115
|
}
|
|
@@ -40,7 +40,11 @@ export function MediaVideo({ src, thumbnailUrl, alt = "Video", controls = true,
|
|
|
40
40
|
? null
|
|
41
41
|
: (watermark ?? siteSettings?.watermark ?? null);
|
|
42
42
|
if (!resolvedSrc) {
|
|
43
|
-
return (_jsx(Row
|
|
43
|
+
return (_jsx(Row
|
|
44
|
+
// audit-color-pair-function-ok: deliberate faded placeholder-icon shade, not a readability regression
|
|
45
|
+
, {
|
|
46
|
+
// audit-color-pair-function-ok: deliberate faded placeholder-icon shade, not a readability regression
|
|
47
|
+
className: "absolute inset-0 text-zinc-400 text-[length:var(--appkit-text-4xl)]", surface: "subtle", align: "center", justify: "center", role: "img", "aria-label": alt, children: _jsx(Span, { "aria-hidden": "true", children: "\uD83C\uDFAC" }) }));
|
|
44
48
|
}
|
|
45
49
|
return (_jsxs(_Fragment, { children: [_jsx("video", { ref: videoRef, src: resolvedSrc, poster: resolvedPoster, controls: controls, autoPlay: autoPlayMuted, muted: autoPlayMuted, loop: loop, playsInline: true, "aria-label": alt, className: `absolute inset-0 w-full h-full ${fitClass}` }), effectiveWatermark ? _jsx(MediaVideoWatermarkLayer, { config: effectiveWatermark }) : null] }));
|
|
46
50
|
}
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { Columns, Heart, ShoppingCart } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useProducts } from "../../products/hooks/useProducts";
|
|
7
|
-
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast } from "../../../ui";
|
|
7
|
+
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
9
9
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
10
10
|
import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../../products/constants/action-defs";
|
|
@@ -222,7 +222,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
|
|
|
222
222
|
setCompareIds(ids);
|
|
223
223
|
},
|
|
224
224
|
},
|
|
225
|
-
] }), totalPages > 1 && (_jsx(
|
|
225
|
+
] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No pre-orders found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx(Div, { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
|
|
226
226
|
setCompareIds([]);
|
|
227
227
|
selection.clearSelection();
|
|
228
228
|
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, useCallback } from "react";
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { useProducts } from "../hooks/useProducts";
|
|
6
|
-
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast } from "../../../ui";
|
|
6
|
+
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
|
|
7
7
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
8
8
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
9
9
|
import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
|
|
@@ -155,5 +155,5 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
|
|
|
155
155
|
selection.clearSelection();
|
|
156
156
|
},
|
|
157
157
|
},
|
|
158
|
-
] }), totalPages > 1 && (_jsx(
|
|
158
|
+
] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-[var(--appkit-space-4)]" : gridClass, wishlistActions: wishlistActions })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
159
159
|
}
|
|
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
|
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
6
6
|
import { useProducts } from "../hooks/useProducts";
|
|
7
|
-
import { Div, FilterDrawer, Label, ListingToolbar, Pagination, Row, Select, Stack, Text } from "../../../ui";
|
|
7
|
+
import { Div, FilterDrawer, Label, ListingToolbar, Pagination, Row, Select, Stack, Text, StickyToolbar } from "../../../ui";
|
|
8
8
|
import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
|
|
9
9
|
import { useBrands } from "../hooks/useBrands";
|
|
10
10
|
import { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
|
|
@@ -88,7 +88,7 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
|
|
|
88
88
|
table.set(TABLE_KEYS.VIEW, v);
|
|
89
89
|
}, onResetAll: resetAll, hasActiveState: hasActiveState, toggles: [
|
|
90
90
|
{ label: "Show closed", active: showClosed, onChange: (next) => table.set(TABLE_KEYS.SHOW_CLOSED, next ? "true" : "") },
|
|
91
|
-
] }), totalPages > 1 && (_jsx(
|
|
91
|
+
] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No prize draws found." })) : (_jsx(Div, { className: gridClass, children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsxs(Stack, { gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "prizeRevealStatusFilter", size: "xs", weight: "semibold", color: "muted", className: "block mb-1.5", children: "Reveal status" }), _jsx(Select, { id: "prizeRevealStatusFilter", options: [
|
|
92
92
|
{ value: "", label: "Any" },
|
|
93
93
|
{ value: "pending", label: "Reveal pending" },
|
|
94
94
|
{ value: "open", label: "Reveal open" },
|
|
@@ -5,7 +5,7 @@ import { ShoppingCart, Heart, Columns } from "lucide-react";
|
|
|
5
5
|
import { useRouter } from "next/navigation";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useProducts } from "../hooks/useProducts";
|
|
8
|
-
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
|
|
9
9
|
import { usePendingTable } from "../../../react/hooks/usePendingTable";
|
|
10
10
|
import { useAuthGate } from "../../../react/hooks/useAuthGate";
|
|
11
11
|
import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../constants/action-defs";
|
|
@@ -239,7 +239,7 @@ export function ProductsIndexListing({ initialData }) {
|
|
|
239
239
|
setCompareIds(ids);
|
|
240
240
|
},
|
|
241
241
|
},
|
|
242
|
-
] }), totalPages > 1 && (_jsx(
|
|
242
|
+
] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
|
|
243
243
|
setCompareIds([]);
|
|
244
244
|
selection.clearSelection();
|
|
245
245
|
}, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions, featureOptions: featureOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
|
|
@@ -345,6 +345,8 @@ export declare const ADMIN_BULK_ACTIONS: {
|
|
|
345
345
|
readonly deals: readonly ["row-remove"];
|
|
346
346
|
readonly featured: readonly ["row-remove"];
|
|
347
347
|
readonly shipments: readonly ["row-mark-received", "row-delete"];
|
|
348
|
+
readonly testerChecklistItems: readonly ["row-delete"];
|
|
349
|
+
readonly testerFeedback: readonly ["row-mark-read"];
|
|
348
350
|
};
|
|
349
351
|
export declare const SELLER_BULK_ACTIONS: {
|
|
350
352
|
readonly products: readonly ["row-publish", "row-archive", "row-delete"];
|
|
@@ -445,6 +445,8 @@ export const ADMIN_BULK_ACTIONS = {
|
|
|
445
445
|
deals: [ROW_ACTION_ID.REMOVE],
|
|
446
446
|
featured: [ROW_ACTION_ID.REMOVE],
|
|
447
447
|
shipments: [ROW_ACTION_ID.MARK_RECEIVED, ROW_ACTION_ID.DELETE],
|
|
448
|
+
testerChecklistItems: [ROW_ACTION_ID.DELETE],
|
|
449
|
+
testerFeedback: [ROW_ACTION_ID.MARK_READ],
|
|
448
450
|
};
|
|
449
451
|
// Seller bulk actions per listing entity
|
|
450
452
|
export const SELLER_BULK_ACTIONS = {
|
|
@@ -153,12 +153,6 @@ export declare const BUNDLE_SORT_OPTIONS: readonly [...({
|
|
|
153
153
|
}, {
|
|
154
154
|
readonly value: string;
|
|
155
155
|
readonly label: "Price: High to Low";
|
|
156
|
-
}, {
|
|
157
|
-
readonly value: string;
|
|
158
|
-
readonly label: "Most Savings";
|
|
159
|
-
}, {
|
|
160
|
-
readonly value: string;
|
|
161
|
-
readonly label: "Most Items";
|
|
162
156
|
}];
|
|
163
157
|
export declare const PRIZE_DRAW_SORT_OPTIONS: readonly [{
|
|
164
158
|
readonly value: string;
|
|
@@ -76,11 +76,13 @@ export const PREORDER_SORT_OPTIONS = [
|
|
|
76
76
|
// ---------------------------------------------------------------------------
|
|
77
77
|
// Bundles
|
|
78
78
|
// ---------------------------------------------------------------------------
|
|
79
|
+
// "Most Savings" / "Most Items" sorts were removed (2026-08-17) — they
|
|
80
|
+
// referenced `savingsAmount`/`bundleItemCount`, fields that were never added
|
|
81
|
+
// to ProductDocument, so the sorts silently no-opped. Re-add once those
|
|
82
|
+
// fields are computed and written at bundle create/update time.
|
|
79
83
|
export const BUNDLE_SORT_OPTIONS = [
|
|
80
84
|
...BASE_TIME_SORTS.slice(0, 1), // Newest First
|
|
81
85
|
...BASE_PRICE_SORTS,
|
|
82
|
-
{ value: sortBy("savingsAmount", "DESC"), label: "Most Savings" },
|
|
83
|
-
{ value: sortBy("bundleItemCount", "DESC"), label: "Most Items" },
|
|
84
86
|
];
|
|
85
87
|
// ---------------------------------------------------------------------------
|
|
86
88
|
// Prize Draws
|
|
@@ -123,6 +123,14 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
123
123
|
canFilter: boolean;
|
|
124
124
|
canSort: boolean;
|
|
125
125
|
};
|
|
126
|
+
isSold: {
|
|
127
|
+
canFilter: boolean;
|
|
128
|
+
canSort: boolean;
|
|
129
|
+
};
|
|
130
|
+
isPartOfBundle: {
|
|
131
|
+
canFilter: boolean;
|
|
132
|
+
canSort: boolean;
|
|
133
|
+
};
|
|
126
134
|
createdAt: {
|
|
127
135
|
canFilter: boolean;
|
|
128
136
|
canSort: boolean;
|
|
@@ -135,6 +143,18 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
135
143
|
canFilter: boolean;
|
|
136
144
|
canSort: boolean;
|
|
137
145
|
};
|
|
146
|
+
prizeRevealWindowStart: {
|
|
147
|
+
canFilter: boolean;
|
|
148
|
+
canSort: boolean;
|
|
149
|
+
};
|
|
150
|
+
prizeRevealWindowEnd: {
|
|
151
|
+
canFilter: boolean;
|
|
152
|
+
canSort: boolean;
|
|
153
|
+
};
|
|
154
|
+
prizeRevealStatus: {
|
|
155
|
+
canFilter: boolean;
|
|
156
|
+
canSort: boolean;
|
|
157
|
+
};
|
|
138
158
|
startingBid: {
|
|
139
159
|
canFilter: boolean;
|
|
140
160
|
canSort: boolean;
|
|
@@ -479,9 +479,14 @@ ProductRepository.SIEVE_FIELDS = {
|
|
|
479
479
|
viewCount: { canFilter: true, canSort: true },
|
|
480
480
|
currentBid: { canFilter: true, canSort: true },
|
|
481
481
|
bidCount: { canFilter: true, canSort: true },
|
|
482
|
+
isSold: { canFilter: true, canSort: true },
|
|
483
|
+
isPartOfBundle: { canFilter: true, canSort: false },
|
|
482
484
|
createdAt: { canFilter: true, canSort: true },
|
|
483
485
|
updatedAt: { canFilter: true, canSort: true },
|
|
484
486
|
auctionEndDate: { canFilter: true, canSort: true },
|
|
487
|
+
prizeRevealWindowStart: { canFilter: true, canSort: true },
|
|
488
|
+
prizeRevealWindowEnd: { canFilter: true, canSort: true },
|
|
489
|
+
prizeRevealStatus: { canFilter: true, canSort: false },
|
|
485
490
|
startingBid: { canFilter: true, canSort: true },
|
|
486
491
|
buyNowPrice: { canFilter: true, canSort: true },
|
|
487
492
|
minBidIncrement: { canFilter: true, canSort: false },
|
|
@@ -124,6 +124,10 @@ export interface ProductDocument extends BaseDocument {
|
|
|
124
124
|
title: string;
|
|
125
125
|
description: string;
|
|
126
126
|
slug?: string;
|
|
127
|
+
/** Tester sandbox flag — set on shared admin-seeded test products; swept by testerSandboxCleanup. */
|
|
128
|
+
isTestData?: boolean;
|
|
129
|
+
/** When isTestData, the cutoff after which testerSandboxCleanup deletes this doc (cascades to bids). */
|
|
130
|
+
testDataExpiresAt?: Date;
|
|
127
131
|
seoTitle?: string;
|
|
128
132
|
seoDescription?: string;
|
|
129
133
|
seoKeywords?: string[];
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
4
|
import { Search, SlidersHorizontal, X } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
-
import { Button, DateInput, IconButton, Input, ListingFilterDrawer, Pagination, RadioGroup, SortDropdown, Div, Grid, Row, Span, Stack, Text, Heading } from "../../../ui";
|
|
6
|
+
import { Button, DateInput, IconButton, Input, ListingFilterDrawer, Pagination, RadioGroup, SortDropdown, Div, Grid, Row, Span, Stack, Text, Heading, StickyToolbar } from "../../../ui";
|
|
7
7
|
import { usePromotions } from "../hooks/usePromotions";
|
|
8
8
|
import { CouponCard } from "./CouponCard";
|
|
9
9
|
import { TABLE_KEYS } from "../../../constants/table-keys";
|
|
@@ -53,9 +53,10 @@ export function CouponsIndexListing({ initialCoupons, storeSlug: _storeSlug, sto
|
|
|
53
53
|
const clearPending = useCallback(() => {
|
|
54
54
|
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
55
55
|
}, []);
|
|
56
|
-
// Build Sieve filter string from committed URL table values
|
|
56
|
+
// Build Sieve filter string from committed URL table values. `validity.isActive==true`
|
|
57
|
+
// is enforced server-side by /api/coupons itself — don't duplicate it here.
|
|
57
58
|
const buildFilters = () => {
|
|
58
|
-
const parts = [
|
|
59
|
+
const parts = [];
|
|
59
60
|
const typeFilter = table.get(TABLE_KEYS.TYPE);
|
|
60
61
|
if (typeFilter)
|
|
61
62
|
parts.push(`${COUPON_FIELDS.TYPE}==${typeFilter}`);
|
|
@@ -76,11 +77,23 @@ export function CouponsIndexListing({ initialCoupons, storeSlug: _storeSlug, sto
|
|
|
76
77
|
filters: buildFilters(),
|
|
77
78
|
});
|
|
78
79
|
// Use initial data on first load if available and no search/filter active
|
|
79
|
-
const
|
|
80
|
+
const baseCoupons = !isLoading && coupons.length > 0
|
|
80
81
|
? coupons
|
|
81
82
|
: !isLoading && initialCoupons && !table.get(TABLE_KEYS.QUERY) && !table.get(TABLE_KEYS.TYPE)
|
|
82
83
|
? initialCoupons
|
|
83
84
|
: coupons;
|
|
85
|
+
// The coupons dataset is always small (a handful to a few dozen per scope), and
|
|
86
|
+
// neither the sieve schema nor the listingProcessor "coupons" collection support
|
|
87
|
+
// substring/full-text search server-side — so the toolbar search filters the
|
|
88
|
+
// already-fetched page client-side against code/name/description instead of
|
|
89
|
+
// silently doing nothing (the search input previously wrote to the URL but was
|
|
90
|
+
// never read by buildFilters()).
|
|
91
|
+
const committedQuery = table.get(TABLE_KEYS.QUERY).trim().toLowerCase();
|
|
92
|
+
const displayCoupons = committedQuery
|
|
93
|
+
? baseCoupons.filter((c) => [c.code, c.name, c.description]
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.some((field) => field.toLowerCase().includes(committedQuery)))
|
|
96
|
+
: baseCoupons;
|
|
84
97
|
const commitSearch = useCallback(() => {
|
|
85
98
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
86
99
|
}, [searchInput, table]);
|
|
@@ -93,7 +106,7 @@ export function CouponsIndexListing({ initialCoupons, storeSlug: _storeSlug, sto
|
|
|
93
106
|
const clearFilters = () => {
|
|
94
107
|
table.setMany({ type: "", [TABLE_KEYS.DATE_FROM]: "", [TABLE_KEYS.DATE_TO]: "" });
|
|
95
108
|
};
|
|
96
|
-
return (_jsxs(Div, { className: "min-h-[40vh]", children: [_jsxs(
|
|
109
|
+
return (_jsxs(Div, { className: "min-h-[40vh]", children: [_jsxs(StickyToolbar, { offset: "header", tone: "default", border: true, padding: "md", z: "above-toolbar", dismissible: true, id: "coupons-toolbar", children: [_jsxs(Row, { gap: "xs", className: "max-w-full", children: [_jsxs(Button, { type: "button", variant: hasActiveFilters ? "outline" : "ghost", onClick: openFilters, gap: "sm", rounded: "lg", paddingX: "md", paddingY: "sm", textSize: "sm", weight: "medium", className: hasActiveFilters ? "border-primary bg-primary-50 text-primary-700 dark:bg-primary-900/30 dark:text-primary-300 shrink-0" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] shrink-0", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsxs(Span, { className: "hidden sm:inline", children: ["Filters", hasActiveFilters ? " •" : ""] })] }), _jsxs(Row, { surface: "default", className: `flex-1 ${__O.hidden}`, border: "strong", rounded: "lg", children: [_jsx(Input, { bare: true, type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleKeyDown, placeholder: "Search by name or description\u2026", className: "min-w-0 flex-1 bg-transparent px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text)] placeholder-zinc-400 outline-none" }), searchInput && (_jsx(IconButton, { type: "button", onClick: () => { setSearchInput(""); table.set(TABLE_KEYS.QUERY, ""); }, "aria-label": "Clear search", variant: "ghost", size: "sm", children: _jsx(X, { className: "h-3.5 w-3.5" }) })), _jsx(IconButton, { type: "button", onClick: commitSearch, "aria-label": "Search", variant: "ghost", size: "sm", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs(Row, { color: "muted", textSize: "sm", gap: "xs", className: "shrink-0", children: [_jsx(Span, { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, onChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, options: COUPON_SORT_OPTIONS })] })] }), hasActiveFilters && (_jsxs(Row, { gap: "xs", wrap: true, className: "mt-2", children: [activeType && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: [COUPON_TYPES.find((t) => t.value === activeType)?.label ?? activeType, _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.TYPE, ""); }, "aria-label": "Remove type filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_FROM) && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: ["From: ", table.get(TABLE_KEYS.DATE_FROM), _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_FROM, ""); }, "aria-label": "Remove from-date filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_TO) && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: ["To: ", table.get(TABLE_KEYS.DATE_TO), _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_TO, ""); }, "aria-label": "Remove to-date filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), _jsx(Button, { variant: "ghost", type: "button", onClick: clearFilters, textSize: "xs", textColor: "muted", className: "underline", children: "Clear all" })] }))] }), _jsxs(Div, { paddingY: "y-lg", paddingX: "x-md", children: [isLoading ? (_jsx(Grid, { gap: "sm", className: "md:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", gap: "sm", rounded: "xl", padding: "md", className: "animate-pulse", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : displayCoupons.length === 0 ? (_jsx(Div, { className: "text-left", padding: "y-4xl", children: _jsx(Text, { color: "faint", children: "No coupons match your search." }) })) : (_jsx(Grid, { gap: "sm", className: "md:grid-cols-2 lg:grid-cols-3", children: displayCoupons.map((coupon) => (_jsx(CouponCard, { coupon: coupon, labels: {
|
|
97
110
|
copy: "Copy",
|
|
98
111
|
copied: "Copied!",
|
|
99
112
|
expires: "Expires",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
-
import { Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, Stack, Text } from "../../../ui";
|
|
5
|
+
import { Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, Stack, Text, StickyToolbar } from "../../../ui";
|
|
6
6
|
import { ReviewCard } from "./ReviewsList";
|
|
7
7
|
import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "./ReviewFilters";
|
|
8
8
|
import { useReviews } from "../hooks/useReviews";
|
|
@@ -108,5 +108,5 @@ export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
|
108
108
|
value: opt.value,
|
|
109
109
|
label: SORT_OPTION_LABELS[opt.key] ?? opt.key,
|
|
110
110
|
}));
|
|
111
|
-
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(
|
|
111
|
+
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: _jsxs(Stack, { className: `${__P.p4}`, gap: "3", children: [_jsx(Div, { className: "h-4 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-2/3", surface: "subtle", rounded: "default" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No reviews found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) })] }));
|
|
112
112
|
}
|
|
@@ -3,6 +3,23 @@ import { useEffect, useRef, useState } from "react";
|
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { SEARCH_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
5
5
|
const SUGGESTIONS_ENDPOINT = SEARCH_ENDPOINTS.SUGGESTIONS;
|
|
6
|
+
/**
|
|
7
|
+
* /api/search/suggestions only understands the 4 content categories it can
|
|
8
|
+
* actually produce typeahead rows for (singular: product/category/blog/event)
|
|
9
|
+
* — it has no dedicated suggestion category for the other 10 SearchResourceType
|
|
10
|
+
* values (auctions, stores, brands, …). Map the ones it does support; for
|
|
11
|
+
* everything else, omit `type` entirely so the endpoint returns its
|
|
12
|
+
* unfiltered "all" set rather than zero results. Previously this sent the
|
|
13
|
+
* plural resource-type value verbatim (e.g. "products", the search bar's
|
|
14
|
+
* own default), which matched none of the route's singular checks and made
|
|
15
|
+
* every default-state search return empty suggestions.
|
|
16
|
+
*/
|
|
17
|
+
const SUGGESTION_TYPE_PARAM_MAP = {
|
|
18
|
+
products: "product",
|
|
19
|
+
categories: "category",
|
|
20
|
+
blog: "blog",
|
|
21
|
+
events: "event",
|
|
22
|
+
};
|
|
6
23
|
/**
|
|
7
24
|
* W1-19 — wired to `/api/search/suggestions` 2026-05-23. Fetches up to 20
|
|
8
25
|
* matches (5 per resource type) and surfaces them as typeahead rows.
|
|
@@ -30,7 +47,9 @@ export function useNavSuggestions(query, selectedType, debounceMs = 250) {
|
|
|
30
47
|
try {
|
|
31
48
|
const params = new URLSearchParams({ q: trimmed });
|
|
32
49
|
if (selectedType && selectedType !== "all") {
|
|
33
|
-
|
|
50
|
+
const mappedType = SUGGESTION_TYPE_PARAM_MAP[selectedType];
|
|
51
|
+
if (mappedType)
|
|
52
|
+
params.set("type", mappedType);
|
|
34
53
|
}
|
|
35
54
|
const res = await fetch(`${SUGGESTIONS_ENDPOINT}?${params.toString()}`, { signal: ctrl.signal, credentials: "same-origin" });
|
|
36
55
|
if (!res.ok) {
|
|
@@ -25,6 +25,7 @@ import { siteSettingsRepository } from "../../admin/repository/site-settings.rep
|
|
|
25
25
|
import { computePayoutDeduction } from "../../../_internal/shared/fees/calculator";
|
|
26
26
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
27
27
|
import { finalizeStagedMediaUrl, finalizeStagedMediaField, finalizeStagedMediaArray, } from "../../media/finalize";
|
|
28
|
+
import { StoreStatusValues } from "../../stores/schemas/firestore";
|
|
28
29
|
// --- Helper -------------------------------------------------------------------
|
|
29
30
|
async function finalizeProductMediaReferences(data) {
|
|
30
31
|
const finalized = { ...data };
|
|
@@ -58,12 +59,13 @@ export async function becomeSeller(userId) {
|
|
|
58
59
|
"pending",
|
|
59
60
|
};
|
|
60
61
|
}
|
|
62
|
+
const storeStatus = profile?.isTester ? "approved" : "pending";
|
|
61
63
|
await userRepository.update(userId, {
|
|
62
64
|
role: "seller",
|
|
63
|
-
storeStatus
|
|
65
|
+
storeStatus,
|
|
64
66
|
});
|
|
65
|
-
serverLogger.info("becomeSeller: application submitted", { userId });
|
|
66
|
-
return { storeStatus
|
|
67
|
+
serverLogger.info("becomeSeller: application submitted", { userId, storeStatus });
|
|
68
|
+
return { storeStatus };
|
|
67
69
|
}
|
|
68
70
|
// --- Create Store -------------------------------------------------------------
|
|
69
71
|
export async function createStore(userId, userName, input) {
|
|
@@ -79,23 +81,26 @@ export async function createStore(userId, userName, input) {
|
|
|
79
81
|
const suffix = `-${attempt}`;
|
|
80
82
|
storeSlug = `${baseSlug.slice(0, 80 - suffix.length)}${suffix}`;
|
|
81
83
|
}
|
|
84
|
+
const profile = await userRepository.findById(userId);
|
|
85
|
+
const isTester = profile?.isTester === true;
|
|
82
86
|
const store = await storeRepository.create({
|
|
83
87
|
storeSlug,
|
|
84
88
|
ownerId: userId,
|
|
85
89
|
storeName,
|
|
86
90
|
storeDescription: storeDescription || undefined,
|
|
87
91
|
storeCategory: storeCategory || undefined,
|
|
88
|
-
isPublic:
|
|
89
|
-
status:
|
|
92
|
+
isPublic: isTester,
|
|
93
|
+
status: isTester ? StoreStatusValues.ACTIVE : StoreStatusValues.PENDING,
|
|
90
94
|
});
|
|
91
95
|
await userRepository.update(userId, {
|
|
92
96
|
storeId: store.id,
|
|
93
97
|
storeSlug: store.storeSlug,
|
|
94
|
-
storeStatus: "pending",
|
|
98
|
+
storeStatus: isTester ? "approved" : "pending",
|
|
95
99
|
});
|
|
96
100
|
serverLogger.info("createStore: store created", {
|
|
97
101
|
userId,
|
|
98
102
|
storeSlug: store.storeSlug,
|
|
103
|
+
isTester,
|
|
99
104
|
});
|
|
100
105
|
return { store };
|
|
101
106
|
}
|