@decocms/apps-wake 7.66.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.
Files changed (56) hide show
  1. package/package.json +58 -0
  2. package/src/actions/cart/addCoupon.ts +43 -0
  3. package/src/actions/cart/addItem.ts +10 -0
  4. package/src/actions/cart/addItems.ts +50 -0
  5. package/src/actions/cart/addKit.ts +53 -0
  6. package/src/actions/cart/partnerAssociate.ts +48 -0
  7. package/src/actions/cart/partnerDisassociate.ts +43 -0
  8. package/src/actions/cart/removeCoupon.ts +39 -0
  9. package/src/actions/cart/removeKit.ts +56 -0
  10. package/src/actions/cart/updateItemQuantity.ts +69 -0
  11. package/src/actions/newsletter/register.ts +32 -0
  12. package/src/actions/notifyme.ts +28 -0
  13. package/src/actions/review/create.ts +30 -0
  14. package/src/actions/shippingSimulation.ts +60 -0
  15. package/src/actions/submmitForm.ts +25 -0
  16. package/src/actions/wishlist/addProduct.ts +43 -0
  17. package/src/actions/wishlist/removeProduct.ts +45 -0
  18. package/src/client.ts +123 -0
  19. package/src/commerceLoaders.ts +137 -0
  20. package/src/handlers/sitemap.ts +61 -0
  21. package/src/index.ts +58 -0
  22. package/src/loaders/cart.ts +46 -0
  23. package/src/loaders/partners.ts +58 -0
  24. package/src/loaders/productDetailsPage.ts +174 -0
  25. package/src/loaders/productList.ts +262 -0
  26. package/src/loaders/productListingPage.ts +333 -0
  27. package/src/loaders/proxy.ts +79 -0
  28. package/src/loaders/recommendations.ts +84 -0
  29. package/src/loaders/shop.ts +37 -0
  30. package/src/loaders/suggestion.ts +67 -0
  31. package/src/loaders/user.ts +48 -0
  32. package/src/loaders/wishlist.ts +40 -0
  33. package/src/manifest.gen.ts +69 -0
  34. package/src/mod.ts +88 -0
  35. package/src/registry.ts +9 -0
  36. package/src/utils/authError.ts +107 -0
  37. package/src/utils/authenticate.ts +42 -0
  38. package/src/utils/capitalize.ts +9 -0
  39. package/src/utils/cart.ts +33 -0
  40. package/src/utils/client.ts +11 -0
  41. package/src/utils/cookies.ts +82 -0
  42. package/src/utils/getVariations.ts +47 -0
  43. package/src/utils/graphql/queries.ts +1363 -0
  44. package/src/utils/graphql/storefront.graphql.gen.ts +10233 -0
  45. package/src/utils/graphql.ts +88 -0
  46. package/src/utils/graphqlOperationName.ts +67 -0
  47. package/src/utils/httpError.ts +15 -0
  48. package/src/utils/instrumentedFetch.ts +46 -0
  49. package/src/utils/operationRouter.ts +51 -0
  50. package/src/utils/parseHeaders.ts +19 -0
  51. package/src/utils/partner.ts +34 -0
  52. package/src/utils/requestCtx.ts +41 -0
  53. package/src/utils/transform.ts +460 -0
  54. package/src/utils/types.ts +6 -0
  55. package/src/utils/user.ts +9 -0
  56. package/tsconfig.json +7 -0
@@ -0,0 +1,460 @@
1
+ import type {
2
+ BreadcrumbList,
3
+ FilterRange,
4
+ ListItem,
5
+ OfferItemCondition,
6
+ Product,
7
+ ProductListingPage,
8
+ PropertyValue,
9
+ UnitPriceSpecification,
10
+ } from "@decocms/apps-commerce/types";
11
+ import { DEFAULT_IMAGE } from "@decocms/apps-commerce/utils/constants";
12
+ import type {
13
+ BuyList,
14
+ ProductFragment,
15
+ SearchQuery,
16
+ SingleProductFragment,
17
+ } from "./graphql/storefront.graphql.gen";
18
+
19
+ export const FILTER_PARAM = "filtro";
20
+
21
+ export const CONDITIONS: Record<string, OfferItemCondition> = {
22
+ Novo: "https://schema.org/NewCondition",
23
+ Usado: "https://schema.org/UsedCondition",
24
+ Renovado: "https://schema.org/RefurbishedCondition",
25
+ Danificado: "https://schema.org/DamagedCondition",
26
+ };
27
+
28
+ export const camposAdicionais = ["Atacado", "Estoque", "Atributo", "Informacao", "TabelaPreco"];
29
+
30
+ export const parseSlug = (slug: string) => {
31
+ const segments = slug.split("-");
32
+ const id = Number(segments.at(-1));
33
+
34
+ if (!id) {
35
+ throw new Error("Malformed slug. Expecting {slug}-{id} format");
36
+ }
37
+
38
+ return {
39
+ slug: segments.slice(0, -1).join("-"),
40
+ id: Number(segments.at(-1)),
41
+ };
42
+ };
43
+
44
+ export const getProductUrl = (
45
+ { alias }: ProductFragment | SingleProductFragment,
46
+ base: URL | string,
47
+ ) => new URL(`/produto/${alias}`, base);
48
+
49
+ export const getVariantUrl = (
50
+ variant: ProductFragment | SingleProductFragment | BuyList,
51
+ base: URL | string,
52
+ variantId?: string,
53
+ ) => {
54
+ const url = getProductUrl(variant, base);
55
+
56
+ url.searchParams.set("skuId", variantId ?? variant.productVariantId);
57
+
58
+ return url;
59
+ };
60
+
61
+ type WakeFilterItem = NonNullable<
62
+ NonNullable<NonNullable<SearchQuery["result"]>["aggregations"]>["filters"]
63
+ >[0];
64
+
65
+ export const toFilterItem = (filter: WakeFilterItem, base: URL) => ({
66
+ "@type": "FilterToggle" as const,
67
+ key: filter?.origin ?? "",
68
+ label: filter?.field ?? "",
69
+ quantity: filter?.values?.length ?? 0,
70
+ values:
71
+ filter?.values?.map((filterValue) => {
72
+ const url = new URL(base);
73
+ const { name, quantity } = filterValue!;
74
+
75
+ const filterParams = url.searchParams.getAll(FILTER_PARAM);
76
+
77
+ const index = filterParams.findIndex((f) => (f.split("__")[1] || f.split(":")[1]) === name);
78
+
79
+ const selected = index > -1;
80
+
81
+ if (selected) {
82
+ const params = new URLSearchParams();
83
+ url.searchParams.forEach((value, key) => {
84
+ if (key !== FILTER_PARAM || !value.endsWith(name!)) {
85
+ params.append(key, value);
86
+ }
87
+ });
88
+ url.search = `${params}`;
89
+ } else {
90
+ url.searchParams.append(FILTER_PARAM, `${filter.field}__${name}`);
91
+ }
92
+
93
+ return {
94
+ value: name!,
95
+ label: name!,
96
+ quantity: quantity!,
97
+ selected,
98
+ url: url.href,
99
+ };
100
+ }) ?? [],
101
+ });
102
+
103
+ export const toPriceFilter = (
104
+ filter: NonNullable<
105
+ NonNullable<NonNullable<SearchQuery["result"]>["aggregations"]>["priceRanges"]
106
+ >,
107
+ base: URL,
108
+ ) => ({
109
+ "@type": "FilterToggle" as const,
110
+ key: "precoPor",
111
+ label: "Preço",
112
+ quantity: filter.length ?? 0,
113
+ values:
114
+ filter.map((filterValue) => {
115
+ const url = new URL(base);
116
+ const { range, quantity } = filterValue!;
117
+ const [min, max] = range?.split("-") ?? [];
118
+ const name = `${min};${max}`;
119
+
120
+ const isDirectParam = url.searchParams.getAll("precoPor");
121
+
122
+ const filterParams = isDirectParam ? isDirectParam : url.searchParams.getAll(FILTER_PARAM);
123
+
124
+ const index = isDirectParam
125
+ ? filterParams.indexOf(name)
126
+ : filterParams.findIndex((f) => (f.split("__")[1] || f.split(":")[1]) === name);
127
+
128
+ const selected = index > -1;
129
+
130
+ if (selected) {
131
+ const params = new URLSearchParams();
132
+ url.searchParams.forEach((value, key) => {
133
+ if ((key !== FILTER_PARAM && key !== "precoPor") || !value.endsWith(name)) {
134
+ params.append(key, value);
135
+ }
136
+ });
137
+ url.search = `${params}`;
138
+ } else {
139
+ url.searchParams.append("precoPor", `${name}`);
140
+ }
141
+
142
+ return {
143
+ value: range!,
144
+ label: range!,
145
+ quantity: quantity!,
146
+ selected,
147
+ url: url.href,
148
+ };
149
+ }) ?? [],
150
+ });
151
+
152
+ export const toFilters = (
153
+ aggregations: NonNullable<SearchQuery["result"]>["aggregations"],
154
+ { base }: { base: URL },
155
+ ): ProductListingPage["filters"] => {
156
+ base.searchParams.delete("page");
157
+ const filters: ProductListingPage["filters"] =
158
+ aggregations?.filters?.map((filter) => toFilterItem(filter, base)) ?? [];
159
+
160
+ if (aggregations?.priceRanges) {
161
+ const pricefilter = toPriceFilter(aggregations?.priceRanges, base);
162
+
163
+ filters.push(pricefilter);
164
+ }
165
+
166
+ if (aggregations?.maximumPrice && aggregations?.minimumPrice) {
167
+ const priceRange: FilterRange = {
168
+ "@type": "FilterRange" as const,
169
+ key: "precoPor",
170
+ label: "Preço",
171
+ values: {
172
+ max: aggregations.maximumPrice,
173
+ min: aggregations.minimumPrice,
174
+ },
175
+ };
176
+
177
+ filters.push(priceRange);
178
+ }
179
+
180
+ return filters;
181
+ };
182
+
183
+ export const toBreadcrumbList = (
184
+ breadcrumbs: SingleProductFragment["breadcrumbs"] = [],
185
+ { base }: { base: URL },
186
+ product?: Product,
187
+ ): BreadcrumbList => {
188
+ const itemListElement = [
189
+ ...(breadcrumbs ?? []).map(
190
+ (item, i): ListItem<string> => ({
191
+ "@type": "ListItem",
192
+ name: item!.text!,
193
+ position: i + 1,
194
+ item: new URL(item!.link!, base).href,
195
+ }),
196
+ ),
197
+ ];
198
+
199
+ if (product) {
200
+ itemListElement.push({
201
+ "@type": "ListItem",
202
+ name: product.isVariantOf?.name,
203
+ item: product.isVariantOf?.url ?? "",
204
+ position: (breadcrumbs ?? []).length + 1,
205
+ });
206
+ }
207
+
208
+ return {
209
+ "@type": "BreadcrumbList",
210
+ numberOfItems: itemListElement.length,
211
+ itemListElement,
212
+ };
213
+ };
214
+
215
+ export const toProduct = (
216
+ variant: ProductFragment | SingleProductFragment,
217
+ { base }: { base: URL | string },
218
+ variants: Product[] = [],
219
+ variantId?: number | null,
220
+ ): Product => {
221
+ const images = variant.images?.map((image) => ({
222
+ "@type": "ImageObject" as const,
223
+ encodingFormat: "image",
224
+ url: image?.url ?? "",
225
+ alternateName: image?.fileName ?? "",
226
+ }));
227
+ const additionalProperty: PropertyValue[] = [];
228
+
229
+ if ((variant as SingleProductFragment)?.attributeSelections) {
230
+ (variant as SingleProductFragment)?.attributeSelections?.selections?.forEach((selection) => {
231
+ if (selection?.name === "Outras Opções") {
232
+ selection.values?.forEach((value) => {
233
+ additionalProperty.push({
234
+ "@type": "PropertyValue",
235
+ url: value?.alias ?? undefined,
236
+ value: value?.selected ? "true" : "false",
237
+ name: value?.value ?? undefined,
238
+ valueReference: "SELECTIONS",
239
+ });
240
+ });
241
+ }
242
+ });
243
+ }
244
+
245
+ variant.productCategories?.forEach((category) => {
246
+ additionalProperty.push({
247
+ "@type": "PropertyValue" as const,
248
+ name: "category",
249
+ propertyID: String(category?.id) ?? "",
250
+ value: category?.name ?? "",
251
+ });
252
+ });
253
+
254
+ variant.informations?.forEach((info) => {
255
+ additionalProperty.push({
256
+ "@type": "PropertyValue",
257
+ name: info?.type ?? undefined,
258
+ alternateName: info?.title ?? undefined,
259
+ value: info?.value ?? undefined,
260
+ valueReference: "INFORMATION",
261
+ });
262
+ });
263
+
264
+ variant.attributes?.forEach((attr) => {
265
+ additionalProperty.push({
266
+ "@type": "PropertyValue",
267
+ additionalType: attr?.type ?? undefined,
268
+ name: attr?.name ?? undefined,
269
+ value: attr?.value ?? undefined,
270
+ propertyID: String(attr?.id) ?? undefined,
271
+ valueReference: "SPECIFICATION",
272
+ });
273
+ });
274
+
275
+ if (variant.urlVideo) {
276
+ additionalProperty.push({
277
+ "@type": "PropertyValue",
278
+ name: "urlVideo",
279
+ value: variant.urlVideo,
280
+ valueReference: "PROPERTY",
281
+ });
282
+ }
283
+
284
+ if (variant.promotions) {
285
+ variant.promotions.forEach((promotion) => {
286
+ additionalProperty.push({
287
+ "@type": "PropertyValue",
288
+ name: promotion!.title ?? undefined,
289
+ value: promotion!.content ?? undefined,
290
+ identifier: promotion!.id,
291
+ image: promotion!.fullStampUrl
292
+ ? [
293
+ {
294
+ "@type": "ImageObject",
295
+ encodingFormat: "image",
296
+ url: promotion!.fullStampUrl,
297
+ },
298
+ ]
299
+ : undefined,
300
+ valueReference: "PROMOTION",
301
+ });
302
+ });
303
+ }
304
+
305
+ if ((variant as BuyList).kit) {
306
+ additionalProperty.push({
307
+ "@type": "PropertyValue",
308
+ name: "kit",
309
+ value: String((variant as BuyList).kit),
310
+ valueReference: "PROPERTY",
311
+ });
312
+ }
313
+
314
+ if ((variant as BuyList).buyListId) {
315
+ additionalProperty.push({
316
+ "@type": "PropertyValue",
317
+ name: "buyListId",
318
+ value: String((variant as BuyList).buyListId),
319
+ valueReference: "PROPERTY",
320
+ });
321
+ }
322
+
323
+ if (variant.collection) {
324
+ additionalProperty.push({
325
+ "@type": "PropertyValue",
326
+ name: "collection",
327
+ value: variant.collection ?? undefined,
328
+ valueReference: "COLLECTION",
329
+ });
330
+ }
331
+
332
+ const priceSpecification: UnitPriceSpecification[] = [];
333
+
334
+ if (variant.prices?.listPrice) {
335
+ priceSpecification.push({
336
+ "@type": "UnitPriceSpecification",
337
+ priceType: "https://schema.org/ListPrice",
338
+ price: variant.prices.listPrice,
339
+ });
340
+ }
341
+ if (variant.prices?.price) {
342
+ priceSpecification.push({
343
+ "@type": "UnitPriceSpecification",
344
+ priceType: "https://schema.org/SalePrice",
345
+ price: variant.prices.price,
346
+ });
347
+ }
348
+
349
+ if (variant.prices?.installmentPlans) {
350
+ variant.prices.installmentPlans.forEach((installmentPlan) => {
351
+ if (installmentPlan) {
352
+ installmentPlan.installments?.forEach((installment) => {
353
+ priceSpecification.push({
354
+ "@type": "UnitPriceSpecification",
355
+ priceType: "https://schema.org/SalePrice",
356
+ priceComponentType: "https://schema.org/Installment",
357
+ name: installmentPlan.displayName ?? undefined,
358
+ description: installmentPlan.name ?? undefined,
359
+ billingDuration: installment?.number,
360
+ billingIncrement: installment?.value,
361
+ price: installment?.value,
362
+ });
363
+ });
364
+ }
365
+ });
366
+ }
367
+ const review =
368
+ (variant as SingleProductFragment).reviews?.map((review) => ({
369
+ "@type": "Review" as const,
370
+ author: [
371
+ {
372
+ "@type": "Author" as const,
373
+ name: review?.customer ?? undefined,
374
+ identifier: review?.email ?? undefined,
375
+ },
376
+ ],
377
+ datePublished: review?.reviewDate ?? undefined,
378
+ reviewBody: review?.review ?? undefined,
379
+ reviewRating: {
380
+ "@type": "AggregateRating" as const,
381
+ bestRating: 5,
382
+ worstRating: 1,
383
+ ratingValue: review?.rating ?? undefined,
384
+ ratingCount: 1,
385
+ },
386
+ })) ?? [];
387
+
388
+ const isSimilarTo = variant.similarProducts?.map((p) => toProduct(p!, { base }));
389
+
390
+ const variantSelected =
391
+ variants.find((v) => {
392
+ return Number(v.productID) === Number(variantId);
393
+ }) ?? {};
394
+
395
+ const aggregateRating =
396
+ variant.numberOfVotes || (variant as SingleProductFragment).reviews?.length
397
+ ? {
398
+ "@type": "AggregateRating" as const,
399
+ bestRating: 5,
400
+ ratingCount: variant.numberOfVotes || undefined,
401
+ ratingValue: variant.averageRating ?? undefined,
402
+ reviewCount: (variant as SingleProductFragment).reviews?.length,
403
+ worstRating: 1,
404
+ }
405
+ : undefined;
406
+
407
+ return {
408
+ "@type": "Product",
409
+ url: getVariantUrl(variant, base).href,
410
+ gtin: variant.ean ?? undefined,
411
+ sku: variant.sku!,
412
+ description:
413
+ variant.informations?.find((info) => info?.type === "Descrição")?.value ?? undefined,
414
+ productID: variant.productVariantId,
415
+ name: variant.variantName ?? undefined,
416
+ inProductGroupWithID: variant.productId,
417
+ image: !images?.length ? [DEFAULT_IMAGE] : images,
418
+ brand: {
419
+ "@type": "Brand",
420
+ name: variant.productBrand?.name ?? undefined,
421
+ url: variant.productBrand?.alias
422
+ ? new URL(`/${variant.productBrand.alias}`, base).href
423
+ : undefined,
424
+ logo: variant.productBrand?.fullUrlLogo ?? undefined,
425
+ },
426
+ offers: {
427
+ "@type": "AggregateOffer",
428
+ highPrice: variant.prices?.price,
429
+ lowPrice: variant.prices?.price,
430
+ priceCurrency: "BRL",
431
+ offerCount: 1,
432
+ offers: [
433
+ {
434
+ "@type": "Offer",
435
+ seller: variant.seller?.name ?? undefined,
436
+ price: variant.prices?.price,
437
+ priceSpecification,
438
+ itemCondition: CONDITIONS[variant.condition!],
439
+ availability: variant.available
440
+ ? "https://schema.org/InStock"
441
+ : "https://schema.org/OutOfStock",
442
+ inventoryLevel: { value: variant.stock },
443
+ },
444
+ ],
445
+ },
446
+ additionalProperty,
447
+ ...variantSelected,
448
+ isSimilarTo,
449
+ review,
450
+ aggregateRating,
451
+ isVariantOf: {
452
+ "@type": "ProductGroup",
453
+ url: getProductUrl(variant, base).href,
454
+ name: variant.productName ?? undefined,
455
+ productGroupID: variant.productId,
456
+ hasVariant: variants,
457
+ additionalProperty: [],
458
+ },
459
+ };
460
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A prop resolved from the request URL by the CMS/router. In the Deco/Deno
3
+ * runtime this came from `website/functions/requestToParam.ts`; here it is a
4
+ * plain string (e.g. a route slug) supplied by the loader caller.
5
+ */
6
+ export type RequestURLParam = string;
@@ -0,0 +1,9 @@
1
+ import { getCookies } from "./cookies";
2
+
3
+ const LOGIN_COOKIE = "fbits-login";
4
+
5
+ export const getUserCookie = (headers: Headers): string | undefined => {
6
+ const cookies = getCookies(headers);
7
+
8
+ return cookies[LOGIN_COOKIE];
9
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist"
5
+ },
6
+ "include": ["src/**/*"]
7
+ }