@mohasinac/appkit 4.37.1 → 4.39.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/digital-code/CodeRevealPanel.d.ts +20 -1
- package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +21 -1
- package/dist/_internal/client/features/digital-code/DigitalContentPoolManager.d.ts +40 -0
- package/dist/_internal/client/features/digital-code/DigitalContentPoolManager.js +67 -0
- package/dist/_internal/server/features/bundles/og.d.ts +0 -1
- package/dist/_internal/server/features/bundles/og.js +2 -18
- package/dist/_internal/server/features/catalogue/og.d.ts +0 -1
- package/dist/_internal/server/features/catalogue/og.js +1 -9
- package/dist/_internal/server/features/checkout/actions.js +18 -0
- package/dist/_internal/server/features/classified/og.d.ts +0 -1
- package/dist/_internal/server/features/classified/og.js +1 -9
- package/dist/_internal/server/features/digital-code/download.d.ts +60 -0
- package/dist/_internal/server/features/digital-code/download.js +128 -0
- package/dist/_internal/server/features/digital-code/og.d.ts +0 -1
- package/dist/_internal/server/features/digital-code/og.js +1 -9
- package/dist/_internal/server/features/digital-code/pool.d.ts +101 -0
- package/dist/_internal/server/features/digital-code/pool.js +208 -0
- package/dist/_internal/server/features/digital-code/upload.d.ts +51 -0
- package/dist/_internal/server/features/digital-code/upload.js +144 -0
- package/dist/_internal/server/features/grouped/og.d.ts +7 -4
- package/dist/_internal/server/features/grouped/og.js +7 -6
- package/dist/_internal/server/features/item-requests/og.d.ts +0 -1
- package/dist/_internal/server/features/item-requests/og.js +2 -18
- package/dist/_internal/server/features/live/og.d.ts +0 -1
- package/dist/_internal/server/features/live/og.js +1 -9
- package/dist/_internal/server/features/prize-draws/og.js +5 -10
- package/dist/_internal/server/features/products/og.js +5 -2
- package/dist/_internal/server/features/seo/sitemap.js +10 -2
- package/dist/_internal/server/features/shared/listing-adapters.d.ts +1 -2
- package/dist/_internal/server/features/shared/listing-adapters.js +5 -3
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +40 -0
- package/dist/_internal/server/jobs/core/countersReconcile.js +153 -40
- package/dist/_internal/server/jobs/core/onCategoryWrite.js +33 -0
- package/dist/_internal/server/jobs/core/onProductWrite.js +93 -25
- package/dist/_internal/server/notifications/send-recorder.d.ts +79 -0
- package/dist/_internal/server/notifications/send-recorder.js +126 -0
- package/dist/_internal/shared/listing-types/_registry.d.ts +31 -4
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/art/config.js +2 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/config.js +3 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/classified/config.js +3 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +3 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/live/config.js +3 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +2 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +3 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/config.js +2 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +2 -0
- package/dist/client/observability/reportClientError.js +11 -1
- package/dist/client.d.ts +6 -0
- package/dist/client.js +10 -0
- package/dist/core/hooks/useSyncManager.js +11 -1
- package/dist/features/account/hooks/useNotifications.js +25 -5
- package/dist/features/auctions/components/AuctionBottomActions.js +10 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +12 -3
- package/dist/features/auctions/components/LiveBidPrice.js +2 -2
- package/dist/features/auctions/components/LiveMinIncrement.d.ts +1 -1
- package/dist/features/auctions/components/LiveMinIncrement.js +8 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -3
- package/dist/features/auctions/components/PlaceBidFormClient.js +16 -1
- package/dist/features/auctions/hooks/useAuctions.js +12 -1
- package/dist/features/auctions/hooks/useRealtimeBids.d.ts +3 -0
- package/dist/features/auctions/hooks/useRealtimeBids.js +138 -36
- package/dist/features/blog/components/BlogIndexPageView.js +10 -1
- package/dist/features/blog/repository/blog.repository.d.ts +14 -0
- package/dist/features/blog/repository/blog.repository.js +23 -1
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +2 -2
- package/dist/features/catalogue/components/PublicCatalogueView.js +2 -2
- package/dist/features/categories/components/BundleCollage.js +10 -5
- package/dist/features/categories/components/BundleDetailView.js +7 -3
- package/dist/features/categories/components/CategoryDetailPageView.js +1 -1
- package/dist/features/categories/components/CategoryProductsListing.js +4 -1
- package/dist/features/categories/components/CategoryTree.js +1 -1
- package/dist/features/categories/repository/categories.repository.d.ts +34 -2
- package/dist/features/categories/repository/categories.repository.js +31 -9
- package/dist/features/classified/components/ClassifiedDetailPageView.js +2 -2
- package/dist/features/contact/email.js +36 -1
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +2 -2
- package/dist/features/homepage/components/FeaturedBundlesSection.js +4 -4
- package/dist/features/live/components/LiveItemDetailPageView.js +2 -2
- package/dist/features/orders/repository/orders.repository.d.ts +21 -1
- package/dist/features/orders/repository/orders.repository.js +26 -9
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +2 -2
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -2
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +4 -1
- package/dist/features/products/actions/product-actions.d.ts +9 -0
- package/dist/features/products/actions/product-actions.js +11 -0
- package/dist/features/products/columns/productTableColumns.js +2 -2
- package/dist/features/products/components/AuctionsIndexListing.js +4 -1
- package/dist/features/products/components/BidHistory.js +2 -2
- package/dist/features/products/components/CompareOverlay.js +2 -2
- package/dist/features/products/components/GroupMemberPicker.js +5 -5
- package/dist/features/products/components/ListingBottomActions.js +12 -3
- package/dist/features/products/components/MakeOfferButton.js +7 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +4 -4
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -2
- package/dist/features/products/components/PrizeDrawCollage.js +16 -5
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -2
- package/dist/features/products/components/PrizeDrawsIndexListing.js +4 -1
- package/dist/features/products/components/PrizeRevealModal.js +2 -2
- package/dist/features/products/components/ProductDetailActions.js +10 -3
- package/dist/features/products/components/ProductFilters.js +2 -2
- package/dist/features/products/components/ProductGrid.js +5 -2
- package/dist/features/products/components/ProductsIndexListing.js +12 -1
- package/dist/features/products/components/ProductsIndexPageView.js +26 -0
- package/dist/features/products/components/ShowGroupSection.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +2 -2
- package/dist/features/products/constants/listing-tabs.d.ts +36 -12
- package/dist/features/products/constants/listing-tabs.js +38 -14
- package/dist/features/products/constants/sieve.d.ts +14 -0
- package/dist/features/products/constants/sieve.js +39 -0
- package/dist/features/products/repository/products.repository.d.ts +35 -0
- package/dist/features/products/repository/products.repository.js +116 -3
- package/dist/features/products/schemas/firestore.d.ts +37 -0
- package/dist/features/seller/actions/seller-actions.js +4 -13
- package/dist/features/seller/components/SellerProductShell.d.ts +7 -1
- package/dist/features/seller/components/SellerProductShell.js +11 -2
- package/dist/features/stores/api/[storeSlug]/reviews/route.d.ts +6 -1
- package/dist/features/stores/api/[storeSlug]/reviews/route.js +89 -30
- package/dist/features/stores/components/StoresIndexPageView.js +17 -1
- package/dist/features/tester/seed-data/authored/admin__users-trust.js +1 -1
- package/dist/features/tester/seed-data/authored/buying__bidding.js +5 -4
- package/dist/features/tester/seed-data/authored/buying__browsing-search.js +85 -85
- package/dist/features/tester/seed-data/authored/buying__image-tile-layout.js +2 -2
- package/dist/features/tester/seed-data/authored/buying__offers.js +1 -1
- package/dist/features/tester/seed-data/authored/buying__product-detail.js +53 -15
- package/dist/features/tester/seed-data/authored/buying__user-dashboard-extras.js +6 -6
- package/dist/features/tester/seed-data/authored/content-discovery__search.js +6 -4
- package/dist/features/tester/seed-data/authored/design-ux__carousel-arrow-bounds.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__homepage-carousels.js +2 -2
- package/dist/features/tester/seed-data/authored/design-ux__status-badge-legibility.js +4 -4
- package/dist/features/tester/seed-data/authored/design-ux__sticky-cta-bar.js +8 -8
- package/dist/features/tester/seed-data/authored/public-pages__core-listing-pages.js +8 -8
- package/dist/features/tester/seed-data/authored/seo__canonical-and-host.js +16 -0
- package/dist/features/tester/seed-data/authored/seo__og-images.js +17 -0
- package/dist/features/tester/seed-data/stores-tester-seed-data.js +41 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +36 -6
- package/dist/features/tester/utils/phases.js +78 -12
- package/dist/features/wishlist/components/WishlistPage.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/providers/db-firebase/rtdb-paths.d.ts +7 -0
- package/dist/providers/db-firebase/rtdb-paths.js +7 -0
- package/dist/react/contexts/SessionContext.js +40 -1
- package/dist/react/hooks/useCanSeePrices.d.ts +37 -0
- package/dist/react/hooks/useCanSeePrices.js +40 -0
- package/dist/seed/addresses-seed-data.js +36 -2
- package/dist/seed/users-seed-data.js +10 -2
- package/dist/seo/json-ld.d.ts +21 -0
- package/dist/seo/json-ld.js +45 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +12 -0
- package/dist/styles.css +41 -0
- package/dist/ui/components/FilePickerButton.d.ts +32 -0
- package/dist/ui/components/FilePickerButton.js +23 -0
- package/dist/ui/components/GatedPrice.d.ts +66 -0
- package/dist/ui/components/GatedPrice.js +80 -0
- package/dist/ui/components/GatedPrice.style.css +40 -0
- package/dist/ui/components/PriceDisplay.d.ts +12 -0
- package/dist/ui/components/PriceDisplay.js +17 -17
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +8 -0
- package/package.json +1 -1
package/dist/styles.css
CHANGED
|
@@ -3295,6 +3295,47 @@
|
|
|
3295
3295
|
color: var(--appkit-color-emerald-300);
|
|
3296
3296
|
}
|
|
3297
3297
|
|
|
3298
|
+
/**
|
|
3299
|
+
* Sign-in gate for money amounts on public listing surfaces.
|
|
3300
|
+
*
|
|
3301
|
+
* The pending state must occupy the SAME vertical space as a rendered price, or
|
|
3302
|
+
* every card in a grid reflows once the session resolves. It is deliberately a
|
|
3303
|
+
* silent placeholder rather than a shimmer: the wait is short, and an animated
|
|
3304
|
+
* skeleton on 24 cards reads as a page that failed to load.
|
|
3305
|
+
*/
|
|
3306
|
+
.appkit-gated-price__pending {
|
|
3307
|
+
display: inline-block;
|
|
3308
|
+
min-width: 5rem;
|
|
3309
|
+
height: 1.25rem;
|
|
3310
|
+
border-radius: 0.25rem;
|
|
3311
|
+
background: var(--appkit-color-surface-muted);
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
.appkit-gated-price__prompt {
|
|
3315
|
+
display: inline-flex;
|
|
3316
|
+
align-items: center;
|
|
3317
|
+
gap: 0.25rem;
|
|
3318
|
+
font-size: 0.8125rem;
|
|
3319
|
+
font-weight: 600;
|
|
3320
|
+
line-height: 1.25rem;
|
|
3321
|
+
color: var(--appkit-color-primary);
|
|
3322
|
+
text-decoration: none;
|
|
3323
|
+
cursor: pointer;
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
.appkit-gated-price__prompt:hover {
|
|
3327
|
+
text-decoration: underline;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
/* A lock glyph via ::before rather than an icon component: this renders inside
|
|
3331
|
+
card grids, where 24 extra SVG trees per page is real weight for decoration
|
|
3332
|
+
that carries no meaning the label doesn't already state. */
|
|
3333
|
+
.appkit-gated-price__prompt::before {
|
|
3334
|
+
content: "\1F512";
|
|
3335
|
+
font-size: 0.75rem;
|
|
3336
|
+
line-height: 1;
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3298
3339
|
.appkit-item-row {
|
|
3299
3340
|
display: flex;
|
|
3300
3341
|
align-items: flex-start;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ButtonProps } from "./Button";
|
|
3
|
+
/**
|
|
4
|
+
* A button that opens the native file picker.
|
|
5
|
+
*
|
|
6
|
+
* 🛑 THE RAW `<input type="file">` BELONGS HERE, and only here. Rule #9 bans raw
|
|
7
|
+
* form elements in product code and points every case at a primitive; there was
|
|
8
|
+
* no primitive for a file picker, so the one feature that needed one
|
|
9
|
+
* (`DigitalContentPoolManager`) had to carry a raw input plus a suppression
|
|
10
|
+
* marker — and `audit-no-suppression-comments` is strict-zero and says, in its
|
|
11
|
+
* own header, "do NOT add a new marker or exception". The only close available
|
|
12
|
+
* was to extend the primitive layer, which is exactly what Root Cause #22 says
|
|
13
|
+
* to do before reaching for a marker.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately NOT built on `MediaUploadField`. That component drives the public
|
|
16
|
+
* `/api/media/*` pipeline, which ends by minting a `/media/{shortId}` URL served
|
|
17
|
+
* with no authentication — correct for product photography, catastrophic for a
|
|
18
|
+
* paid download. This primitive hands the caller a `File` and has no opinion
|
|
19
|
+
* about where the bytes go.
|
|
20
|
+
*
|
|
21
|
+
* The input is visually hidden rather than styled, because a styled file input
|
|
22
|
+
* cannot be made to match `<Button>` across browsers — Safari and Firefox each
|
|
23
|
+
* render their own immovable "Choose file" text inside it.
|
|
24
|
+
*/
|
|
25
|
+
export interface FilePickerButtonProps extends Omit<ButtonProps, "onClick" | "type" | "children"> {
|
|
26
|
+
/** Called with the chosen file. Never called with null. */
|
|
27
|
+
onFile: (file: File) => void;
|
|
28
|
+
/** Native `accept` string, e.g. `"image/*"`. Omit to accept anything. */
|
|
29
|
+
accept?: string;
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
}
|
|
32
|
+
export declare function FilePickerButton({ onFile, accept, children, ...buttonProps }: FilePickerButtonProps): React.JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useRef } from "react";
|
|
4
|
+
import { Button } from "./Button";
|
|
5
|
+
// No forwardRef: appkit's <Button> does not accept a ref, so there is nothing
|
|
6
|
+
// to forward one to. Adding the plumbing would be a prop that silently does
|
|
7
|
+
// nothing, which is worse than its absence.
|
|
8
|
+
export function FilePickerButton({ onFile, accept, children, ...buttonProps }) {
|
|
9
|
+
const inputRef = useRef(null);
|
|
10
|
+
const inputId = useId();
|
|
11
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { ...buttonProps, type: "button", onClick: () => inputRef.current?.click(), children: children }), _jsx("input", { ref: inputRef, id: inputId, type: "file", accept: accept, hidden: true, onChange: (ev) => {
|
|
12
|
+
const file = ev.target.files?.[0];
|
|
13
|
+
/*
|
|
14
|
+
* Reset BEFORE handing the file on. Without it, picking the same file
|
|
15
|
+
* twice in a row fires no change event the second time — the value is
|
|
16
|
+
* unchanged — and the control silently does nothing, which reads as a
|
|
17
|
+
* broken button rather than as a no-op.
|
|
18
|
+
*/
|
|
19
|
+
ev.target.value = "";
|
|
20
|
+
if (file)
|
|
21
|
+
onFile(file);
|
|
22
|
+
} })] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* 🛑 CONTRACT WITH THE STRUCTURED DATA — do not rename.
|
|
4
|
+
*
|
|
5
|
+
* The Product/Auction JSON-LD declares this exact selector in its paywalled
|
|
6
|
+
* content markup (`hasPart.cssSelector`, see `appkit/src/seo/json-ld.ts`), which
|
|
7
|
+
* is what tells Google the page legitimately carries an `offers.price` that an
|
|
8
|
+
* anonymous visitor cannot see. Rename this and the selector silently matches
|
|
9
|
+
* nothing, which reads to a crawler as an undeclared content mismatch.
|
|
10
|
+
*
|
|
11
|
+
* It is rendered in EVERY state — signed in, signed out and resolving — because
|
|
12
|
+
* the markup describes the region, not the refusal.
|
|
13
|
+
*/
|
|
14
|
+
export declare const GATED_PRICE_CLASS = "appkit-gated-price";
|
|
15
|
+
export interface GatedPriceProps {
|
|
16
|
+
/** The amount to show once the viewer is allowed to see it. */
|
|
17
|
+
amount?: number;
|
|
18
|
+
currency?: string;
|
|
19
|
+
/** Struck-through "was" price; a discount badge is derived from the pair. */
|
|
20
|
+
originalAmount?: number;
|
|
21
|
+
variant?: "compact" | "detail";
|
|
22
|
+
className?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Copy for the signed-out state. Default: "Sign in to see price". Override
|
|
25
|
+
* when the amount isn't a price — "Sign in to see the current bid".
|
|
26
|
+
*/
|
|
27
|
+
label?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Arbitrary money content to gate, for the sites that don't render a bare
|
|
30
|
+
* amount ("Buy Now — ₹1,200", "min increment ₹100", a composite row). When
|
|
31
|
+
* given, `amount` is ignored and this is what renders once allowed.
|
|
32
|
+
*/
|
|
33
|
+
children?: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Renders its children only when the viewer may see prices; renders NOTHING
|
|
37
|
+
* otherwise — no prompt, no placeholder.
|
|
38
|
+
*
|
|
39
|
+
* For SECONDARY money details that sit beside an already-gated primary amount:
|
|
40
|
+
* a handling fee, a deposit note, a "you save ₹X" line, a min-bid increment.
|
|
41
|
+
* Those read as broken copy when gated in place ("min increment Sign in to see
|
|
42
|
+
* price"), and the prompt is already on screen one line up, so repeating it is
|
|
43
|
+
* noise rather than an affordance.
|
|
44
|
+
*
|
|
45
|
+
* 🛑 Do NOT use this for a listing's primary price. A card whose price row
|
|
46
|
+
* simply vanishes reads as a rendering bug, and its height stops matching its
|
|
47
|
+
* neighbours — use `<GatedPrice>` there, which keeps the row and explains it.
|
|
48
|
+
*
|
|
49
|
+
* Renders nothing while the session resolves, so a signed-in viewer sees these
|
|
50
|
+
* appear rather than flash the wrong state.
|
|
51
|
+
*/
|
|
52
|
+
export declare function PricesOnly({ children }: {
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
}): React.JSX.Element | null;
|
|
55
|
+
/**
|
|
56
|
+
* A money amount that only signed-in visitors may read.
|
|
57
|
+
*
|
|
58
|
+
* Three states, and the middle one is the whole reason this is a component
|
|
59
|
+
* rather than an inline `user ? price : prompt` at each call site — see
|
|
60
|
+
* `useCanSeePrices` for why "resolving" must not render the prompt.
|
|
61
|
+
*
|
|
62
|
+
* The amount is still present in the page payload and in the structured data;
|
|
63
|
+
* this gates the RENDER. Withholding it from the response entirely would mean
|
|
64
|
+
* fetching prices separately on every listing page — deliberately out of scope.
|
|
65
|
+
*/
|
|
66
|
+
export declare function GatedPrice({ amount, currency, originalAmount, variant, className, label, children, }: GatedPriceProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Span } from "./Typography";
|
|
4
|
+
import { Row } from "./Layout";
|
|
5
|
+
import { ROUTES } from "../../next/routing/route-map";
|
|
6
|
+
import { getDefaultCurrency } from "../../core/baseline-resolver";
|
|
7
|
+
import { formatCurrency, formatPercentage } from "../../utils/number.formatter";
|
|
8
|
+
import { useCanSeePrices } from "../../react/hooks/useCanSeePrices";
|
|
9
|
+
/**
|
|
10
|
+
* 🛑 CONTRACT WITH THE STRUCTURED DATA — do not rename.
|
|
11
|
+
*
|
|
12
|
+
* The Product/Auction JSON-LD declares this exact selector in its paywalled
|
|
13
|
+
* content markup (`hasPart.cssSelector`, see `appkit/src/seo/json-ld.ts`), which
|
|
14
|
+
* is what tells Google the page legitimately carries an `offers.price` that an
|
|
15
|
+
* anonymous visitor cannot see. Rename this and the selector silently matches
|
|
16
|
+
* nothing, which reads to a crawler as an undeclared content mismatch.
|
|
17
|
+
*
|
|
18
|
+
* It is rendered in EVERY state — signed in, signed out and resolving — because
|
|
19
|
+
* the markup describes the region, not the refusal.
|
|
20
|
+
*/
|
|
21
|
+
export const GATED_PRICE_CLASS = "appkit-gated-price";
|
|
22
|
+
const DEFAULT_LABEL = "Sign in to see price";
|
|
23
|
+
/**
|
|
24
|
+
* Renders its children only when the viewer may see prices; renders NOTHING
|
|
25
|
+
* otherwise — no prompt, no placeholder.
|
|
26
|
+
*
|
|
27
|
+
* For SECONDARY money details that sit beside an already-gated primary amount:
|
|
28
|
+
* a handling fee, a deposit note, a "you save ₹X" line, a min-bid increment.
|
|
29
|
+
* Those read as broken copy when gated in place ("min increment Sign in to see
|
|
30
|
+
* price"), and the prompt is already on screen one line up, so repeating it is
|
|
31
|
+
* noise rather than an affordance.
|
|
32
|
+
*
|
|
33
|
+
* 🛑 Do NOT use this for a listing's primary price. A card whose price row
|
|
34
|
+
* simply vanishes reads as a rendering bug, and its height stops matching its
|
|
35
|
+
* neighbours — use `<GatedPrice>` there, which keeps the row and explains it.
|
|
36
|
+
*
|
|
37
|
+
* Renders nothing while the session resolves, so a signed-in viewer sees these
|
|
38
|
+
* appear rather than flash the wrong state.
|
|
39
|
+
*/
|
|
40
|
+
export function PricesOnly({ children }) {
|
|
41
|
+
const { canSeePrices } = useCanSeePrices();
|
|
42
|
+
if (!canSeePrices)
|
|
43
|
+
return null;
|
|
44
|
+
return _jsx(_Fragment, { children: children });
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A money amount that only signed-in visitors may read.
|
|
48
|
+
*
|
|
49
|
+
* Three states, and the middle one is the whole reason this is a component
|
|
50
|
+
* rather than an inline `user ? price : prompt` at each call site — see
|
|
51
|
+
* `useCanSeePrices` for why "resolving" must not render the prompt.
|
|
52
|
+
*
|
|
53
|
+
* The amount is still present in the page payload and in the structured data;
|
|
54
|
+
* this gates the RENDER. Withholding it from the response entirely would mean
|
|
55
|
+
* fetching prices separately on every listing page — deliberately out of scope.
|
|
56
|
+
*/
|
|
57
|
+
export function GatedPrice({ amount, currency = getDefaultCurrency(), originalAmount, variant = "compact", className, label = DEFAULT_LABEL, children, }) {
|
|
58
|
+
const { canSeePrices, isAuthResolving } = useCanSeePrices();
|
|
59
|
+
const rootClass = `${GATED_PRICE_CLASS} ${className ?? ""}`.trim();
|
|
60
|
+
if (isAuthResolving) {
|
|
61
|
+
return (_jsx(Span, { className: `${rootClass} appkit-gated-price__pending`, "aria-hidden": "true" }));
|
|
62
|
+
}
|
|
63
|
+
if (!canSeePrices) {
|
|
64
|
+
return (_jsx("a", { href: String(ROUTES.AUTH.LOGIN), className: `${rootClass} appkit-gated-price__prompt`, children: label }));
|
|
65
|
+
}
|
|
66
|
+
if (children !== undefined) {
|
|
67
|
+
return _jsx(Span, { className: rootClass, children: children });
|
|
68
|
+
}
|
|
69
|
+
if (amount === undefined)
|
|
70
|
+
return null;
|
|
71
|
+
const hasDiscount = originalAmount !== undefined && originalAmount > amount;
|
|
72
|
+
const discountPct = hasDiscount ? (originalAmount - amount) / originalAmount : 0;
|
|
73
|
+
const priceClass = variant === "detail"
|
|
74
|
+
? "appkit-price-display__price appkit-price-display__price--detail"
|
|
75
|
+
: "appkit-price-display__price appkit-price-display__price--compact";
|
|
76
|
+
const originalClass = variant === "detail"
|
|
77
|
+
? "appkit-price-display__original appkit-price-display__original--detail"
|
|
78
|
+
: "appkit-price-display__original appkit-price-display__original--compact";
|
|
79
|
+
return (_jsxs(Row, { wrap: true, align: "baseline", gap: "xs", className: `appkit-price-display ${rootClass}`, children: [_jsx(Span, { className: priceClass, children: formatCurrency(amount, currency) }), hasDiscount && (_jsxs(_Fragment, { children: [_jsx(Span, { className: originalClass, children: formatCurrency(originalAmount, currency) }), _jsxs(Span, { className: "appkit-price-display__discount", children: ["-", formatPercentage(discountPct, 0)] })] }))] }));
|
|
80
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign-in gate for money amounts on public listing surfaces.
|
|
3
|
+
*
|
|
4
|
+
* The pending state must occupy the SAME vertical space as a rendered price, or
|
|
5
|
+
* every card in a grid reflows once the session resolves. It is deliberately a
|
|
6
|
+
* silent placeholder rather than a shimmer: the wait is short, and an animated
|
|
7
|
+
* skeleton on 24 cards reads as a page that failed to load.
|
|
8
|
+
*/
|
|
9
|
+
.appkit-gated-price__pending {
|
|
10
|
+
display: inline-block;
|
|
11
|
+
min-width: 5rem;
|
|
12
|
+
height: 1.25rem;
|
|
13
|
+
border-radius: 0.25rem;
|
|
14
|
+
background: var(--appkit-color-surface-muted);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.appkit-gated-price__prompt {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
gap: 0.25rem;
|
|
21
|
+
font-size: 0.8125rem;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
line-height: 1.25rem;
|
|
24
|
+
color: var(--appkit-color-primary);
|
|
25
|
+
text-decoration: none;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.appkit-gated-price__prompt:hover {
|
|
30
|
+
text-decoration: underline;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* A lock glyph via ::before rather than an icon component: this renders inside
|
|
34
|
+
card grids, where 24 extra SVG trees per page is real weight for decoration
|
|
35
|
+
that carries no meaning the label doesn't already state. */
|
|
36
|
+
.appkit-gated-price__prompt::before {
|
|
37
|
+
content: "\1F512";
|
|
38
|
+
font-size: 0.75rem;
|
|
39
|
+
line-height: 1;
|
|
40
|
+
}
|
|
@@ -5,4 +5,16 @@ export interface PriceDisplayProps {
|
|
|
5
5
|
variant?: "compact" | "detail";
|
|
6
6
|
className?: string;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* A product price on a public surface — gated behind sign-in.
|
|
10
|
+
*
|
|
11
|
+
* This is now a thin wrapper over `<GatedPrice>`, with no opt-out, because both
|
|
12
|
+
* of its consumers (`ProductGrid`'s card + list rows, and the generic
|
|
13
|
+
* `ProductDetailPageView`) are public listing surfaces. There is deliberately no
|
|
14
|
+
* `ungated` prop: nothing in admin, seller, cart, checkout or orders uses this
|
|
15
|
+
* component, so an escape hatch here would exist only to be misused. Those
|
|
16
|
+
* surfaces call `formatCurrency` directly and are signed-in by definition.
|
|
17
|
+
*
|
|
18
|
+
* Rendering, discount badge and markup are unchanged for a signed-in viewer.
|
|
19
|
+
*/
|
|
8
20
|
export declare function PriceDisplay({ amount, currency, originalAmount, variant, className, }: PriceDisplayProps): import("react").JSX.Element;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return (
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { GatedPrice } from "./GatedPrice";
|
|
4
|
+
/**
|
|
5
|
+
* A product price on a public surface — gated behind sign-in.
|
|
6
|
+
*
|
|
7
|
+
* This is now a thin wrapper over `<GatedPrice>`, with no opt-out, because both
|
|
8
|
+
* of its consumers (`ProductGrid`'s card + list rows, and the generic
|
|
9
|
+
* `ProductDetailPageView`) are public listing surfaces. There is deliberately no
|
|
10
|
+
* `ungated` prop: nothing in admin, seller, cart, checkout or orders uses this
|
|
11
|
+
* component, so an escape hatch here would exist only to be misused. Those
|
|
12
|
+
* surfaces call `formatCurrency` directly and are signed-in by definition.
|
|
13
|
+
*
|
|
14
|
+
* Rendering, discount badge and markup are unchanged for a signed-in viewer.
|
|
15
|
+
*/
|
|
16
|
+
export function PriceDisplay({ amount, currency, originalAmount, variant = "compact", className, }) {
|
|
17
|
+
return (_jsx(GatedPrice, { amount: amount, currency: currency, originalAmount: originalAmount, variant: variant, className: className }));
|
|
18
18
|
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export type { SkeletonProps } from "./components/Skeleton";
|
|
|
33
33
|
export { Skeleton } from "./components/Skeleton";
|
|
34
34
|
export type { ButtonProps } from "./components/Button";
|
|
35
35
|
export { Button } from "./components/Button";
|
|
36
|
+
export { FilePickerButton } from "./components/FilePickerButton";
|
|
37
|
+
export type { FilePickerButtonProps } from "./components/FilePickerButton";
|
|
36
38
|
export type { IconButtonProps } from "./components/IconButton";
|
|
37
39
|
export { IconButton } from "./components/IconButton";
|
|
38
40
|
export type { IconProps, IconTone } from "./components/Icon";
|
|
@@ -115,6 +117,8 @@ export type { RatingDisplayProps } from "./components/RatingDisplay";
|
|
|
115
117
|
export { RatingDisplay } from "./components/RatingDisplay";
|
|
116
118
|
export type { PriceDisplayProps } from "./components/PriceDisplay";
|
|
117
119
|
export { PriceDisplay } from "./components/PriceDisplay";
|
|
120
|
+
export type { GatedPriceProps } from "./components/GatedPrice";
|
|
121
|
+
export { GatedPrice, PricesOnly, GATED_PRICE_CLASS } from "./components/GatedPrice";
|
|
118
122
|
export type { StatsGridProps, StatItem } from "./components/StatsGrid";
|
|
119
123
|
export { StatsGrid } from "./components/StatsGrid";
|
|
120
124
|
export type { SummaryCardProps, SummaryLine } from "./components/SummaryCard";
|
package/dist/ui/index.js
CHANGED
|
@@ -19,6 +19,13 @@ export { SiteLogo } from "./components/SiteLogo";
|
|
|
19
19
|
export { SiteMark } from "./components/SiteMark";
|
|
20
20
|
export { Skeleton } from "./components/Skeleton";
|
|
21
21
|
export { Button } from "./components/Button";
|
|
22
|
+
/*
|
|
23
|
+
* The one primitive allowed to own a raw <input type="file">. Rule #9 sends
|
|
24
|
+
* every raw form element to a primitive and there was none for a file picker,
|
|
25
|
+
* so the only feature that needed one had to carry a suppression marker that
|
|
26
|
+
* audit-no-suppression-comments (strict-zero) correctly refuses.
|
|
27
|
+
*/
|
|
28
|
+
export { FilePickerButton } from "./components/FilePickerButton";
|
|
22
29
|
export { IconButton } from "./components/IconButton";
|
|
23
30
|
export { Icon } from "./components/Icon";
|
|
24
31
|
export { ICONS, ICON_SIZE, isIconKey, resolveIcon, } from "./icons/icon-registry";
|
|
@@ -60,6 +67,7 @@ export { StepperNav } from "./components/StepperNav";
|
|
|
60
67
|
export { ViewToggle } from "./components/ViewToggle";
|
|
61
68
|
export { RatingDisplay } from "./components/RatingDisplay";
|
|
62
69
|
export { PriceDisplay } from "./components/PriceDisplay";
|
|
70
|
+
export { GatedPrice, PricesOnly, GATED_PRICE_CLASS } from "./components/GatedPrice";
|
|
63
71
|
export { StatsGrid } from "./components/StatsGrid";
|
|
64
72
|
export { SummaryCard } from "./components/SummaryCard";
|
|
65
73
|
export { CountdownDisplay } from "./components/CountdownDisplay";
|