@mohasinac/appkit 4.64.1 → 4.65.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/.build-stamp.json +3 -3
- package/dist/_internal/server/features/checkout/actions.js +18 -1
- package/dist/_internal/server/features/search/index-build/sources.js +27 -12
- package/dist/_internal/server/features/seo/sitemap.js +78 -12
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +16 -1
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +14 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +19 -15
- package/dist/_internal/server/features/stores/tabs.js +20 -21
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +9 -4
- package/dist/_internal/server/jobs/core/categoryTaxonomyBackfill.js +22 -0
- package/dist/_internal/server/jobs/core/countersReconcile.js +0 -1
- package/dist/_internal/server/jobs/handlers/onCatalogueSubmittedForApproval.js +1 -1
- package/dist/_internal/shared/features/site-settings/types.d.ts +10 -2
- package/dist/_internal/shared/features/sticker-trace/trace/segment.js +13 -2
- package/dist/_internal/shared/listing-types/feature-flags.d.ts +54 -0
- package/dist/_internal/shared/listing-types/feature-flags.js +72 -0
- package/dist/client.d.ts +5 -2
- package/dist/client.js +8 -1
- package/dist/constants/field-names.d.ts +1 -0
- package/dist/constants/field-names.js +14 -0
- package/dist/features/account/api/[userId]/addresses/route.js +1 -5
- package/dist/features/addresses/repository/addresses.repository.js +1 -1
- package/dist/features/admin/api/bids/route.js +1 -5
- package/dist/features/admin/api/coupons/route.js +1 -5
- package/dist/features/admin/api/products/route.js +1 -5
- package/dist/features/admin/api/reviews/route.js +1 -5
- package/dist/features/admin/components/AdminCarouselEditorView.js +0 -1
- package/dist/features/admin/components/AdminFeatureEditorView.js +1 -2
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminProductEditorView.js +4 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +19 -3
- package/dist/features/admin/components/OwnerQuickActions.d.ts +7 -1
- package/dist/features/admin/components/OwnerQuickActions.js +1 -1
- package/dist/features/admin/schemas/admin-editor-forms.js +0 -1
- package/dist/features/admin/schemas/firestore.d.ts +52 -0
- package/dist/features/admin/schemas/firestore.js +20 -0
- package/dist/features/auctions/api/route.js +1 -8
- package/dist/features/auctions/components/AuctionDetailPageView.js +1 -7
- package/dist/features/auth/hooks/useAuth.js +1 -1
- package/dist/features/before-after/api/route.js +1 -5
- package/dist/features/blog/api/route.js +1 -8
- package/dist/features/blog/components/BlogIndexPageView.d.ts +2 -3
- package/dist/features/blog/components/BlogIndexPageView.js +6 -9
- package/dist/features/cart/actions/cart-actions.js +1 -1
- package/dist/features/cart/api/route.js +1 -5
- package/dist/features/cart/utils/guest-cart.js +1 -5
- package/dist/features/catalogue/repository/catalogue.repository.js +1 -1
- package/dist/features/categories/api/route.js +5 -23
- package/dist/features/categories/components/BundlesListView.d.ts +2 -3
- package/dist/features/categories/components/BundlesListView.js +2 -5
- package/dist/features/categories/components/CategoriesIndexListing.js +47 -5
- package/dist/features/categories/components/CategoriesIndexPageView.d.ts +2 -3
- package/dist/features/categories/components/CategoriesIndexPageView.js +0 -32
- package/dist/features/categories/constants/category-tabs.d.ts +1 -1
- package/dist/features/categories/constants/category-tabs.js +7 -0
- package/dist/features/categories/hooks/useCategories.d.ts +6 -0
- package/dist/features/categories/hooks/useCategories.js +73 -2
- package/dist/features/categories/types/index.d.ts +1 -1
- package/dist/features/classified/components/ClassifiedDetailPageView.js +2 -8
- package/dist/features/collections/api/route.js +1 -5
- package/dist/features/consultation/api/route.js +1 -5
- package/dist/features/contact/email.js +2 -5
- package/dist/features/corporate/api/route.js +1 -5
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +1 -7
- package/dist/features/events/components/AdminEventEditorView.js +1 -1
- package/dist/features/events/components/EventsListPageView.d.ts +2 -3
- package/dist/features/events/components/EventsListPageView.js +8 -11
- package/dist/features/faq/constants/help-guides.d.ts +8 -5
- package/dist/features/faq/constants/help-guides.js +9 -5
- package/dist/features/filters/AsyncFacetSection.js +2 -2
- package/dist/features/filters/FilterFacetSection.js +5 -5
- package/dist/features/filters/RangeFilter.js +2 -2
- package/dist/features/filters/SwitchFilter.js +2 -2
- package/dist/features/filters/filterUtils.d.ts +0 -2
- package/dist/features/filters/filterUtils.js +0 -4
- package/dist/features/filters/index.d.ts +1 -1
- package/dist/features/filters/index.js +1 -1
- package/dist/features/forms/Checkbox.js +4 -3
- package/dist/features/forms/Input.js +5 -4
- package/dist/features/forms/Radio.js +6 -5
- package/dist/features/forms/Select.js +3 -2
- package/dist/features/forms/Slider.js +3 -2
- package/dist/features/forms/Textarea.js +3 -2
- package/dist/features/forms/Toggle.js +3 -3
- package/dist/features/forms/index.d.ts +1 -1
- package/dist/features/forms/index.js +1 -1
- package/dist/features/forms/utils.d.ts +0 -2
- package/dist/features/forms/utils.js +0 -4
- package/dist/features/grouped/schemas/grouped-listing-form.d.ts +6 -6
- package/dist/features/grouped/schemas/index.d.ts +2 -2
- package/dist/features/history/utils/guest-history.js +1 -5
- package/dist/features/homepage/components/CustomCardsSection.js +8 -2
- package/dist/features/homepage/components/FeaturedBundlesSection.d.ts +6 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +1 -1
- package/dist/features/layout/AppLayoutShell.d.ts +23 -1
- package/dist/features/layout/AppLayoutShell.js +13 -12
- package/dist/features/layout/BottomNavItem.d.ts +9 -1
- package/dist/features/layout/BottomNavItem.js +18 -4
- package/dist/features/layout/index.d.ts +1 -1
- package/dist/features/live/components/LiveItemDetailPageView.js +1 -7
- package/dist/features/media/MediaLightbox.js +3 -5
- package/dist/features/orders/components/OrderCompletionPanel.d.ts +1 -1
- package/dist/features/orders/components/OrderCompletionPanel.js +1 -1
- package/dist/features/orders/components/RaiseDisputeForm.js +1 -1
- package/dist/features/orders/components/RefundRequestView.js +1 -1
- package/dist/features/parts/schemas/firestore.d.ts +274 -0
- package/dist/features/parts/schemas/firestore.js +19 -0
- package/dist/features/payments/repository/saved-payment-methods.repository.js +1 -1
- package/dist/features/pre-orders/api/admin/route.js +1 -5
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -7
- package/dist/features/products/api/route.js +1 -3
- package/dist/features/products/components/CustomSectionTabContent.js +0 -5
- package/dist/features/products/components/GroupMemberPicker.js +2 -2
- package/dist/features/products/components/GroupSettingsPanel.js +1 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -7
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
- package/dist/features/products/components/ProductDetailPageView.js +1 -7
- package/dist/features/products/repository/products.repository.js +0 -1
- package/dist/features/promotions/api/route.js +1 -8
- package/dist/features/reviews/components/ReviewsIndexPageView.d.ts +2 -3
- package/dist/features/reviews/components/ReviewsIndexPageView.js +9 -12
- package/dist/features/seller/api/coupons/route.js +1 -5
- package/dist/features/seller/api/products/route.js +1 -5
- package/dist/features/seller/components/SellerProductShell.js +1 -2
- package/dist/features/seller/hooks/useSellerListingData.js +0 -30
- package/dist/features/stores/api/[storeSlug]/auctions/route.js +1 -10
- package/dist/features/stores/api/[storeSlug]/products/route.js +1 -10
- package/dist/features/stores/components/InteractiveStoreCard.js +9 -1
- package/dist/features/stores/components/StoreGuideHubView.js +1 -1
- package/dist/features/stores/components/StoresIndexPageView.d.ts +2 -3
- package/dist/features/stores/components/StoresIndexPageView.js +6 -9
- package/dist/features/stores/schemas/firestore.d.ts +27 -0
- package/dist/features/stores/schemas/firestore.js +2 -0
- package/dist/features/wishlist/utils/guest-wishlist.js +1 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -1
- package/dist/providers/db-firebase/admin-app-lite.js +14 -2
- package/dist/providers/db-firebase/admin.js +8 -2
- package/dist/react/hooks/useBrowsePageFlags.d.ts +26 -0
- package/dist/react/hooks/useBrowsePageFlags.js +18 -0
- package/dist/schemas/registry.d.ts +4 -4
- package/dist/security/csp.d.ts +32 -2
- package/dist/security/csp.js +32 -2
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.js +1 -1
- package/dist/ui/components/Chart.js +0 -1
- package/dist/ui/rich-text/RichText.js +1 -3
- package/dist/ui/rich-text/RichTextRenderer.js +1 -3
- package/dist/utils/browser-storage.d.ts +14 -0
- package/dist/utils/browser-storage.js +18 -0
- package/dist/utils/classnames.d.ts +19 -0
- package/dist/utils/classnames.js +21 -0
- package/dist/utils/search-params.d.ts +44 -0
- package/dist/utils/search-params.js +46 -0
- package/dist/utils/sieve-builder.d.ts +13 -0
- package/dist/utils/sieve-builder.js +22 -0
- package/dist/utils/string.formatter.d.ts +12 -0
- package/dist/utils/string.formatter.js +17 -0
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import Link from "next/link.js";
|
|
4
|
-
import { Div, Heading, HorizontalScroller, PlainCard, PLAIN_CLASS, PosterCard, PosterGrid, POSTER_CLASS, Section, } from "../../../ui/index.js";
|
|
4
|
+
import { Div, Heading, HorizontalScroller, PlainCard, PLAIN_CLASS, PosterCard, PosterGrid, POSTER_CLASS, Section, Anchor, } from "../../../ui/index.js";
|
|
5
5
|
import { THEMED_TEXT_PRIMARY } from "../../../_internal/shared/styles/themed.js";
|
|
6
6
|
/* Not re-exported from `ui/index` — import from the defining module (Root Cause #18). */
|
|
7
7
|
import { Iframe } from "../../../ui/components/Iframe.js";
|
|
@@ -22,7 +22,13 @@ function isKeyChip(variant) {
|
|
|
22
22
|
function Chip({ button, className, }) {
|
|
23
23
|
const external = button.target === "_blank" || /^https?:\/\//.test(button.href);
|
|
24
24
|
if (external) {
|
|
25
|
-
|
|
25
|
+
/*
|
|
26
|
+
* 🛑 `<Anchor>`, not a raw `<a>`. It owns the target+rel pair, so an external
|
|
27
|
+
* link cannot ship `target="_blank"` without `rel="noopener noreferrer"` —
|
|
28
|
+
* that is a tabnabbing hole, not a style preference. This branch had both
|
|
29
|
+
* written out by hand and correct; the point is that the next one will not be.
|
|
30
|
+
*/
|
|
31
|
+
return (_jsx(Anchor, { href: button.href, className: className, external: true, target: button.target ?? "_blank", children: button.label }));
|
|
26
32
|
}
|
|
27
33
|
return (_jsx(Link, { href: button.href, className: className, children: button.label }));
|
|
28
34
|
}
|
|
@@ -15,6 +15,11 @@ export interface FeaturedBundlesSectionProps {
|
|
|
15
15
|
/** Cap the rendered bundles. Items are supplied pre-fetched, so this slices. */
|
|
16
16
|
maxItems?: number;
|
|
17
17
|
/** Show the "N% off" badge on each cover. Defaults to `true`. */
|
|
18
|
+
/**
|
|
19
|
+
* 🛑 ACCEPTED AND NOT READ — the savings badge renders unconditionally, so
|
|
20
|
+
* passing `false` has never hidden it. Declared because callers pass it;
|
|
21
|
+
* either wire it to the badge or delete it with its 7 call sites.
|
|
22
|
+
*/
|
|
18
23
|
showSavingsBadge?: boolean;
|
|
19
24
|
/** "View all" link label. */
|
|
20
25
|
viewMoreLabel?: string;
|
|
@@ -23,4 +28,4 @@ export interface FeaturedBundlesSectionProps {
|
|
|
23
28
|
bundleSlug: string;
|
|
24
29
|
}) => Promise<unknown>;
|
|
25
30
|
}
|
|
26
|
-
export declare function FeaturedBundlesSection({ title, description, initialItems, maxItems,
|
|
31
|
+
export declare function FeaturedBundlesSection({ title, description, initialItems, maxItems, viewMoreLabel, className, onBuyNow, }: FeaturedBundlesSectionProps): React.JSX.Element | null;
|
|
@@ -24,7 +24,7 @@ import { BUNDLE_COPY } from "../../../_internal/shared/features/categories/bundl
|
|
|
24
24
|
import { MarketplaceBundleCard } from "../../products/components/MarketplaceBundleCard.js";
|
|
25
25
|
import { BundleBuyNowCta } from "../../categories/components/BundleBuyNowCta.js";
|
|
26
26
|
import { useHandMode } from "../../../_internal/client/hand-mode/index.js";
|
|
27
|
-
export function FeaturedBundlesSection({ title = BUNDLE_COPY.featuredDefaultTitle, description, initialItems = [], maxItems,
|
|
27
|
+
export function FeaturedBundlesSection({ title = BUNDLE_COPY.featuredDefaultTitle, description, initialItems = [], maxItems, viewMoreLabel = BUNDLE_COPY.featured.viewAll, className = "", onBuyNow, }) {
|
|
28
28
|
const { hand } = useHandMode();
|
|
29
29
|
const bundles = initialItems.filter((c) => c.categoryType === "bundle");
|
|
30
30
|
const items = maxItems && maxItems > 0 ? bundles.slice(0, maxItems) : bundles;
|
|
@@ -26,6 +26,22 @@ export interface AppLayoutShellSidebarSection {
|
|
|
26
26
|
/** When true, this section is expanded by default. */
|
|
27
27
|
defaultOpen?: boolean;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Trailing control rendered beside a sidebar link — the seam for an admin's
|
|
31
|
+
* inline "hide this from the nav" toggle.
|
|
32
|
+
*
|
|
33
|
+
* 🛑 A SLOT, not a built-in toggle. appkit has no idea what `navConfig` is,
|
|
34
|
+
* which role may write it, or what a write costs; the consumer owns all three.
|
|
35
|
+
* Baking an admin control into a layout primitive would also put a write path
|
|
36
|
+
* inside a component every visitor renders.
|
|
37
|
+
*
|
|
38
|
+
* 🛑 Whatever it returns is rendered OUTSIDE the `<TextLink>`, as a sibling.
|
|
39
|
+
* A `<button>` inside an `<a>` is invalid HTML that browsers resolve by
|
|
40
|
+
* splitting the anchor — the same trap `InteractiveStoreCard` documents for
|
|
41
|
+
* its trust badges. That is why the row became a flex container rather than
|
|
42
|
+
* the control simply being appended to the link's children.
|
|
43
|
+
*/
|
|
44
|
+
export type SidebarItemActionRenderer = (item: AppLayoutShellSidebarLink) => React.ReactNode;
|
|
29
45
|
export interface AppLayoutShellSidebarAction {
|
|
30
46
|
href: string;
|
|
31
47
|
label: string;
|
|
@@ -36,6 +52,12 @@ export interface AppLayoutShellProps {
|
|
|
36
52
|
navItems: MainNavbarItem[];
|
|
37
53
|
sidebarItems?: AppLayoutShellSidebarLink[];
|
|
38
54
|
sidebarSections?: AppLayoutShellSidebarSection[];
|
|
55
|
+
/**
|
|
56
|
+
* Trailing control beside each sidebar link — the admin inline-toggle seam.
|
|
57
|
+
* See `SidebarItemActionRenderer`. Omitted for every non-admin viewer, so a
|
|
58
|
+
* normal visitor renders exactly what they rendered before.
|
|
59
|
+
*/
|
|
60
|
+
renderSidebarItemAction?: SidebarItemActionRenderer;
|
|
39
61
|
sidebarPrimaryActions?: AppLayoutShellSidebarAction[];
|
|
40
62
|
sidebarTitle?: string;
|
|
41
63
|
hiddenNavItems?: string[];
|
|
@@ -156,4 +178,4 @@ export interface AppLayoutShellProps {
|
|
|
156
178
|
};
|
|
157
179
|
};
|
|
158
180
|
}
|
|
159
|
-
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, showBottomNav, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): React.JSX.Element;
|
|
181
|
+
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, renderSidebarItemAction, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, showBottomNav, contentClassName, lightBackground, darkBackground, }: AppLayoutShellProps): React.JSX.Element;
|
|
@@ -74,7 +74,7 @@ function flattenSidebarLinks(items) {
|
|
|
74
74
|
* no caret small enough to aim at reliably — the tap has to do both jobs, in
|
|
75
75
|
* order.
|
|
76
76
|
*/
|
|
77
|
-
function SidebarParentLink({ item, activeItem, activeHref, navItemClass, navItemActiveClass, }) {
|
|
77
|
+
function SidebarParentLink({ item, activeItem, activeHref, navItemClass, navItemActiveClass, renderItemAction, }) {
|
|
78
78
|
const children = item.children ?? [];
|
|
79
79
|
const containsActive = !!activeItem && children.some((child) => child.href === activeItem.href);
|
|
80
80
|
const [expanded, setExpanded] = useState(containsActive);
|
|
@@ -92,19 +92,20 @@ function SidebarParentLink({ item, activeItem, activeHref, navItemClass, navItem
|
|
|
92
92
|
return;
|
|
93
93
|
e.preventDefault();
|
|
94
94
|
setExpanded(true);
|
|
95
|
-
}, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label, _jsx(ChevronDown, { className: `ms-auto w-3.5 h-3.5 shrink-0 opacity-70 transition-transform duration-200 ${expanded ? "rotate-180" : ""}`, "aria-hidden": "true" })] }), expanded && (_jsx(SidebarSectionLinks, { items: children, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, nested: true }))] }));
|
|
95
|
+
}, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label, _jsx(ChevronDown, { className: `ms-auto w-3.5 h-3.5 shrink-0 opacity-70 transition-transform duration-200 ${expanded ? "rotate-180" : ""}`, "aria-hidden": "true" })] }), expanded && (_jsx(SidebarSectionLinks, { items: children, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, nested: true, renderItemAction: renderItemAction }))] }));
|
|
96
96
|
}
|
|
97
97
|
/** One sidebar link plus its indented sub-links. */
|
|
98
|
-
function SidebarSectionLinks({ items, activeHref, activeItem, navItemClass, navItemActiveClass, nested = false, }) {
|
|
98
|
+
function SidebarSectionLinks({ items, activeHref, activeItem, navItemClass, navItemActiveClass, nested = false, renderItemAction, }) {
|
|
99
99
|
return (_jsx(Ul, { spacing: "2xs", className: nested ? "ps-[var(--appkit-space-6)]" : undefined, children: items.map((item) => {
|
|
100
100
|
const isActive = activeItem?.href === item.href;
|
|
101
101
|
if (item.children && item.children.length > 0) {
|
|
102
|
-
return (_jsx(SidebarParentLink, { item: item, activeItem: activeItem, activeHref: activeHref, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass }, `${item.href}-${item.label}`));
|
|
102
|
+
return (_jsx(SidebarParentLink, { item: item, activeItem: activeItem, activeHref: activeHref, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, renderItemAction: renderItemAction }, `${item.href}-${item.label}`));
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
const action = renderItemAction?.(item);
|
|
105
|
+
return (_jsx(Li, { children: _jsxs(Row, { align: "center", gap: "xs", className: action ? "w-full" : undefined, children: [_jsxs(TextLink, { href: item.href, variant: "none", className: `${isActive ? navItemActiveClass : navItemClass}${action ? " min-w-0 flex-1" : ""}`, "aria-current": isActive ? "page" : undefined, children: [item.icon && (_jsx(Span, { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }), action] }) }, `${item.href}-${item.label}`));
|
|
105
106
|
}) }));
|
|
106
107
|
}
|
|
107
|
-
function CollapsibleSidebarSection({ section, navItemClass, navItemActiveClass, activeHref, }) {
|
|
108
|
+
function CollapsibleSidebarSection({ section, navItemClass, navItemActiveClass, activeHref, renderItemAction, }) {
|
|
108
109
|
const activeItem = findActiveNavItem(flattenSidebarLinks(section.items), activeHref);
|
|
109
110
|
const hasActive = !!activeItem;
|
|
110
111
|
const [open, setOpen] = useState((section.defaultOpen ?? false) || hasActive);
|
|
@@ -116,9 +117,9 @@ function CollapsibleSidebarSection({ section, navItemClass, navItemActiveClass,
|
|
|
116
117
|
}, [hasActive]);
|
|
117
118
|
const hasTitle = !!section.title;
|
|
118
119
|
if (!hasTitle) {
|
|
119
|
-
return (_jsx(SidebarSectionLinks, { items: section.items, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass }));
|
|
120
|
+
return (_jsx(SidebarSectionLinks, { items: section.items, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, renderItemAction: renderItemAction }));
|
|
120
121
|
}
|
|
121
|
-
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""} shrink-0`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(SidebarSectionLinks, { items: section.items, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass }))] }));
|
|
122
|
+
return (_jsxs(Stack, { gap: "none", className: "", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-[var(--appkit-space-1)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-semibold uppercase tracking-wider text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)] transition-colors", children: [_jsx(Span, { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""} shrink-0`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(SidebarSectionLinks, { items: section.items, activeHref: activeHref, activeItem: activeItem, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, renderItemAction: renderItemAction }))] }));
|
|
122
123
|
}
|
|
123
124
|
/** Sidebar header when a user is logged in — avatar + display name + close button. */
|
|
124
125
|
function SidebarUserHeader({ user, onClose, profileHref, }) {
|
|
@@ -136,7 +137,7 @@ function SidebarUserHeader({ user, onClose, profileHref, }) {
|
|
|
136
137
|
function SidebarGuestHeader({ sidebarTitle, onClose, }) {
|
|
137
138
|
return (_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Div, { textWeight: "semibold", textSize: "sm", color: "primary", children: sidebarTitle }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: onClose, className: "rounded-full p-[var(--appkit-space-2)] hover:bg-surface-hover hover:text-zinc-900 text-[var(--appkit-color-text-muted)] dark:hover:text-zinc-100 transition-all hover:rotate-90", children: _jsx("svg", { className: "w-4 h-4 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }));
|
|
138
139
|
}
|
|
139
|
-
function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions, user, activeHref, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
140
|
+
function SidebarContent({ sidebarItems, sidebarSections, renderSidebarItemAction, sidebarPrimaryActions, user, activeHref, profileHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, adminHref, storeHref, sellerHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, theme, activeThemeName, toggleTheme, onLogout, onAfterLogout, }) {
|
|
140
141
|
const hasLegacyItems = sidebarItems.length > 0;
|
|
141
142
|
const hasSections = !!(sidebarSections && sidebarSections.length > 0);
|
|
142
143
|
const isAuthenticated = !!user;
|
|
@@ -185,10 +186,10 @@ function SidebarContent({ sidebarItems, sidebarSections, sidebarPrimaryActions,
|
|
|
185
186
|
action.variant === "outline"
|
|
186
187
|
? "border hover:bg-surface-hover border-[var(--appkit-color-border)] text-[var(--appkit-color-text)]"
|
|
187
188
|
: "bg-primary text-white hover:bg-primary-600 dark:bg-primary dark:hover:bg-primary-600 btn-glow",
|
|
188
|
-
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, hasActive: !!activeProfileLink, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: activeProfileLink?.href === profileHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === profileHref ? "page" : undefined, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: activeProfileLink?.href === userOrdersHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userOrdersHref ? "page" : undefined, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: activeProfileLink?.href === userWishlistHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userWishlistHref ? "page" : undefined, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: activeProfileLink?.href === userSettingsHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userSettingsHref ? "page" : undefined, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && userOrdersHref && (_jsxs(TextLink, { href: userOrdersHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && userReviewsHref && (_jsxs(TextLink, { href: userReviewsHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), auctionsEnabled && user.stats.auctionsWon != null && userOrdersHref && (_jsxs(TextLink, { href: `${userOrdersHref}?orderType=auction`, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, hasActive: !!activeDashboardLink, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: activeDashboardLink?.href === adminHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === adminHref ? "page" : undefined, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: activeDashboardLink?.href === resolvedStoreHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === resolvedStoreHref ? "page" : undefined, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, activeHref: activeHref }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-surface hover:text-primary-800 dark:hover:text-primary-200", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
189
|
+
].join(" "), children: action.label }, `${action.href}-${action.label}`))) })), isAuthenticated && (_jsx(CollapsibleNavGroup, { title: labels.sectionTitle, hasActive: !!activeProfileLink, children: _jsxs(Ul, { spacing: "2xs", children: [_jsx(Li, { children: _jsx(TextLink, { href: profileHref, variant: "none", className: activeProfileLink?.href === profileHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === profileHref ? "page" : undefined, children: labels.profile }) }), userOrdersHref && (_jsx(Li, { children: _jsx(TextLink, { href: userOrdersHref, variant: "none", className: activeProfileLink?.href === userOrdersHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userOrdersHref ? "page" : undefined, children: labels.orders }) })), userWishlistHref && (_jsx(Li, { children: _jsx(TextLink, { href: userWishlistHref, variant: "none", className: activeProfileLink?.href === userWishlistHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userWishlistHref ? "page" : undefined, children: labels.wishlist }) })), userSettingsHref && (_jsx(Li, { children: _jsx(TextLink, { href: userSettingsHref, variant: "none", className: activeProfileLink?.href === userSettingsHref ? navItemActiveClass : navItemClass, "aria-current": activeProfileLink?.href === userSettingsHref ? "page" : undefined, children: labels.settings }) }))] }) })), isAuthenticated && user?.stats && (_jsxs(Div, { layout: "grid", gap: "2", className: "grid-cols-2", children: [user.stats.totalOrders != null && userOrdersHref && (_jsxs(TextLink, { href: userOrdersHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.totalOrders }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Orders" })] })), user.stats.reviewsCount != null && userReviewsHref && (_jsxs(TextLink, { href: userReviewsHref, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.reviewsCount }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Reviews" })] })), auctionsEnabled && user.stats.auctionsWon != null && userOrdersHref && (_jsxs(TextLink, { href: `${userOrdersHref}?orderType=auction`, variant: "none", className: CLS_STAT_BOX, children: [_jsx(Text, { className: "leading-none", color: "primary", size: "lg", weight: "bold", children: user.stats.auctionsWon }), _jsx(Text, { className: CLS_STAT_LABEL, children: "Auctions Won" })] }))] })), isAuthenticated && isAdminOrSeller && (adminHref || resolvedStoreHref) && (_jsx(CollapsibleNavGroup, { title: labels.dashboardSectionTitle, hasActive: !!activeDashboardLink, children: _jsxs(Ul, { spacing: "2xs", children: [adminHref && role === "admin" && (_jsx(Li, { children: _jsx(TextLink, { href: adminHref, variant: "none", className: activeDashboardLink?.href === adminHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === adminHref ? "page" : undefined, children: labels.adminDashboard }) })), resolvedStoreHref && isAdminOrSeller && (_jsx(Li, { children: _jsx(TextLink, { href: resolvedStoreHref, variant: "none", className: activeDashboardLink?.href === resolvedStoreHref ? navItemActiveClass : navItemClass, "aria-current": activeDashboardLink?.href === resolvedStoreHref ? "page" : undefined, children: labels.storeDashboard }) }))] }) })), normalizedSections.map((section, sectionIndex) => (_jsx(CollapsibleSidebarSection, { section: section, navItemClass: navItemClass, navItemActiveClass: navItemActiveClass, activeHref: activeHref, renderItemAction: renderSidebarItemAction }, `sidebar-section-${sectionIndex}`))), (sidebarLocaleSlot || showThemeToggleInSidebar || (isAuthenticated && onLogout)) && (_jsxs(Stack, { border: "default", className: "border-t border-[var(--appkit-color-border-subtle)]", padding: "t-md", gap: "3", children: [sidebarLocaleSlot, showThemeToggleInSidebar && (_jsxs("button", { type: "button", onClick: toggleTheme, className: "flex w-full items-center justify-end gap-[var(--appkit-space-3)] rounded-lg px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] transition-colors hover:bg-primary-surface hover:text-primary-800 dark:hover:text-primary-200", children: [_jsx(Span, { "aria-hidden": "true", children: theme === "dark" ? "☀️" : "🌙" }), activeThemeName] })), isAuthenticated && onLogout && (_jsx("button", { type: "button", onClick: () => { onLogout(); onAfterLogout(); }, className: CLS_LOGOUT_BTN, children: labels.logout }))] }))] }));
|
|
189
190
|
}
|
|
190
191
|
// ─── Main component ────────────────────────────────────────────────────────────
|
|
191
|
-
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter = true, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, showBottomNav = true, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
192
|
+
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, renderSidebarItemAction, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, messagesHref, unreadMessageCount, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, showFooter = true, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, userReviewsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, showBottomNav = true, contentClassName, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
192
193
|
// QueryClientProvider used to be created here so AppLayoutShell could stand
|
|
193
194
|
// alone. The real cause of the 2026-06-10/11 "No QueryClient set" prod crash
|
|
194
195
|
// turned out to be a peer-dep duplicate of @tanstack/query-core under
|
|
@@ -236,7 +237,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
236
237
|
// by its countdown alone got no clearance). That mirror is gone: BottomChrome
|
|
237
238
|
// measures the real tier and publishes --bottom-chrome-height, and the footer
|
|
238
239
|
// reserves space from that. Nothing needs to guess any more.
|
|
239
|
-
const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, sidebarPrimaryActions: sidebarPrimaryActions, user: user, activeHref: pathname ?? "", profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, userReviewsHref: userReviewsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
|
|
240
|
+
const sidebarContent = (_jsx(SidebarContent, { sidebarItems: sidebarItems, sidebarSections: sidebarSections, renderSidebarItemAction: renderSidebarItemAction, sidebarPrimaryActions: sidebarPrimaryActions, user: user, activeHref: pathname ?? "", profileHref: profileHref, userOrdersHref: userOrdersHref, userWishlistHref: userWishlistHref, userSettingsHref: userSettingsHref, userReviewsHref: userReviewsHref, adminHref: adminHref, storeHref: storeHref, sellerHref: sellerHref, sidebarLocaleSlot: sidebarLocaleSlot, showThemeToggleInSidebar: showThemeToggleInSidebar, sidebarProfileLabels: sidebarProfileLabels, theme: theme, activeThemeName: activeTheme.name, toggleTheme: toggleTheme, onLogout: onLogout, onAfterLogout: () => setSidebarOpen(false) }));
|
|
240
241
|
const normalizedLightBackground = {
|
|
241
242
|
type: lightBackground.type,
|
|
242
243
|
value: lightBackground.value,
|
|
@@ -6,6 +6,14 @@ export interface BottomNavItemProps {
|
|
|
6
6
|
badge?: number | null;
|
|
7
7
|
isActive?: boolean;
|
|
8
8
|
className?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The link component. Defaults to `next/link`, and that default is
|
|
11
|
+
* load-bearing — see the note at the render below.
|
|
12
|
+
*
|
|
13
|
+
* Override it to supply a locale-aware link (`@/i18n/navigation`), never to go
|
|
14
|
+
* back to a bare anchor. Same contract as `OverflowRow`'s `linkAs`.
|
|
15
|
+
*/
|
|
16
|
+
linkAs?: React.ElementType;
|
|
9
17
|
}
|
|
10
18
|
/**
|
|
11
19
|
* BottomNavItem — a single tap target inside `<BottomNavLayout>`.
|
|
@@ -25,4 +33,4 @@ export interface BottomNavItemProps {
|
|
|
25
33
|
* </BottomNavLayout>
|
|
26
34
|
* ```
|
|
27
35
|
*/
|
|
28
|
-
export declare function BottomNavItem({ href, label, icon, badge, isActive, className, }: BottomNavItemProps): React.JSX.Element;
|
|
36
|
+
export declare function BottomNavItem({ href, label, icon, badge, isActive, className, linkAs: Link, }: BottomNavItemProps): React.JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import NextLink from "next/link.js";
|
|
2
3
|
import { Li, Span } from "../../ui/index.js";
|
|
3
4
|
/**
|
|
4
5
|
* BottomNavItem — a single tap target inside `<BottomNavLayout>`.
|
|
@@ -18,10 +19,23 @@ import { Li, Span } from "../../ui/index.js";
|
|
|
18
19
|
* </BottomNavLayout>
|
|
19
20
|
* ```
|
|
20
21
|
*/
|
|
21
|
-
export function BottomNavItem({ href, label, icon, badge, isActive = false, className, }) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
export function BottomNavItem({ href, label, icon, badge, isActive = false, className, linkAs: Link = NextLink, }) {
|
|
23
|
+
/*
|
|
24
|
+
* 🛑 `next/link`, not a bare `<a>`. This rendered a plain anchor until
|
|
25
|
+
* 2026-10-24, with two comments in this file contradicting each other about
|
|
26
|
+
* it — the docstring above said "Renders as `<a>` via Next.js `<Link>`" while
|
|
27
|
+
* the line below it said "use a plain anchor so this component stays
|
|
28
|
+
* framework-agnostic". The code did the second, so every tap on the MOBILE
|
|
29
|
+
* BOTTOM NAV — the primary navigation on a phone — was a full page load, and
|
|
30
|
+
* the only symptom is a flash.
|
|
31
|
+
*
|
|
32
|
+
* "Framework-agnostic" was never the real constraint: `OverflowRow` has
|
|
33
|
+
* imported `next/link` as its `linkAs` default since it shipped, and
|
|
34
|
+
* CLAUDE.md records that default as load-bearing for exactly this reason. The
|
|
35
|
+
* same escape hatch is here, so a consumer can still inject a locale-aware
|
|
36
|
+
* link.
|
|
37
|
+
*/
|
|
38
|
+
return (_jsx(Li, { className: "flex-1", children: _jsxs(Link, { href: href, "aria-label": label, "aria-current": isActive ? "page" : undefined, className: `relative flex flex-col items-center justify-center gap-[var(--appkit-space-0-5)] h-full w-full min-h-[44px] transition-colors ${isActive
|
|
25
39
|
? "text-primary-600 dark:text-secondary-400"
|
|
26
40
|
: "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]"}${className ? ` ${className}` : ""}`, children: [_jsxs(Span, { className: "relative", children: [icon, badge != null && badge > 0 && (_jsx(Span, { layout: "flex-center", color: "inverse", "aria-label": `${badge} items`, className: "absolute -top-1.5 -right-1.5 min-w-[16px] h-4 px-[var(--appkit-space-1)] text-[10px] bg-secondary-500 leading-none", rounded: "full", weight: "bold", children: badge > 99 ? "99+" : badge }))] }), _jsx(Span, { weight: "medium", className: "text-[10px] leading-none", children: label })] }) }));
|
|
27
41
|
}
|
|
@@ -25,7 +25,7 @@ export type { BackToTopProps } from "./BackToTop";
|
|
|
25
25
|
export { LayoutClient } from "./LayoutClient";
|
|
26
26
|
export type { LayoutClientProps, LayoutProvider } from "./LayoutClient";
|
|
27
27
|
export { AppLayoutShell } from "./AppLayoutShell";
|
|
28
|
-
export type { AppLayoutShellProps, AppLayoutShellSidebarLink, AppLayoutShellSidebarSection } from "./AppLayoutShell";
|
|
28
|
+
export type { AppLayoutShellProps, AppLayoutShellSidebarLink, AppLayoutShellSidebarSection, SidebarItemActionRenderer } from "./AppLayoutShell";
|
|
29
29
|
export { BottomActionsProvider, useBottomActionsContext, } from "./BottomActionsContext";
|
|
30
30
|
export type { BottomAction, BottomBulkConfig, BottomActionsState, BottomActionsDesktopMode, } from "./BottomActionsContext";
|
|
31
31
|
export { useBottomActions } from "./hooks/useBottomActions";
|
|
@@ -10,7 +10,7 @@ const CLS_WARN_BODY = "mt-1 text-warning";
|
|
|
10
10
|
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
11
11
|
import { getDefaultCurrency } from "../../../core/baseline-resolver.js";
|
|
12
12
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
13
|
-
import {
|
|
13
|
+
import { toDescriptionHtml } from "../../../utils/string.formatter.js";
|
|
14
14
|
import { safeDisplayName } from "../../../security/index.js";
|
|
15
15
|
import { Div, Container, GatedPrice, PricesOnly, Heading, Main, Nav, RichText, Row, Section, Span, Stack, Text, SpecTable, } from "../../../ui/index.js";
|
|
16
16
|
import { PreOrderDetailView } from "../../products/components/PreOrderDetailView.js";
|
|
@@ -27,12 +27,6 @@ import { ReviewsListingPanel } from "../../reviews/components/ReviewsListingPane
|
|
|
27
27
|
import { ListingBottomActions } from "../../products/components/ListingBottomActions.js";
|
|
28
28
|
import { productImageUrls } from "../../products/schemas/firestore.js";
|
|
29
29
|
import { PurchaseReviewAffordance } from "../../reviews/components/PurchaseReviewAffordance.js";
|
|
30
|
-
function toDescriptionHtml(raw) {
|
|
31
|
-
if (!raw)
|
|
32
|
-
return "";
|
|
33
|
-
const s = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
34
|
-
return normalizeRichTextHtml(s);
|
|
35
|
-
}
|
|
36
30
|
export async function LiveItemDetailPageView({ slug, initialProduct, renderActions }) {
|
|
37
31
|
const product = initialProduct !== undefined
|
|
38
32
|
? (initialProduct ?? undefined)
|
|
@@ -7,6 +7,7 @@ import { Button } from "../../ui/components/Button.js";
|
|
|
7
7
|
import { Div } from "../../ui/components/Div.js";
|
|
8
8
|
import { Row } from "../../ui/components/Layout.js";
|
|
9
9
|
import { Span } from "../../ui/components/Typography.js";
|
|
10
|
+
import { joinClasses } from "../../utils/classnames.js";
|
|
10
11
|
const CLS_CLOSE_HOVER = "hover:!bg-error-surface";
|
|
11
12
|
const MIN_ZOOM = 1;
|
|
12
13
|
const MAX_ZOOM = 4;
|
|
@@ -23,18 +24,15 @@ const DEFAULT_LABELS = {
|
|
|
23
24
|
imageThumbnail: (n) => `Image ${n}`,
|
|
24
25
|
lightboxTitle: "Image viewer",
|
|
25
26
|
};
|
|
26
|
-
function cn(...classes) {
|
|
27
|
-
return classes.filter(Boolean).join(" ");
|
|
28
|
-
}
|
|
29
27
|
function IconBtn({ onClick, disabled, ariaLabel, ariaCurrent, className, children, }) {
|
|
30
|
-
return (_jsx(Button, { type: "button", onClick: onClick, disabled: disabled, "aria-label": ariaLabel, "aria-current": ariaCurrent, className:
|
|
28
|
+
return (_jsx(Button, { type: "button", onClick: onClick, disabled: disabled, "aria-label": ariaLabel, "aria-current": ariaCurrent, className: joinClasses("inline-flex items-center justify-center w-12 h-12 rounded-full bg-white/15 hover:bg-white/30 text-white border-0 active:scale-95 transition-colors disabled:opacity-30 disabled:cursor-not-allowed", className), children: children }));
|
|
31
29
|
}
|
|
32
30
|
// --- Controls bar sub-components ---------------------------------------------
|
|
33
31
|
function LightboxControlsBar({ items, index, zoom, isFullscreen, L, goPrev, goNext, zoomIn, zoomOut, resetZoom, toggleFullscreen, onClose, }) {
|
|
34
32
|
return (_jsxs(Row, { gap: "xs", className: "bg-black/85 backdrop-blur-sm shrink-0 border-t border-white/10", padding: "inline", align: "center", justify: "center", wrap: true, children: [items.length > 1 && (_jsx(IconBtn, { onClick: (e) => { e.stopPropagation(); goPrev(); }, ariaLabel: L.prevImage, children: _jsx(ChevronLeft, { className: "w-7 h-7" }) })), _jsx(IconBtn, { onClick: zoomOut, disabled: zoom <= MIN_ZOOM, ariaLabel: L.zoomOut, children: _jsx(ZoomOut, { className: "w-7 h-7" }) }), _jsxs(Span, { color: "inverse-muted", size: "xs", className: "w-14 tabular-nums", align: "center", children: [Math.round(zoom * 100), "%"] }), _jsx(IconBtn, { onClick: zoomIn, disabled: zoom >= MAX_ZOOM, ariaLabel: L.zoomIn, children: _jsx(ZoomIn, { className: "w-7 h-7" }) }), zoom > 1 && (_jsx(IconBtn, { onClick: resetZoom, ariaLabel: L.resetZoom, children: _jsx(RotateCcw, { className: "w-7 h-7" }) })), _jsx(Span, { className: "w-px h-6 bg-white/15 mx-1 shrink-0", "aria-hidden": "true" }), _jsxs(Span, { color: "inverse-muted", size: "xs", className: "w-12 tabular-nums", align: "center", children: [index + 1, " / ", items.length] }), _jsx(Span, { className: "w-px h-6 bg-white/15 mx-1 shrink-0", "aria-hidden": "true" }), _jsx(IconBtn, { onClick: toggleFullscreen, ariaLabel: isFullscreen ? L.exitFullscreen : L.enterFullscreen, children: isFullscreen ? _jsx(Minimize, { className: "w-6 h-6" }) : _jsx(Maximize, { className: "w-6 h-6" }) }), _jsx(IconBtn, { onClick: onClose, ariaLabel: L.close, className: CLS_CLOSE_HOVER, children: _jsx(X, { className: "w-7 h-7" }) }), items.length > 1 && (_jsx(IconBtn, { onClick: (e) => { e.stopPropagation(); goNext(); }, ariaLabel: L.nextImage, children: _jsx(ChevronRight, { className: "w-7 h-7" }) }))] }));
|
|
35
33
|
}
|
|
36
34
|
function LightboxThumbnailStrip({ items, index, onSelect, resetZoom, L, }) {
|
|
37
|
-
return (_jsx(Row, { surface: "overlay-xl", className: "backdrop-blur-sm overflow-x-auto shrink-0", padding: "inline", justify: "center", gap: "sm", style: { paddingBottom: "calc(0.75rem + env(safe-area-inset-bottom, 0px))" }, children: items.map((item, i) => (_jsx(Button, { type: "button", onClick: () => { onSelect(i); resetZoom(); }, "aria-label": L.imageThumbnail(i + 1), "aria-current": i === index ? "true" : undefined, className:
|
|
35
|
+
return (_jsx(Row, { surface: "overlay-xl", className: "backdrop-blur-sm overflow-x-auto shrink-0", padding: "inline", justify: "center", gap: "sm", style: { paddingBottom: "calc(0.75rem + env(safe-area-inset-bottom, 0px))" }, children: items.map((item, i) => (_jsx(Button, { type: "button", onClick: () => { onSelect(i); resetZoom(); }, "aria-label": L.imageThumbnail(i + 1), "aria-current": i === index ? "true" : undefined, className: joinClasses("relative shrink-0 w-14 h-14 p-[var(--appkit-space-0)] overflow-hidden rounded-lg border-2 transition-all active:scale-95", i === index ? "border-white opacity-100" : "border-transparent opacity-40 hover:opacity-75"), children: _jsx(MediaImage, { src: item.src, alt: item.alt, size: "thumbnail" }) }, i))) }));
|
|
38
36
|
}
|
|
39
37
|
// --- MediaLightbox ------------------------------------------------------------
|
|
40
38
|
export function MediaLightbox({ items, initialIndex = 0, isOpen, onClose, labels, }) {
|
|
@@ -24,4 +24,4 @@ export interface OrderCompletionPanelProps {
|
|
|
24
24
|
/** Called after a successful completion, so the page can refresh the order. */
|
|
25
25
|
onCompleted?: () => void;
|
|
26
26
|
}
|
|
27
|
-
export declare function OrderCompletionPanel({
|
|
27
|
+
export declare function OrderCompletionPanel({ endpoint, completedAt, completedBy, isDelivered, isContested, onCompleted, }: OrderCompletionPanelProps): import("react").JSX.Element | null;
|
|
@@ -80,7 +80,7 @@ const MIN_COMMENT = 10;
|
|
|
80
80
|
function StarRow({ value, onChange, disabled, }) {
|
|
81
81
|
return (_jsx(Row, { gap: "xs", role: "group", "aria-label": "Rating out of 5", children: [1, 2, 3, 4, 5].map((n) => (_jsx(Button, { variant: n <= value ? "primary" : "outline", size: "sm", disabled: disabled, "aria-label": `${n} star${n === 1 ? "" : "s"}`, "aria-pressed": n === value, onClick: () => onChange(n), children: _jsx(Icon, { name: "review", filled: n <= value }) }, n))) }));
|
|
82
82
|
}
|
|
83
|
-
export function OrderCompletionPanel({
|
|
83
|
+
export function OrderCompletionPanel({ endpoint, completedAt, completedBy, isDelivered, isContested, onCompleted, }) {
|
|
84
84
|
const [lines, setLines] = useState([]);
|
|
85
85
|
const [loading, setLoading] = useState(true);
|
|
86
86
|
const [loadError, setLoadError] = useState(null);
|
|
@@ -58,7 +58,7 @@ export function RaiseDisputeForm({ order, reasonsEndpoint, onSubmit, onDone, })
|
|
|
58
58
|
* previous kind's reasons — which is the one thing that must never happen
|
|
59
59
|
* here, because a mismatched reason is refused by the server.
|
|
60
60
|
*/
|
|
61
|
-
const loadReasons = useCallback(async (query
|
|
61
|
+
const loadReasons = useCallback(async (query) => {
|
|
62
62
|
if (!kind)
|
|
63
63
|
return { items: [], hasMore: false };
|
|
64
64
|
/*
|
|
@@ -29,7 +29,7 @@ import { SectionForm, useSectionFormNav } from "../../shell/index.js";
|
|
|
29
29
|
import { z } from "zod";
|
|
30
30
|
import { REFUND_COPY } from "../../../_internal/shared/features/orders/refund-copy.js";
|
|
31
31
|
import { resolvePublishedCopy, resolvePublishedValues, } from "../../../_internal/shared/content/published-values.js";
|
|
32
|
-
import { RETURN_REASON_LABEL, selectableReturnReasons
|
|
32
|
+
import { RETURN_REASON_LABEL, selectableReturnReasons } from "../../../_internal/shared/features/orders/return-reasons.js";
|
|
33
33
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
34
34
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
35
35
|
import { toUserMessage } from "../../../errors/error-display-map.js";
|