@decocms/apps 0.20.1
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/.github/workflows/release.yml +34 -0
- package/.releaserc.json +25 -0
- package/commerce/components/Image.tsx +209 -0
- package/commerce/components/JsonLd.tsx +285 -0
- package/commerce/sdk/analytics.ts +24 -0
- package/commerce/sdk/formatPrice.ts +23 -0
- package/commerce/sdk/url.ts +9 -0
- package/commerce/sdk/useOffer.ts +75 -0
- package/commerce/sdk/useVariantPossibilities.ts +43 -0
- package/commerce/types/commerce.ts +1105 -0
- package/commerce/utils/canonical.ts +11 -0
- package/commerce/utils/constants.ts +9 -0
- package/commerce/utils/filters.ts +10 -0
- package/commerce/utils/productToAnalyticsItem.ts +67 -0
- package/commerce/utils/stateByZip.ts +50 -0
- package/knip.json +19 -0
- package/package.json +77 -0
- package/shopify/actions/cart/addItems.ts +37 -0
- package/shopify/actions/cart/updateCoupons.ts +32 -0
- package/shopify/actions/cart/updateItems.ts +32 -0
- package/shopify/actions/user/signIn.ts +45 -0
- package/shopify/actions/user/signUp.ts +36 -0
- package/shopify/client.ts +58 -0
- package/shopify/index.ts +32 -0
- package/shopify/init.ts +40 -0
- package/shopify/loaders/ProductDetailsPage.ts +35 -0
- package/shopify/loaders/ProductList.ts +101 -0
- package/shopify/loaders/ProductListingPage.ts +180 -0
- package/shopify/loaders/RelatedProducts.ts +45 -0
- package/shopify/loaders/cart.ts +73 -0
- package/shopify/loaders/shop.ts +40 -0
- package/shopify/loaders/user.ts +44 -0
- package/shopify/utils/admin/admin.ts +57 -0
- package/shopify/utils/admin/queries.ts +29 -0
- package/shopify/utils/cart.ts +28 -0
- package/shopify/utils/cookies.ts +85 -0
- package/shopify/utils/enums.ts +438 -0
- package/shopify/utils/graphql.ts +69 -0
- package/shopify/utils/storefront/queries.ts +530 -0
- package/shopify/utils/storefront/storefront.graphql.gen.ts +113 -0
- package/shopify/utils/transform.ts +436 -0
- package/shopify/utils/types.ts +191 -0
- package/shopify/utils/user.ts +23 -0
- package/shopify/utils/utils.ts +164 -0
- package/tsconfig.json +11 -0
- package/vtex/README.md +6 -0
- package/vtex/actions/address.ts +211 -0
- package/vtex/actions/auth.ts +337 -0
- package/vtex/actions/checkout.ts +497 -0
- package/vtex/actions/index.ts +11 -0
- package/vtex/actions/masterData.ts +170 -0
- package/vtex/actions/misc.ts +196 -0
- package/vtex/actions/newsletter.ts +108 -0
- package/vtex/actions/orders.ts +37 -0
- package/vtex/actions/profile.ts +119 -0
- package/vtex/actions/session.ts +87 -0
- package/vtex/actions/trigger.ts +43 -0
- package/vtex/actions/wishlist.ts +116 -0
- package/vtex/client.ts +423 -0
- package/vtex/hooks/index.ts +4 -0
- package/vtex/hooks/useAutocomplete.ts +89 -0
- package/vtex/hooks/useCart.ts +219 -0
- package/vtex/hooks/useUser.ts +78 -0
- package/vtex/hooks/useWishlist.ts +119 -0
- package/vtex/index.ts +14 -0
- package/vtex/inline-loaders/productDetailsPage.ts +75 -0
- package/vtex/inline-loaders/productList.ts +163 -0
- package/vtex/inline-loaders/productListingPage.ts +447 -0
- package/vtex/inline-loaders/relatedProducts.ts +83 -0
- package/vtex/inline-loaders/suggestions.ts +49 -0
- package/vtex/inline-loaders/workflowProducts.ts +68 -0
- package/vtex/invoke.ts +202 -0
- package/vtex/loaders/address.ts +120 -0
- package/vtex/loaders/brands.ts +51 -0
- package/vtex/loaders/cart.ts +49 -0
- package/vtex/loaders/catalog.ts +165 -0
- package/vtex/loaders/collections.ts +57 -0
- package/vtex/loaders/index.ts +19 -0
- package/vtex/loaders/legacy.ts +671 -0
- package/vtex/loaders/logistics.ts +115 -0
- package/vtex/loaders/navbar.ts +29 -0
- package/vtex/loaders/orders.ts +103 -0
- package/vtex/loaders/pageType.ts +62 -0
- package/vtex/loaders/payment.ts +107 -0
- package/vtex/loaders/profile.ts +138 -0
- package/vtex/loaders/promotion.ts +33 -0
- package/vtex/loaders/search.ts +127 -0
- package/vtex/loaders/session.ts +91 -0
- package/vtex/loaders/user.ts +89 -0
- package/vtex/loaders/wishlist.ts +89 -0
- package/vtex/loaders/wishlistProducts.ts +81 -0
- package/vtex/loaders/workflow.ts +323 -0
- package/vtex/logo.png +0 -0
- package/vtex/middleware.ts +229 -0
- package/vtex/types.ts +248 -0
- package/vtex/utils/batch.ts +21 -0
- package/vtex/utils/cookies.ts +76 -0
- package/vtex/utils/enrichment.ts +540 -0
- package/vtex/utils/fetchCache.ts +150 -0
- package/vtex/utils/index.ts +17 -0
- package/vtex/utils/intelligentSearch.ts +84 -0
- package/vtex/utils/legacy.ts +155 -0
- package/vtex/utils/pickAndOmit.ts +30 -0
- package/vtex/utils/proxy.ts +196 -0
- package/vtex/utils/resourceRange.ts +10 -0
- package/vtex/utils/segment.ts +163 -0
- package/vtex/utils/similars.ts +38 -0
- package/vtex/utils/sitemap.ts +133 -0
- package/vtex/utils/slugCache.ts +32 -0
- package/vtex/utils/slugify.ts +13 -0
- package/vtex/utils/transform.ts +1331 -0
- package/vtex/utils/types.ts +1884 -0
- package/vtex/utils/vtexId.ts +103 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VTEX search-related loaders (Intelligent Search + Catalog).
|
|
3
|
+
* Pure async functions — require configureVtex() to have been called.
|
|
4
|
+
*
|
|
5
|
+
* Ported from deco-cx/apps:
|
|
6
|
+
* vtex/loaders/intelligentSearch/topsearches.ts
|
|
7
|
+
* vtex/loaders/intelligentSearch/productSearchValidator.ts
|
|
8
|
+
* vtex/loaders/options/productIdByTerm.ts
|
|
9
|
+
*
|
|
10
|
+
* @see https://developers.vtex.com/docs/api-reference/intelligent-search-api
|
|
11
|
+
*/
|
|
12
|
+
import { intelligentSearch, getVtexConfig } from "../client";
|
|
13
|
+
import type { Suggestion } from "../utils/types";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// getTopSearches
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the top searches from Intelligent Search.
|
|
21
|
+
*
|
|
22
|
+
* @param locale - BCP-47 locale (defaults to the configured locale or "pt-BR")
|
|
23
|
+
*/
|
|
24
|
+
export async function getTopSearches(locale?: string): Promise<Suggestion> {
|
|
25
|
+
const cfg = getVtexConfig();
|
|
26
|
+
const effectiveLocale = locale ?? cfg.locale ?? "pt-BR";
|
|
27
|
+
|
|
28
|
+
return intelligentSearch<Suggestion>("/top_searches", {
|
|
29
|
+
locale: effectiveLocale,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// validateProductSearch
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
export interface FacetsSearchProps {
|
|
38
|
+
query?: string;
|
|
39
|
+
facets?: string;
|
|
40
|
+
sort?: string;
|
|
41
|
+
count?: number;
|
|
42
|
+
page?: number;
|
|
43
|
+
locale?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validate whether a product search returns results.
|
|
48
|
+
*
|
|
49
|
+
* Runs the given search parameters against Intelligent Search.
|
|
50
|
+
* If no results are found and the props include facets, retries
|
|
51
|
+
* the search without facets.
|
|
52
|
+
*
|
|
53
|
+
* Returns the raw IS response or `null` when nothing is found.
|
|
54
|
+
*/
|
|
55
|
+
export async function validateProductSearch<T = unknown>(
|
|
56
|
+
props: FacetsSearchProps,
|
|
57
|
+
fetcher: (props: FacetsSearchProps) => Promise<T[] | null>,
|
|
58
|
+
): Promise<T[] | null> {
|
|
59
|
+
const results = await fetcher(props);
|
|
60
|
+
if (results !== null && results.length > 0) return results;
|
|
61
|
+
|
|
62
|
+
if (props.facets) {
|
|
63
|
+
return fetcher({ ...props, facets: "" });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// getProductIdByTerm
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
interface ProductIdOption {
|
|
74
|
+
value: string;
|
|
75
|
+
label: string;
|
|
76
|
+
image?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface ISSuggestionProduct {
|
|
80
|
+
productId: string;
|
|
81
|
+
productName: string;
|
|
82
|
+
brand: string;
|
|
83
|
+
linkText: string;
|
|
84
|
+
items: Array<{
|
|
85
|
+
itemId: string;
|
|
86
|
+
name: string;
|
|
87
|
+
images: Array<{ imageUrl: string; imageText: string }>;
|
|
88
|
+
sellers: Array<{
|
|
89
|
+
commertialOffer: { Price: number; ListPrice: number };
|
|
90
|
+
}>;
|
|
91
|
+
}>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface ISSuggestionResponse {
|
|
95
|
+
searches: Array<{ term: string; count: number }>;
|
|
96
|
+
products: ISSuggestionProduct[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Search for products by free-text term and return a list of
|
|
101
|
+
* `{ value (SKU ID), label, image }` options.
|
|
102
|
+
*
|
|
103
|
+
* Hits the IS autocomplete_suggestions endpoint.
|
|
104
|
+
*/
|
|
105
|
+
export async function getProductIdByTerm(
|
|
106
|
+
term?: string,
|
|
107
|
+
): Promise<ProductIdOption[]> {
|
|
108
|
+
const query = (term ?? "").trim();
|
|
109
|
+
if (!query) return [];
|
|
110
|
+
|
|
111
|
+
const data = await intelligentSearch<ISSuggestionResponse>(
|
|
112
|
+
"/autocomplete_suggestions/",
|
|
113
|
+
{ query },
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
if (!data.products?.length) {
|
|
117
|
+
return [{ value: "No products found", label: "No products found" }];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return data.products.flatMap((product) =>
|
|
121
|
+
(product.items ?? []).map((item) => ({
|
|
122
|
+
value: item.itemId,
|
|
123
|
+
label: `${item.itemId} - ${product.productName} ${item.name} - ${product.productId}`,
|
|
124
|
+
image: item.images?.[0]?.imageUrl,
|
|
125
|
+
})),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VTEX Session API loaders.
|
|
3
|
+
* Pure async functions — require configureVtex() to have been called.
|
|
4
|
+
*
|
|
5
|
+
* Ported from deco-cx/apps:
|
|
6
|
+
* vtex/loaders/session/getSession.ts
|
|
7
|
+
* vtex/loaders/session/getUserSessions.ts
|
|
8
|
+
*
|
|
9
|
+
* @see https://developers.vtex.com/docs/api-reference/session-manager-api
|
|
10
|
+
*/
|
|
11
|
+
import { vtexFetch, vtexIOGraphQL } from "../client";
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// getSession (REST)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Fetch the current session data.
|
|
19
|
+
*
|
|
20
|
+
* @param items - Keys to retrieve, e.g. `["public.variable1", "profile.email"]`.
|
|
21
|
+
* Pass `["*"]` (or omit) to retrieve all keys.
|
|
22
|
+
* @param authCookie - The raw `cookie` header value forwarded from the user request.
|
|
23
|
+
*
|
|
24
|
+
* @see https://developers.vtex.com/docs/api-reference/session-manager-api#get-/api/sessions
|
|
25
|
+
*/
|
|
26
|
+
export async function getSession<T = any>(
|
|
27
|
+
items: string[] = ["*"],
|
|
28
|
+
authCookie: string,
|
|
29
|
+
): Promise<T> {
|
|
30
|
+
const qs = new URLSearchParams({ items: items.join(",") });
|
|
31
|
+
return vtexFetch<T>(`/api/sessions?${qs}`, {
|
|
32
|
+
headers: { cookie: authCookie },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// getUserSessions (authenticated — VTEX IO GraphQL)
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export interface LoginSession {
|
|
41
|
+
id: string;
|
|
42
|
+
cacheId: string;
|
|
43
|
+
deviceType: string;
|
|
44
|
+
city: string;
|
|
45
|
+
lastAccess: string;
|
|
46
|
+
browser: string;
|
|
47
|
+
os: string;
|
|
48
|
+
ip: string;
|
|
49
|
+
fullAddress: string;
|
|
50
|
+
firstAccess: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface LoginSessionsInfo {
|
|
54
|
+
currentLoginSessionId: string;
|
|
55
|
+
loginSessions: LoginSession[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const USER_SESSIONS_QUERY = `query getUserSessions {
|
|
59
|
+
loginSessionsInfo {
|
|
60
|
+
currentLoginSessionId
|
|
61
|
+
loginSessions {
|
|
62
|
+
id
|
|
63
|
+
cacheId
|
|
64
|
+
deviceType
|
|
65
|
+
city
|
|
66
|
+
lastAccess
|
|
67
|
+
browser
|
|
68
|
+
os
|
|
69
|
+
ip
|
|
70
|
+
fullAddress
|
|
71
|
+
firstAccess
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}`;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fetch all active login sessions for the currently authenticated user.
|
|
78
|
+
* Requires a valid VTEX auth cookie.
|
|
79
|
+
*/
|
|
80
|
+
export async function getUserSessions(
|
|
81
|
+
authCookie: string,
|
|
82
|
+
): Promise<LoginSessionsInfo> {
|
|
83
|
+
const { loginSessionsInfo } = await vtexIOGraphQL<{
|
|
84
|
+
loginSessionsInfo: LoginSessionsInfo;
|
|
85
|
+
}>(
|
|
86
|
+
{ query: USER_SESSIONS_QUERY },
|
|
87
|
+
{ cookie: authCookie },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return loginSessionsInfo;
|
|
91
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VTEX User API loader.
|
|
3
|
+
* Pure async function — requires configureVtex() to have been called.
|
|
4
|
+
*
|
|
5
|
+
* Ported from deco-cx/apps:
|
|
6
|
+
* vtex/loaders/user.ts
|
|
7
|
+
*
|
|
8
|
+
* @see https://developers.vtex.com/docs/api-reference/checkout-api
|
|
9
|
+
*/
|
|
10
|
+
import { vtexIOGraphQL } from "../client";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export interface Person {
|
|
17
|
+
"@id": string;
|
|
18
|
+
email: string;
|
|
19
|
+
givenName?: string;
|
|
20
|
+
familyName?: string;
|
|
21
|
+
taxID?: string;
|
|
22
|
+
gender?: string;
|
|
23
|
+
telephone?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface VtexUser {
|
|
27
|
+
id: string;
|
|
28
|
+
userId: string;
|
|
29
|
+
email: string;
|
|
30
|
+
firstName?: string;
|
|
31
|
+
lastName?: string;
|
|
32
|
+
profilePicture?: string;
|
|
33
|
+
gender?: string;
|
|
34
|
+
document?: string;
|
|
35
|
+
homePhone?: string;
|
|
36
|
+
businessPhone?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// getUser (authenticated — VTEX IO GraphQL)
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
const USER_QUERY = `query getUserProfile {
|
|
44
|
+
profile {
|
|
45
|
+
id
|
|
46
|
+
userId
|
|
47
|
+
email
|
|
48
|
+
firstName
|
|
49
|
+
lastName
|
|
50
|
+
profilePicture
|
|
51
|
+
gender
|
|
52
|
+
document
|
|
53
|
+
homePhone
|
|
54
|
+
businessPhone
|
|
55
|
+
}
|
|
56
|
+
}`;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Fetch the authenticated user as a Schema.org-style `Person`.
|
|
60
|
+
* Returns `null` when no valid session exists or the query fails.
|
|
61
|
+
*
|
|
62
|
+
* @param authCookie - Raw `cookie` header value from the user request.
|
|
63
|
+
*/
|
|
64
|
+
export async function getUser(
|
|
65
|
+
authCookie: string,
|
|
66
|
+
): Promise<Person | null> {
|
|
67
|
+
try {
|
|
68
|
+
const { profile: user } = await vtexIOGraphQL<{ profile: VtexUser }>(
|
|
69
|
+
{ query: USER_QUERY },
|
|
70
|
+
{ cookie: authCookie },
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
"@id": user.userId ?? user.id,
|
|
75
|
+
email: user.email,
|
|
76
|
+
givenName: user.firstName,
|
|
77
|
+
familyName: user.lastName,
|
|
78
|
+
taxID: user.document?.replace(/[^\d]/g, ""),
|
|
79
|
+
gender: user.gender
|
|
80
|
+
? user.gender === "f"
|
|
81
|
+
? "https://schema.org/Female"
|
|
82
|
+
: "https://schema.org/Male"
|
|
83
|
+
: undefined,
|
|
84
|
+
telephone: user.homePhone ?? user.businessPhone,
|
|
85
|
+
};
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VTEX Wishlist API loader.
|
|
3
|
+
* Pure async function — requires configureVtex() to have been called.
|
|
4
|
+
*
|
|
5
|
+
* Ported from deco-cx/apps:
|
|
6
|
+
* vtex/loaders/wishlist.ts
|
|
7
|
+
*
|
|
8
|
+
* @see https://developers.vtex.com/docs/guides/vtex-wish-list
|
|
9
|
+
*/
|
|
10
|
+
import { vtexIOGraphQL } from "../client";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export interface WishlistItem {
|
|
17
|
+
id: string;
|
|
18
|
+
productId: string;
|
|
19
|
+
sku: string;
|
|
20
|
+
title: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface GetWishlistOpts {
|
|
24
|
+
shopperId: string;
|
|
25
|
+
count?: number;
|
|
26
|
+
page?: number;
|
|
27
|
+
allRecords?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// getWishlist (authenticated — VTEX IO GraphQL)
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
const WISHLIST_QUERY = `query GetWishlist($shopperId: String!, $name: String!, $from: Int, $to: Int) {
|
|
35
|
+
viewList(shopperId: $shopperId, name: $name, from: $from, to: $to)
|
|
36
|
+
@context(provider: "vtex.wish-list@1.x") {
|
|
37
|
+
name
|
|
38
|
+
data {
|
|
39
|
+
id
|
|
40
|
+
productId
|
|
41
|
+
sku
|
|
42
|
+
title
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}`;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fetch the wishlist for a given shopper.
|
|
49
|
+
* Requires a valid VTEX auth cookie.
|
|
50
|
+
*
|
|
51
|
+
* @param authCookie - Raw `cookie` header value from the user request.
|
|
52
|
+
* @param opts.shopperId - The shopper identifier (usually the e-mail from the JWT `sub` claim).
|
|
53
|
+
* @param opts.count - Items per page (default: all).
|
|
54
|
+
* @param opts.page - Zero-based page index (default: 0).
|
|
55
|
+
* @param opts.allRecords - When true, ignores pagination and returns every item.
|
|
56
|
+
*/
|
|
57
|
+
export async function getWishlist(
|
|
58
|
+
authCookie: string,
|
|
59
|
+
opts: GetWishlistOpts,
|
|
60
|
+
): Promise<WishlistItem[]> {
|
|
61
|
+
try {
|
|
62
|
+
const { viewList } = await vtexIOGraphQL<{
|
|
63
|
+
viewList: { name?: string; data: WishlistItem[] };
|
|
64
|
+
}>(
|
|
65
|
+
{
|
|
66
|
+
operationName: "GetWishlist",
|
|
67
|
+
query: WISHLIST_QUERY,
|
|
68
|
+
variables: {
|
|
69
|
+
name: "Wishlist",
|
|
70
|
+
shopperId: opts.shopperId,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{ cookie: authCookie },
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const data = viewList.data ?? [];
|
|
77
|
+
|
|
78
|
+
if (opts.allRecords) return data;
|
|
79
|
+
|
|
80
|
+
const count = opts.count ?? Infinity;
|
|
81
|
+
const page = opts.page ?? 0;
|
|
82
|
+
return data.slice(count * page, count * (page + 1));
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wishlist Products loader.
|
|
3
|
+
* Returns a ProductListingPage built from the user's wishlist items.
|
|
4
|
+
*
|
|
5
|
+
* Ported from deco-cx/apps vtex/loaders/product/wishlist.ts
|
|
6
|
+
*/
|
|
7
|
+
import type {
|
|
8
|
+
Product,
|
|
9
|
+
ProductListingPage,
|
|
10
|
+
} from "../../commerce/types/commerce";
|
|
11
|
+
import { getWishlist } from "./wishlist";
|
|
12
|
+
|
|
13
|
+
export interface WishlistProductsProps {
|
|
14
|
+
/** Items per page @default 12 */
|
|
15
|
+
count?: number;
|
|
16
|
+
/** 1 to start from index 1 @default 0 */
|
|
17
|
+
offset?: 0 | 1;
|
|
18
|
+
/** The user's auth cookie string */
|
|
19
|
+
authCookie: string;
|
|
20
|
+
/** The user's shopper ID (email) */
|
|
21
|
+
shopperId: string;
|
|
22
|
+
/** Current page URL (for pagination links) */
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function withPage(baseUrl: string, page: number): string {
|
|
27
|
+
const url = new URL(baseUrl);
|
|
28
|
+
url.searchParams.set("page", `${page}`);
|
|
29
|
+
return `?${url.searchParams}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function wishlistProducts(
|
|
33
|
+
props: WishlistProductsProps,
|
|
34
|
+
): Promise<ProductListingPage | null> {
|
|
35
|
+
const {
|
|
36
|
+
count: recordPerPage = 12,
|
|
37
|
+
offset = 0,
|
|
38
|
+
authCookie,
|
|
39
|
+
shopperId,
|
|
40
|
+
url: rawUrl,
|
|
41
|
+
} = props;
|
|
42
|
+
|
|
43
|
+
const url = new URL(rawUrl);
|
|
44
|
+
const page = Math.max(
|
|
45
|
+
0,
|
|
46
|
+
Number(url.searchParams.get("page") ?? offset) - offset,
|
|
47
|
+
);
|
|
48
|
+
const items = await getWishlist(authCookie, { shopperId, allRecords: true });
|
|
49
|
+
const records = items.length;
|
|
50
|
+
const start = page * recordPerPage;
|
|
51
|
+
const end = (page + 1) * recordPerPage;
|
|
52
|
+
|
|
53
|
+
const products: Product[] = items
|
|
54
|
+
.map(({ sku, productId }) => ({
|
|
55
|
+
"@type": "Product" as const,
|
|
56
|
+
inProductGroupWithID: productId,
|
|
57
|
+
productID: sku,
|
|
58
|
+
sku,
|
|
59
|
+
}))
|
|
60
|
+
.slice(start, end);
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
"@type": "ProductListingPage",
|
|
64
|
+
breadcrumb: {
|
|
65
|
+
"@type": "BreadcrumbList",
|
|
66
|
+
itemListElement: [],
|
|
67
|
+
numberOfItems: 0,
|
|
68
|
+
},
|
|
69
|
+
filters: [],
|
|
70
|
+
products,
|
|
71
|
+
pageInfo: {
|
|
72
|
+
currentPage: page + offset,
|
|
73
|
+
nextPage: records > end ? withPage(rawUrl, page + 1 + offset) : undefined,
|
|
74
|
+
previousPage: page > 0 ? withPage(rawUrl, page - 1 + offset) : undefined,
|
|
75
|
+
recordPerPage,
|
|
76
|
+
records,
|
|
77
|
+
},
|
|
78
|
+
sortOptions: [],
|
|
79
|
+
seo: null,
|
|
80
|
+
};
|
|
81
|
+
}
|