@decocms/apps-wake 7.72.0 → 8.1.0-next.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/package.json +8 -29
- package/src/index.ts +9 -56
- package/src/storefront.ts +13 -0
- package/src/storefrontTypes.ts +6 -0
- package/src/utils/gql.ts +23 -0
- package/src/utils/graphql/queries.ts +1 -1
- package/src/wakeClient.ts +104 -0
- package/src/actions/cart/addCoupon.ts +0 -43
- package/src/actions/cart/addItem.ts +0 -10
- package/src/actions/cart/addItems.ts +0 -50
- package/src/actions/cart/addKit.ts +0 -53
- package/src/actions/cart/partnerAssociate.ts +0 -48
- package/src/actions/cart/partnerDisassociate.ts +0 -43
- package/src/actions/cart/removeCoupon.ts +0 -39
- package/src/actions/cart/removeKit.ts +0 -56
- package/src/actions/cart/updateItemQuantity.ts +0 -69
- package/src/actions/newsletter/register.ts +0 -32
- package/src/actions/notifyme.ts +0 -28
- package/src/actions/review/create.ts +0 -30
- package/src/actions/shippingSimulation.ts +0 -60
- package/src/actions/submmitForm.ts +0 -25
- package/src/actions/wishlist/addProduct.ts +0 -43
- package/src/actions/wishlist/removeProduct.ts +0 -45
- package/src/client.ts +0 -123
- package/src/commerceLoaders.ts +0 -137
- package/src/handlers/sitemap.ts +0 -61
- package/src/loaders/cart.ts +0 -46
- package/src/loaders/partners.ts +0 -58
- package/src/loaders/productDetailsPage.ts +0 -174
- package/src/loaders/productList.ts +0 -262
- package/src/loaders/productListingPage.ts +0 -333
- package/src/loaders/proxy.ts +0 -79
- package/src/loaders/recommendations.ts +0 -84
- package/src/loaders/shop.ts +0 -37
- package/src/loaders/suggestion.ts +0 -67
- package/src/loaders/user.ts +0 -48
- package/src/loaders/wishlist.ts +0 -40
- package/src/manifest.gen.ts +0 -69
- package/src/mod.ts +0 -88
- package/src/registry.ts +0 -9
- package/src/utils/authError.ts +0 -107
- package/src/utils/authenticate.ts +0 -42
- package/src/utils/capitalize.ts +0 -9
- package/src/utils/cart.ts +0 -33
- package/src/utils/cookies.ts +0 -82
- package/src/utils/getVariations.ts +0 -47
- package/src/utils/graphql.ts +0 -88
- package/src/utils/httpError.ts +0 -15
- package/src/utils/instrumentedFetch.ts +0 -46
- package/src/utils/operationRouter.ts +0 -51
- package/src/utils/parseHeaders.ts +0 -19
- package/src/utils/partner.ts +0 -34
- package/src/utils/requestCtx.ts +0 -41
- package/src/utils/transform.ts +0 -460
- package/src/utils/types.ts +0 -6
- package/src/utils/user.ts +0 -9
- package/tsconfig.json +0 -7
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
import type { ProductListingPage, SortOption } from "@decocms/apps-commerce/types";
|
|
2
|
-
import partnerAssociate from "../actions/cart/partnerAssociate";
|
|
3
|
-
import { getWakeClient } from "../client";
|
|
4
|
-
import { handleAuthError } from "../utils/authError";
|
|
5
|
-
import { capitalize } from "../utils/capitalize";
|
|
6
|
-
import { getVariations, MAXIMUM_REQUEST_QUANTITY } from "../utils/getVariations";
|
|
7
|
-
import { GetPartners, GetURL, Hotsite, Search } from "../utils/graphql/queries";
|
|
8
|
-
import type {
|
|
9
|
-
GetPartnersQuery,
|
|
10
|
-
GetUrlQuery,
|
|
11
|
-
HotsiteQuery,
|
|
12
|
-
ProductFragment,
|
|
13
|
-
ProductSortKeys,
|
|
14
|
-
SearchQuery,
|
|
15
|
-
SortDirection,
|
|
16
|
-
} from "../utils/graphql/storefront.graphql.gen";
|
|
17
|
-
import { getPartnerCookie } from "../utils/partner";
|
|
18
|
-
import { currentRequest, currentRequestHeaders, forwardedHeaders } from "../utils/requestCtx";
|
|
19
|
-
import { FILTER_PARAM, toBreadcrumbList, toFilters, toProduct } from "../utils/transform";
|
|
20
|
-
import type { RequestURLParam } from "../utils/types";
|
|
21
|
-
import type { Filters } from "./productList";
|
|
22
|
-
|
|
23
|
-
export type Sort =
|
|
24
|
-
| "NAME:ASC"
|
|
25
|
-
| "NAME:DESC"
|
|
26
|
-
| "RELEASE_DATE:DESC"
|
|
27
|
-
| "PRICE:ASC"
|
|
28
|
-
| "PRICE:DESC"
|
|
29
|
-
| "DISCOUNT:DESC"
|
|
30
|
-
| "SALES:DESC";
|
|
31
|
-
|
|
32
|
-
export const SORT_OPTIONS: SortOption[] = [
|
|
33
|
-
{ value: "NAME:ASC", label: "Nome A-Z" },
|
|
34
|
-
{ value: "NAME:DESC", label: "Nome Z-A" },
|
|
35
|
-
{ value: "RELEASE_DATE:DESC", label: "Lançamentos" },
|
|
36
|
-
{ value: "PRICE:ASC", label: "Menores Preços" },
|
|
37
|
-
{ value: "PRICE:DESC", label: "Maiores Preços" },
|
|
38
|
-
{ value: "DISCOUNT:DESC", label: "Maiores Descontos" },
|
|
39
|
-
{ value: "SALES:DESC", label: "Mais Vendidos" },
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
type SortValue = `${ProductSortKeys}:${SortDirection}`;
|
|
43
|
-
export interface Props {
|
|
44
|
-
/**
|
|
45
|
-
* @title Count
|
|
46
|
-
* @description Number of products to display
|
|
47
|
-
* @maximum 50
|
|
48
|
-
* @default 12
|
|
49
|
-
*/
|
|
50
|
-
limit?: number;
|
|
51
|
-
|
|
52
|
-
/** @description Types of operations to perform between query terms */
|
|
53
|
-
operation?: "AND" | "OR";
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @ignore
|
|
57
|
-
*/
|
|
58
|
-
page: number;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @title Sorting
|
|
62
|
-
*/
|
|
63
|
-
sort?: Sort;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @description overides the query term
|
|
67
|
-
*/
|
|
68
|
-
query?: string;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @title Only Main Variant
|
|
72
|
-
* @description Toggle the return of only main variants or all variations separeted.
|
|
73
|
-
*/
|
|
74
|
-
onlyMainVariant?: boolean;
|
|
75
|
-
|
|
76
|
-
filters?: Filters;
|
|
77
|
-
|
|
78
|
-
/** @description Retrieve variantions for each product. */
|
|
79
|
-
getVariations?: boolean;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @title Starting page query parameter offset.
|
|
83
|
-
* @description Set the starting page offset. Default to 1.
|
|
84
|
-
*/
|
|
85
|
-
pageOffset?: 0 | 1;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @hide true
|
|
89
|
-
* @description The URL of the page, used to override URL from request
|
|
90
|
-
*/
|
|
91
|
-
pageHref?: string;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* @title Partner Param
|
|
95
|
-
* @description page param to partners page
|
|
96
|
-
* @deprecated
|
|
97
|
-
*/
|
|
98
|
-
slug?: RequestURLParam;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* @title Partner Param
|
|
102
|
-
* @description page param to partners page
|
|
103
|
-
*/
|
|
104
|
-
partnerAlias?: RequestURLParam;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const OUTSIDE_ATTRIBUTES_FILTERS = ["precoPor"];
|
|
108
|
-
|
|
109
|
-
const filtersFromParams = (searchParams: URLSearchParams) => {
|
|
110
|
-
const filters: Array<{ field: string; values: string[] }> = [];
|
|
111
|
-
|
|
112
|
-
searchParams.getAll(FILTER_PARAM).forEach((value) => {
|
|
113
|
-
const test = /.*:.*/;
|
|
114
|
-
const [field, val] = test.test(value) ? value.split(":") : value.split("__");
|
|
115
|
-
|
|
116
|
-
if (!OUTSIDE_ATTRIBUTES_FILTERS.includes(field)) {
|
|
117
|
-
filters.push({ field, values: [val] });
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
return filters;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* @title Wake Integration
|
|
126
|
-
* @description Product Listing Page loader
|
|
127
|
-
*/
|
|
128
|
-
const searchLoader = async (props: Props): Promise<ProductListingPage | null> => {
|
|
129
|
-
const req = currentRequest();
|
|
130
|
-
const reqUrl = req?.url ?? "https://localhost";
|
|
131
|
-
// get url from params
|
|
132
|
-
const url =
|
|
133
|
-
new URL(reqUrl).pathname === "/live/invoke"
|
|
134
|
-
? new URL(props.pageHref || req?.headers.get("referer") || reqUrl)
|
|
135
|
-
: new URL(props.pageHref || reqUrl);
|
|
136
|
-
|
|
137
|
-
const storefront = getWakeClient();
|
|
138
|
-
|
|
139
|
-
const partnerAlias = props.partnerAlias ?? props.slug;
|
|
140
|
-
|
|
141
|
-
const partnerAccessTokenCookie = getPartnerCookie(currentRequestHeaders());
|
|
142
|
-
|
|
143
|
-
const headers = forwardedHeaders();
|
|
144
|
-
|
|
145
|
-
const limit = Number(url.searchParams.get("tamanho") ?? props.limit ?? 12);
|
|
146
|
-
|
|
147
|
-
const filters = filtersFromParams(url.searchParams) ?? props.filters;
|
|
148
|
-
const sort =
|
|
149
|
-
(url.searchParams.get("sort") as SortValue | null) ??
|
|
150
|
-
(url.searchParams.get("ordenacao") as SortValue | null) ??
|
|
151
|
-
props.sort ??
|
|
152
|
-
"SALES:DESC";
|
|
153
|
-
const page =
|
|
154
|
-
props.page ??
|
|
155
|
-
Number(url.searchParams.get("page")) ??
|
|
156
|
-
Number(url.searchParams.get("pagina")) ??
|
|
157
|
-
0;
|
|
158
|
-
const query = props.query ?? url.searchParams.get("busca");
|
|
159
|
-
const operation = props.operation ?? "AND";
|
|
160
|
-
|
|
161
|
-
const [sortKey, sortDirection] = sort.split(":") as [ProductSortKeys, SortDirection];
|
|
162
|
-
|
|
163
|
-
const onlyMainVariant = props.onlyMainVariant ?? true;
|
|
164
|
-
const [minimumPrice, maximumPrice] =
|
|
165
|
-
url.searchParams
|
|
166
|
-
.getAll("filtro")
|
|
167
|
-
?.find((i) => i.startsWith("precoPor"))
|
|
168
|
-
?.split(":")[1]
|
|
169
|
-
?.split(";")
|
|
170
|
-
.map(Number) ??
|
|
171
|
-
url.searchParams.get("precoPor")?.split(";").map(Number) ??
|
|
172
|
-
[];
|
|
173
|
-
|
|
174
|
-
const offset = page <= 1 ? 0 : (page - 1) * limit;
|
|
175
|
-
|
|
176
|
-
const partnerData = partnerAlias
|
|
177
|
-
? await storefront.query<GetPartnersQuery>(
|
|
178
|
-
GetPartners,
|
|
179
|
-
{ first: 1, alias: [partnerAlias] },
|
|
180
|
-
headers,
|
|
181
|
-
)
|
|
182
|
-
: null;
|
|
183
|
-
|
|
184
|
-
const partnerAccessToken =
|
|
185
|
-
partnerData?.partners?.edges?.[0]?.node?.partnerAccessToken ?? partnerAccessTokenCookie;
|
|
186
|
-
|
|
187
|
-
if (partnerAccessToken) {
|
|
188
|
-
try {
|
|
189
|
-
await partnerAssociate({ partnerAccessToken });
|
|
190
|
-
} catch (e) {
|
|
191
|
-
console.error(e);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const urlData = await storefront.query<GetUrlQuery>(GetURL, { url: url.pathname }, headers);
|
|
196
|
-
|
|
197
|
-
const isHotsite = urlData.uri?.kind === "HOTSITE";
|
|
198
|
-
|
|
199
|
-
const commonParams = {
|
|
200
|
-
sortDirection,
|
|
201
|
-
sortKey,
|
|
202
|
-
filters,
|
|
203
|
-
limit: Math.min(limit, MAXIMUM_REQUEST_QUANTITY),
|
|
204
|
-
offset,
|
|
205
|
-
onlyMainVariant,
|
|
206
|
-
minimumPrice,
|
|
207
|
-
maximumPrice,
|
|
208
|
-
partnerAccessToken,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
if (!query && !isHotsite && !partnerAccessToken) {
|
|
212
|
-
console.warn(
|
|
213
|
-
"ProductListingPage: No search query, hotsite, or partner token provided - returning null",
|
|
214
|
-
);
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
let data: SearchQuery | HotsiteQuery | undefined;
|
|
219
|
-
try {
|
|
220
|
-
data = isHotsite
|
|
221
|
-
? await storefront.query<HotsiteQuery>(
|
|
222
|
-
Hotsite,
|
|
223
|
-
{ ...commonParams, url: url.pathname },
|
|
224
|
-
headers,
|
|
225
|
-
)
|
|
226
|
-
: await storefront.query<SearchQuery>(Search, { ...commonParams, query, operation }, headers);
|
|
227
|
-
} catch (error: unknown) {
|
|
228
|
-
handleAuthError(error, "load products");
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const products = data?.result?.productsByOffset?.items ?? [];
|
|
232
|
-
|
|
233
|
-
const nextPage = new URLSearchParams(url.searchParams);
|
|
234
|
-
const previousPage = new URLSearchParams(url.searchParams);
|
|
235
|
-
|
|
236
|
-
const hasNextPage = Boolean(
|
|
237
|
-
(data?.result?.productsByOffset?.totalCount ?? 0) / limit >
|
|
238
|
-
(data?.result?.productsByOffset?.page ?? 0),
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const hasPreviousPage = page > 1;
|
|
242
|
-
|
|
243
|
-
const pageOffset = props.pageOffset ?? 0;
|
|
244
|
-
|
|
245
|
-
if (hasNextPage) {
|
|
246
|
-
nextPage.set("page", (page + pageOffset + 1).toString());
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (hasPreviousPage) {
|
|
250
|
-
previousPage.set("page", (page + pageOffset - 1).toString());
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const productIDs = products.map((i) => i?.productId).filter((id): id is number => id != null);
|
|
254
|
-
|
|
255
|
-
const variations = props.getVariations ? await getVariations(productIDs, url, headers) : [];
|
|
256
|
-
|
|
257
|
-
const breadcrumb = toBreadcrumbList(data?.result?.breadcrumbs, {
|
|
258
|
-
base: url,
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
const title = isHotsite
|
|
262
|
-
? (data as HotsiteQuery)?.result?.seo?.find((i) => i?.type === "TITLE")?.content
|
|
263
|
-
: capitalize(query || "");
|
|
264
|
-
const description = isHotsite
|
|
265
|
-
? (data as HotsiteQuery)?.result?.seo?.find((i) => i?.name === "description")?.content
|
|
266
|
-
: capitalize(query || "");
|
|
267
|
-
const canonical = new URL(isHotsite ? `/${(data as HotsiteQuery)?.result?.url}` : url, url).href;
|
|
268
|
-
|
|
269
|
-
return {
|
|
270
|
-
"@type": "ProductListingPage",
|
|
271
|
-
filters: toFilters(data?.result?.aggregations, { base: url }),
|
|
272
|
-
pageInfo: {
|
|
273
|
-
nextPage: hasNextPage ? `?${nextPage}` : undefined,
|
|
274
|
-
previousPage: hasPreviousPage ? `?${previousPage}` : undefined,
|
|
275
|
-
currentPage: data?.result?.productsByOffset?.page ?? 1,
|
|
276
|
-
records: data?.result?.productsByOffset?.totalCount,
|
|
277
|
-
recordPerPage: limit,
|
|
278
|
-
},
|
|
279
|
-
sortOptions: SORT_OPTIONS,
|
|
280
|
-
breadcrumb,
|
|
281
|
-
seo: {
|
|
282
|
-
description: description || "",
|
|
283
|
-
title: title || "",
|
|
284
|
-
canonical,
|
|
285
|
-
},
|
|
286
|
-
products: products
|
|
287
|
-
?.filter((p): p is ProductFragment => Boolean(p))
|
|
288
|
-
.map((variant) => {
|
|
289
|
-
const productVariations = variations?.filter(
|
|
290
|
-
(v) => v.inProductGroupWithID === variant.productId,
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
return toProduct(variant, { base: url }, productVariations);
|
|
294
|
-
}),
|
|
295
|
-
};
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
export const cache = "stale-while-revalidate";
|
|
299
|
-
|
|
300
|
-
export const cacheKey = (props: Props): string | null => {
|
|
301
|
-
const req = currentRequest();
|
|
302
|
-
const url = new URL(props.pageHref || req?.url || "https://localhost");
|
|
303
|
-
|
|
304
|
-
// Don't cache search queries to ensure freshness
|
|
305
|
-
if (url.searchParams.get("q") || url.searchParams.get("query") || url.searchParams.get("busca")) {
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Avoid cross-tenant cache bleed when a partner token is present
|
|
310
|
-
if (getPartnerCookie(currentRequestHeaders())) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const params = new URLSearchParams([
|
|
315
|
-
["limit", (props.limit || 12).toString()],
|
|
316
|
-
["page", (props.page || 0).toString()],
|
|
317
|
-
["sort", props.sort || ""],
|
|
318
|
-
["pageOffset", (props.pageOffset || 0).toString()],
|
|
319
|
-
]);
|
|
320
|
-
|
|
321
|
-
// Add any filter parameters
|
|
322
|
-
url.searchParams.forEach((value, key) => {
|
|
323
|
-
if (key === "filtro" || key === "q" || key === "sort" || key === "page") {
|
|
324
|
-
params.append(key, value);
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
params.sort();
|
|
329
|
-
url.search = params.toString();
|
|
330
|
-
return url.href;
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
export default searchLoader;
|
package/src/loaders/proxy.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { getCheckoutUrl } from "../client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Checkout paths that must be proxied to the Wake checkout host. In the Deno
|
|
5
|
-
* runtime this loader returned deco `Route[]` consumed by the `website` router;
|
|
6
|
-
* that router does not exist in this architecture, so the loader returns a
|
|
7
|
-
* serializable descriptor list that the storefront's worker entry consumes to
|
|
8
|
-
* install its own proxy handler.
|
|
9
|
-
*/
|
|
10
|
-
const PATHS_TO_PROXY: Array<[string, string?]> = [
|
|
11
|
-
["/checkout", "/checkout"],
|
|
12
|
-
["/Fechamento"],
|
|
13
|
-
["/Fechamento/*"],
|
|
14
|
-
["/Login"],
|
|
15
|
-
["/Login/*"],
|
|
16
|
-
["/login/*"],
|
|
17
|
-
["/Login/Authenticate"],
|
|
18
|
-
["/Carrinho/*"],
|
|
19
|
-
["/api/*"],
|
|
20
|
-
["/MinhaConta"],
|
|
21
|
-
["/MinhaConta/*"],
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
export interface Props {
|
|
25
|
-
extraPathsToProxy?: string[];
|
|
26
|
-
/**
|
|
27
|
-
* @title Other site maps to include
|
|
28
|
-
*/
|
|
29
|
-
includeSiteMap?: string[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type WakeProxyRoute =
|
|
33
|
-
| {
|
|
34
|
-
type: "proxy";
|
|
35
|
-
pathTemplate: string;
|
|
36
|
-
basePath?: string;
|
|
37
|
-
/** Target host to proxy to (the Wake checkout URL). */
|
|
38
|
-
url: string;
|
|
39
|
-
/** Host header to forward. */
|
|
40
|
-
host: string;
|
|
41
|
-
}
|
|
42
|
-
| {
|
|
43
|
-
type: "sitemap";
|
|
44
|
-
pathTemplate: string;
|
|
45
|
-
include?: string[];
|
|
46
|
-
host: string;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @title Wake Proxy Routes
|
|
51
|
-
*/
|
|
52
|
-
function loader(props: Props): WakeProxyRoute[] {
|
|
53
|
-
const { includeSiteMap, extraPathsToProxy = [] } = props;
|
|
54
|
-
|
|
55
|
-
const checkoutUrl = getCheckoutUrl();
|
|
56
|
-
|
|
57
|
-
const extraPathsAsArray: Array<[string, string?]> = extraPathsToProxy.map((path) => [path]);
|
|
58
|
-
|
|
59
|
-
const checkout: WakeProxyRoute[] = [...PATHS_TO_PROXY, ...extraPathsAsArray].map(
|
|
60
|
-
([pathTemplate, basePath]) => ({
|
|
61
|
-
type: "proxy",
|
|
62
|
-
pathTemplate,
|
|
63
|
-
basePath,
|
|
64
|
-
url: checkoutUrl,
|
|
65
|
-
host: checkoutUrl,
|
|
66
|
-
}),
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const sitemap: WakeProxyRoute = {
|
|
70
|
-
type: "sitemap",
|
|
71
|
-
pathTemplate: "/Sitemap.xml",
|
|
72
|
-
include: includeSiteMap,
|
|
73
|
-
host: checkoutUrl,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
return [...checkout, sitemap];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export default loader;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { Product } from "@decocms/apps-commerce/types";
|
|
2
|
-
import { getWakeClient } from "../client";
|
|
3
|
-
import { handleAuthError } from "../utils/authError";
|
|
4
|
-
import { getVariations } from "../utils/getVariations";
|
|
5
|
-
import { ProductRecommendations } from "../utils/graphql/queries";
|
|
6
|
-
import type {
|
|
7
|
-
ProductFragment,
|
|
8
|
-
ProductRecommendationsQuery,
|
|
9
|
-
} from "../utils/graphql/storefront.graphql.gen";
|
|
10
|
-
import { getPartnerCookie } from "../utils/partner";
|
|
11
|
-
import { currentRequestHeaders, currentUrl, forwardedHeaders } from "../utils/requestCtx";
|
|
12
|
-
import { parseSlug, toProduct } from "../utils/transform";
|
|
13
|
-
import type { RequestURLParam } from "../utils/types";
|
|
14
|
-
|
|
15
|
-
export interface Props {
|
|
16
|
-
/**
|
|
17
|
-
* @default DEFAULT
|
|
18
|
-
* @description Algorithm type
|
|
19
|
-
*/
|
|
20
|
-
algorithm: "DEFAULT";
|
|
21
|
-
quantity: number;
|
|
22
|
-
slug: RequestURLParam;
|
|
23
|
-
/** @description Retrieve variantions for each product. */
|
|
24
|
-
getVariations?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @title Wake Integration - Product Recommendations
|
|
29
|
-
* @description Product Recommendations loader
|
|
30
|
-
*/
|
|
31
|
-
const productRecommendationsLoader = async (props: Props): Promise<Product[] | null> => {
|
|
32
|
-
const url = currentUrl();
|
|
33
|
-
const storefront = getWakeClient();
|
|
34
|
-
const { slug, quantity, algorithm = "DEFAULT" } = props;
|
|
35
|
-
const partnerAccessToken = getPartnerCookie(currentRequestHeaders());
|
|
36
|
-
|
|
37
|
-
const headers = forwardedHeaders();
|
|
38
|
-
|
|
39
|
-
const { id: productId } = parseSlug(slug);
|
|
40
|
-
|
|
41
|
-
let data: ProductRecommendationsQuery | undefined;
|
|
42
|
-
try {
|
|
43
|
-
data = await storefront.query<ProductRecommendationsQuery>(
|
|
44
|
-
ProductRecommendations,
|
|
45
|
-
{ quantity, productId, algorithm, partnerAccessToken },
|
|
46
|
-
headers,
|
|
47
|
-
);
|
|
48
|
-
} catch (error: unknown) {
|
|
49
|
-
handleAuthError(error, "load product recommendations");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const products = data?.productRecommendations;
|
|
53
|
-
|
|
54
|
-
if (!Array.isArray(products)) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const productIDs = products.map((i) => i?.productId).filter((id): id is number => id != null);
|
|
59
|
-
|
|
60
|
-
const variations = props.getVariations ? await getVariations(productIDs, url, headers) : [];
|
|
61
|
-
|
|
62
|
-
return products
|
|
63
|
-
?.filter((p): p is ProductFragment => Boolean(p))
|
|
64
|
-
.map((variant) => {
|
|
65
|
-
const productVariations = variations?.filter(
|
|
66
|
-
(v) => v.inProductGroupWithID === variant.productId,
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
return toProduct(variant, { base: url }, productVariations);
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const cache = "stale-while-revalidate";
|
|
74
|
-
|
|
75
|
-
export const cacheKey = (props: Props): string | null => {
|
|
76
|
-
// Avoid cross-tenant cache bleed when a partner token is present
|
|
77
|
-
if (getPartnerCookie(currentRequestHeaders())) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return `wake:recommendations:${props.slug}:${props.quantity}:${props.algorithm}:${props.getVariations ?? false}`;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export default productRecommendationsLoader;
|
package/src/loaders/shop.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { getWakeClient } from "../client";
|
|
2
|
-
import { handleAuthError } from "../utils/authError";
|
|
3
|
-
import { Shop } from "../utils/graphql/queries";
|
|
4
|
-
import type { ShopQuery } from "../utils/graphql/storefront.graphql.gen";
|
|
5
|
-
import { getPartnerCookie } from "../utils/partner";
|
|
6
|
-
import { currentRequestHeaders, forwardedHeaders } from "../utils/requestCtx";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @title Wake Integration - Shop Infos
|
|
10
|
-
* @description Shop Infos loader
|
|
11
|
-
*/
|
|
12
|
-
const shopInfos = async (): Promise<ShopQuery["shop"] | undefined> => {
|
|
13
|
-
const storefront = getWakeClient();
|
|
14
|
-
const headers = forwardedHeaders();
|
|
15
|
-
|
|
16
|
-
let data: ShopQuery;
|
|
17
|
-
try {
|
|
18
|
-
data = await storefront.query<ShopQuery>(Shop, {}, headers);
|
|
19
|
-
} catch (error: unknown) {
|
|
20
|
-
handleAuthError(error, "load shop information");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return data?.shop ?? undefined;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export const cache = "stale-while-revalidate";
|
|
27
|
-
|
|
28
|
-
export const cacheKey = (): string | null => {
|
|
29
|
-
// Avoid cross-tenant cache bleed when a partner token is present
|
|
30
|
-
if (getPartnerCookie(currentRequestHeaders())) {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return "wake:shop";
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export default shopInfos;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { Suggestion } from "@decocms/apps-commerce/types";
|
|
2
|
-
import { getWakeClient } from "../client";
|
|
3
|
-
import { handleAuthError } from "../utils/authError";
|
|
4
|
-
import { Autocomplete } from "../utils/graphql/queries";
|
|
5
|
-
import type { AutocompleteQuery, ProductFragment } from "../utils/graphql/storefront.graphql.gen";
|
|
6
|
-
import { getPartnerCookie } from "../utils/partner";
|
|
7
|
-
import { currentRequestHeaders, currentUrl, forwardedHeaders } from "../utils/requestCtx";
|
|
8
|
-
import { toProduct } from "../utils/transform";
|
|
9
|
-
|
|
10
|
-
export interface Props {
|
|
11
|
-
query: string;
|
|
12
|
-
limit?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @title Wake Integration
|
|
17
|
-
* @description Product Suggestion loader
|
|
18
|
-
*/
|
|
19
|
-
const loader = async (props: Props): Promise<Suggestion | null> => {
|
|
20
|
-
const storefront = getWakeClient();
|
|
21
|
-
const { query, limit = 10 } = props;
|
|
22
|
-
|
|
23
|
-
const partnerAccessToken = getPartnerCookie(currentRequestHeaders());
|
|
24
|
-
const headers = forwardedHeaders();
|
|
25
|
-
const url = currentUrl();
|
|
26
|
-
|
|
27
|
-
if (!query) return null;
|
|
28
|
-
|
|
29
|
-
let data: AutocompleteQuery | undefined;
|
|
30
|
-
try {
|
|
31
|
-
data = await storefront.query<AutocompleteQuery>(
|
|
32
|
-
Autocomplete,
|
|
33
|
-
{ query, limit, partnerAccessToken },
|
|
34
|
-
headers,
|
|
35
|
-
);
|
|
36
|
-
} catch (error: unknown) {
|
|
37
|
-
handleAuthError(error, "load product suggestions");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const { products: wakeProducts, suggestions = [] } = data?.autocomplete ?? {};
|
|
41
|
-
|
|
42
|
-
if (!wakeProducts?.length && !suggestions?.length) return null;
|
|
43
|
-
|
|
44
|
-
const products = wakeProducts
|
|
45
|
-
?.filter((node): node is ProductFragment => Boolean(node))
|
|
46
|
-
.map((node) => toProduct(node, { base: url }));
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
products: products,
|
|
50
|
-
searches: suggestions?.filter(Boolean)?.map((suggestion) => ({
|
|
51
|
-
term: suggestion!,
|
|
52
|
-
})),
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const cache = "no-cache";
|
|
57
|
-
|
|
58
|
-
export const cacheKey = (props: Props): string | null => {
|
|
59
|
-
// Avoid cross-tenant cache bleed when a partner token is present
|
|
60
|
-
if (getPartnerCookie(currentRequestHeaders())) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return `wake:suggestion:${props.query}:${props.limit ?? 10}`;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export default loader;
|
package/src/loaders/user.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { Person } from "@decocms/apps-commerce/types";
|
|
2
|
-
import { getWakeClient } from "../client";
|
|
3
|
-
import { handleAuthError } from "../utils/authError";
|
|
4
|
-
import authenticate from "../utils/authenticate";
|
|
5
|
-
import { GetUser } from "../utils/graphql/queries";
|
|
6
|
-
import type { GetUserQuery } from "../utils/graphql/storefront.graphql.gen";
|
|
7
|
-
import { forwardedHeaders } from "../utils/requestCtx";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @title Wake Integration
|
|
11
|
-
* @description User loader
|
|
12
|
-
*/
|
|
13
|
-
const userLoader = async (): Promise<Person | null> => {
|
|
14
|
-
const storefront = getWakeClient();
|
|
15
|
-
const headers = forwardedHeaders();
|
|
16
|
-
|
|
17
|
-
const customerAccessToken = await authenticate();
|
|
18
|
-
|
|
19
|
-
if (!customerAccessToken) return null;
|
|
20
|
-
|
|
21
|
-
let data: GetUserQuery | undefined;
|
|
22
|
-
try {
|
|
23
|
-
data = await storefront.query<GetUserQuery>(GetUser, { customerAccessToken }, headers);
|
|
24
|
-
} catch (error: unknown) {
|
|
25
|
-
handleAuthError(error, "load user data");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const customer = data?.customer;
|
|
29
|
-
|
|
30
|
-
if (!customer) return null;
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
"@id": String(customer.id ?? customer.customerId),
|
|
34
|
-
email: customer.email ?? undefined,
|
|
35
|
-
givenName: customer.customerName ?? undefined,
|
|
36
|
-
gender:
|
|
37
|
-
customer.gender === "Masculino"
|
|
38
|
-
? "https://schema.org/Male"
|
|
39
|
-
: customer.gender === "Feminino"
|
|
40
|
-
? "https://schema.org/Female"
|
|
41
|
-
: undefined,
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export default userLoader;
|
|
46
|
-
|
|
47
|
-
// User-specific response; must not be cached/shared.
|
|
48
|
-
export const cache = "no-store";
|
package/src/loaders/wishlist.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { getWakeClient } from "../client";
|
|
2
|
-
import { handleAuthError } from "../utils/authError";
|
|
3
|
-
import authenticate from "../utils/authenticate";
|
|
4
|
-
import { GetWishlist } from "../utils/graphql/queries";
|
|
5
|
-
import type {
|
|
6
|
-
GetWishlistQuery,
|
|
7
|
-
WishlistReducedProductFragment,
|
|
8
|
-
} from "../utils/graphql/storefront.graphql.gen";
|
|
9
|
-
import { forwardedHeaders } from "../utils/requestCtx";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @title Wake Integration
|
|
13
|
-
* @description Product Wishlist loader
|
|
14
|
-
*/
|
|
15
|
-
const loader = async (): Promise<WishlistReducedProductFragment[]> => {
|
|
16
|
-
const storefront = getWakeClient();
|
|
17
|
-
const headers = forwardedHeaders();
|
|
18
|
-
|
|
19
|
-
const customerAccessToken = await authenticate();
|
|
20
|
-
|
|
21
|
-
if (!customerAccessToken) return [];
|
|
22
|
-
|
|
23
|
-
let data: GetWishlistQuery | undefined;
|
|
24
|
-
try {
|
|
25
|
-
data = await storefront.query<GetWishlistQuery>(GetWishlist, { customerAccessToken }, headers);
|
|
26
|
-
} catch (error: unknown) {
|
|
27
|
-
handleAuthError(error, "load wishlist");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
data?.customer?.wishlist?.products?.filter((p): p is WishlistReducedProductFragment =>
|
|
32
|
-
Boolean(p),
|
|
33
|
-
) ?? []
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export default loader;
|
|
38
|
-
|
|
39
|
-
// User-specific wishlist data; must not be cached/shared.
|
|
40
|
-
export const cache = "no-store";
|