@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,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InputMaybe,
|
|
3
|
+
ProductCollectionSortKeys,
|
|
4
|
+
ProductFilter,
|
|
5
|
+
SearchSortKeys as SearchSortKeysShopify,
|
|
6
|
+
} from "../utils/storefront/storefront.graphql.gen";
|
|
7
|
+
|
|
8
|
+
export const sortOptions = [
|
|
9
|
+
{ value: "", label: "relevance:desc" },
|
|
10
|
+
{ value: "price-ascending", label: "price:asc" },
|
|
11
|
+
{ value: "price-descending", label: "price:desc" },
|
|
12
|
+
{ value: "best-selling", label: "orders:desc" },
|
|
13
|
+
{ value: "title-ascending", label: "name:asc" },
|
|
14
|
+
{ value: "title-descending", label: "name:desc" },
|
|
15
|
+
{ value: "created-descending", label: "release:desc" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// only these sorts work for search at shopify
|
|
19
|
+
export const searchSortOptions = [
|
|
20
|
+
{ value: "", label: "relevance:desc" },
|
|
21
|
+
{ value: "price-ascending", label: "price:asc" },
|
|
22
|
+
{ value: "price-descending", label: "price:desc" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export type CollectionSortKeys =
|
|
26
|
+
| ""
|
|
27
|
+
| "price-descending"
|
|
28
|
+
| "price-ascending"
|
|
29
|
+
| "best-selling"
|
|
30
|
+
| "title-descending"
|
|
31
|
+
| "title-ascending"
|
|
32
|
+
| "created-descending";
|
|
33
|
+
|
|
34
|
+
export type SearchSortKeys =
|
|
35
|
+
| ""
|
|
36
|
+
| "price-descending"
|
|
37
|
+
| "price-ascending";
|
|
38
|
+
|
|
39
|
+
export const searchSortShopify: Record<
|
|
40
|
+
string,
|
|
41
|
+
{ sortKey: SearchSortKeysShopify; reverse: boolean }
|
|
42
|
+
> = {
|
|
43
|
+
"": {
|
|
44
|
+
sortKey: "RELEVANCE",
|
|
45
|
+
reverse: false,
|
|
46
|
+
},
|
|
47
|
+
"price-descending": {
|
|
48
|
+
sortKey: "PRICE",
|
|
49
|
+
reverse: true,
|
|
50
|
+
},
|
|
51
|
+
"price-ascending": {
|
|
52
|
+
sortKey: "PRICE",
|
|
53
|
+
reverse: false,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const sortShopify: Record<
|
|
58
|
+
string,
|
|
59
|
+
{
|
|
60
|
+
sortKey: ProductCollectionSortKeys | SearchSortKeysShopify;
|
|
61
|
+
reverse: boolean;
|
|
62
|
+
}
|
|
63
|
+
> = {
|
|
64
|
+
"": {
|
|
65
|
+
sortKey: "RELEVANCE",
|
|
66
|
+
reverse: false,
|
|
67
|
+
},
|
|
68
|
+
"price-descending": {
|
|
69
|
+
sortKey: "PRICE",
|
|
70
|
+
reverse: true,
|
|
71
|
+
},
|
|
72
|
+
"price-ascending": {
|
|
73
|
+
sortKey: "PRICE",
|
|
74
|
+
reverse: false,
|
|
75
|
+
},
|
|
76
|
+
"best-selling": {
|
|
77
|
+
sortKey: "BEST_SELLING",
|
|
78
|
+
reverse: false,
|
|
79
|
+
},
|
|
80
|
+
"title-descending": {
|
|
81
|
+
sortKey: "TITLE",
|
|
82
|
+
reverse: true,
|
|
83
|
+
},
|
|
84
|
+
"title-ascending": {
|
|
85
|
+
sortKey: "TITLE",
|
|
86
|
+
reverse: false,
|
|
87
|
+
},
|
|
88
|
+
"created-descending": {
|
|
89
|
+
sortKey: "CREATED",
|
|
90
|
+
reverse: true,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const filterToObject = (
|
|
95
|
+
type: string,
|
|
96
|
+
filter: InputMaybe<ProductFilter>,
|
|
97
|
+
) => {
|
|
98
|
+
if (type == "tag") {
|
|
99
|
+
return { tag: filter?.tag };
|
|
100
|
+
}
|
|
101
|
+
if (type == "productType") {
|
|
102
|
+
return { productType: filter?.productType };
|
|
103
|
+
}
|
|
104
|
+
if (type == "productVendor") {
|
|
105
|
+
return { productVendor: filter?.productVendor };
|
|
106
|
+
}
|
|
107
|
+
if (type == "priceMin") {
|
|
108
|
+
return { price: { min: filter?.price?.min } };
|
|
109
|
+
}
|
|
110
|
+
if (type == "priceMax") {
|
|
111
|
+
return { price: { max: filter?.price?.max } };
|
|
112
|
+
}
|
|
113
|
+
if (type == "variantOption") {
|
|
114
|
+
return { variantOption: filter?.variantOption };
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const getFiltersByUrl = (url: URL) => {
|
|
119
|
+
const filters: InputMaybe<ProductFilter[]> | undefined = [];
|
|
120
|
+
url.searchParams.forEach((value, key) => {
|
|
121
|
+
if (key.startsWith("filter.v.option")) {
|
|
122
|
+
filters.push({
|
|
123
|
+
variantOption: { name: key.split(".")[3], value: value },
|
|
124
|
+
});
|
|
125
|
+
} else if (key.startsWith("filter.p.tag")) {
|
|
126
|
+
filters.push({ tag: value });
|
|
127
|
+
} else if (key.startsWith("filter.p.type")) {
|
|
128
|
+
filters.push({ productType: value });
|
|
129
|
+
} else if (key.startsWith("filter.p.product_type")) {
|
|
130
|
+
filters.push({ productType: value });
|
|
131
|
+
} else if (key.startsWith("filter.p.vendor")) {
|
|
132
|
+
filters.push({ productVendor: value });
|
|
133
|
+
} else if (key.startsWith("filter.v.availability")) {
|
|
134
|
+
filters.push({ available: value.toLowerCase() === "in stock" });
|
|
135
|
+
} else if (key.startsWith("filter.v.price.gte")) {
|
|
136
|
+
filters.push({ price: { min: Number(value) } });
|
|
137
|
+
} else if (key.startsWith("filter.v.price.lte")) {
|
|
138
|
+
filters.push({ price: { max: Number(value) } });
|
|
139
|
+
} else if (key.startsWith("filter.p.m.custom")) {
|
|
140
|
+
filters.push({
|
|
141
|
+
productMetafield: {
|
|
142
|
+
namespace: "custom",
|
|
143
|
+
key: key.replace("filter.p.m.custom.", ""),
|
|
144
|
+
value: value,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
} else if (key.startsWith("filter.v.t.shopify")) {
|
|
148
|
+
filters.push({
|
|
149
|
+
taxonomyMetafield: {
|
|
150
|
+
namespace: "shopify",
|
|
151
|
+
key: key.replace("filter.v.t.shopify.", ""),
|
|
152
|
+
value: value,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
} else if (key.startsWith("filter.p.t.category")) {
|
|
156
|
+
filters.push({
|
|
157
|
+
category: {
|
|
158
|
+
id: value,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
return filters;
|
|
164
|
+
};
|
package/tsconfig.json
ADDED
package/vtex/README.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
VTEX is a cloud-based e-commerce platform and digital commerce company that provides a comprehensive set of tools and services for businesses looking to establish and manage their online retail operations.
|
|
2
|
+
|
|
3
|
+
This app wrapps VTEX API into a comprehensive set of loaders/actions/workflows
|
|
4
|
+
empowering non technical users to interact and act upon their headless commerce.
|
|
5
|
+
|
|
6
|
+
If you want to use a custom search engine (Algolia, Typesense etc), you will need to fill the App Key & App Token properties. For these, follow this guide
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VTEX Address management actions (store-graphql).
|
|
3
|
+
* Ported from deco-cx/apps:
|
|
4
|
+
* - vtex/actions/address/create.ts
|
|
5
|
+
* - vtex/actions/address/delete.ts
|
|
6
|
+
* - vtex/actions/address/update.ts
|
|
7
|
+
* @see https://developers.vtex.com/docs/guides/profile-system
|
|
8
|
+
*/
|
|
9
|
+
import { vtexFetch, getVtexConfig } from "../client";
|
|
10
|
+
import { buildAuthCookieHeader } from "../utils/vtexId";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export interface AddressInput {
|
|
17
|
+
addressName: string;
|
|
18
|
+
addressType?: string;
|
|
19
|
+
city?: string;
|
|
20
|
+
complement?: string;
|
|
21
|
+
country?: string;
|
|
22
|
+
geoCoordinates?: number[];
|
|
23
|
+
neighborhood?: string;
|
|
24
|
+
number?: string;
|
|
25
|
+
postalCode?: string;
|
|
26
|
+
receiverName?: string;
|
|
27
|
+
reference?: string;
|
|
28
|
+
state?: string;
|
|
29
|
+
street?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SavedAddress {
|
|
33
|
+
id: string;
|
|
34
|
+
cacheId: string;
|
|
35
|
+
addressId: string;
|
|
36
|
+
userId?: string;
|
|
37
|
+
addressName: string;
|
|
38
|
+
addressType: string | null;
|
|
39
|
+
city: string | null;
|
|
40
|
+
complement: string | null;
|
|
41
|
+
country: string | null;
|
|
42
|
+
geoCoordinates: number[] | null;
|
|
43
|
+
neighborhood: string | null;
|
|
44
|
+
number: string | null;
|
|
45
|
+
postalCode: string | null;
|
|
46
|
+
receiverName: string | null;
|
|
47
|
+
reference: string | null;
|
|
48
|
+
state: string | null;
|
|
49
|
+
street: string | null;
|
|
50
|
+
name?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface DeleteAddressResult {
|
|
54
|
+
cacheId: string;
|
|
55
|
+
addresses: SavedAddress[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface UpdateAddressResult {
|
|
59
|
+
cacheId: string;
|
|
60
|
+
addresses: SavedAddress;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// GraphQL helper (myvtex.com private graphql)
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
interface GqlResponse<T> {
|
|
68
|
+
data: T;
|
|
69
|
+
errors?: Array<{ message: string }>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function gql<T>(
|
|
73
|
+
query: string,
|
|
74
|
+
variables: Record<string, unknown>,
|
|
75
|
+
authCookie: string,
|
|
76
|
+
): Promise<T> {
|
|
77
|
+
const { account } = getVtexConfig();
|
|
78
|
+
const result = await vtexFetch<GqlResponse<T>>(
|
|
79
|
+
`https://${account}.myvtex.com/_v/private/graphql/v1`,
|
|
80
|
+
{
|
|
81
|
+
method: "POST",
|
|
82
|
+
body: JSON.stringify({ query, variables }),
|
|
83
|
+
headers: { Cookie: buildAuthCookieHeader(authCookie, account) },
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
if (result.errors?.length) {
|
|
87
|
+
throw new Error(`GraphQL error: ${result.errors[0].message}`);
|
|
88
|
+
}
|
|
89
|
+
return result.data;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Mutations
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
const SAVE_ADDRESS = `mutation SaveAddress($address: AddressInput!) {
|
|
97
|
+
saveAddress(address: $address) @context(provider: "vtex.store-graphql") {
|
|
98
|
+
addressId
|
|
99
|
+
cacheId
|
|
100
|
+
id
|
|
101
|
+
userId
|
|
102
|
+
receiverName
|
|
103
|
+
complement
|
|
104
|
+
neighborhood
|
|
105
|
+
country
|
|
106
|
+
state
|
|
107
|
+
number
|
|
108
|
+
street
|
|
109
|
+
geoCoordinates
|
|
110
|
+
postalCode
|
|
111
|
+
city
|
|
112
|
+
name
|
|
113
|
+
addressName
|
|
114
|
+
addressType
|
|
115
|
+
}
|
|
116
|
+
}`;
|
|
117
|
+
|
|
118
|
+
const DELETE_ADDRESS = `mutation DeleteAddress($addressId: String) {
|
|
119
|
+
deleteAddress(id: $addressId) {
|
|
120
|
+
cacheId
|
|
121
|
+
addresses {
|
|
122
|
+
addressId: id
|
|
123
|
+
addressType
|
|
124
|
+
addressName
|
|
125
|
+
city
|
|
126
|
+
complement
|
|
127
|
+
country
|
|
128
|
+
neighborhood
|
|
129
|
+
number
|
|
130
|
+
postalCode
|
|
131
|
+
geoCoordinates
|
|
132
|
+
receiverName
|
|
133
|
+
reference
|
|
134
|
+
state
|
|
135
|
+
street
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}`;
|
|
139
|
+
|
|
140
|
+
const UPDATE_ADDRESS = `mutation UpdateAddress($addressId: String!, $addressFields: AddressInput) {
|
|
141
|
+
updateAddress(id: $addressId, fields: $addressFields) @context(provider: "vtex.store-graphql") {
|
|
142
|
+
cacheId
|
|
143
|
+
addresses: address {
|
|
144
|
+
addressId: id
|
|
145
|
+
addressType
|
|
146
|
+
addressName
|
|
147
|
+
city
|
|
148
|
+
complement
|
|
149
|
+
country
|
|
150
|
+
neighborhood
|
|
151
|
+
number
|
|
152
|
+
postalCode
|
|
153
|
+
geoCoordinates
|
|
154
|
+
receiverName
|
|
155
|
+
reference
|
|
156
|
+
state
|
|
157
|
+
street
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}`;
|
|
161
|
+
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
// Actions
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
/** Create a new user address. Requires the user's VtexIdclientAutCookie token. */
|
|
167
|
+
export async function createAddress(
|
|
168
|
+
input: AddressInput,
|
|
169
|
+
authCookie: string,
|
|
170
|
+
): Promise<SavedAddress> {
|
|
171
|
+
const { saveAddress } = await gql<{ saveAddress: SavedAddress }>(
|
|
172
|
+
SAVE_ADDRESS,
|
|
173
|
+
{ address: input },
|
|
174
|
+
authCookie,
|
|
175
|
+
);
|
|
176
|
+
return saveAddress;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Delete an address by its ID. Returns remaining addresses. */
|
|
180
|
+
export async function deleteAddress(
|
|
181
|
+
addressId: string,
|
|
182
|
+
authCookie: string,
|
|
183
|
+
): Promise<DeleteAddressResult> {
|
|
184
|
+
const { deleteAddress: result } = await gql<{ deleteAddress: DeleteAddressResult }>(
|
|
185
|
+
DELETE_ADDRESS,
|
|
186
|
+
{ addressId },
|
|
187
|
+
authCookie,
|
|
188
|
+
);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Update an existing address. Returns the updated address. */
|
|
193
|
+
export async function updateAddress(
|
|
194
|
+
addressId: string,
|
|
195
|
+
fields: Partial<AddressInput>,
|
|
196
|
+
authCookie: string,
|
|
197
|
+
): Promise<UpdateAddressResult> {
|
|
198
|
+
const { updateAddress: result } = await gql<{ updateAddress: UpdateAddressResult }>(
|
|
199
|
+
UPDATE_ADDRESS,
|
|
200
|
+
{
|
|
201
|
+
addressId,
|
|
202
|
+
addressFields: {
|
|
203
|
+
...fields,
|
|
204
|
+
receiverName: fields.receiverName ?? null,
|
|
205
|
+
complement: fields.complement ?? null,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
authCookie,
|
|
209
|
+
);
|
|
210
|
+
return result;
|
|
211
|
+
}
|