@bacano/sdk 1.2.3 → 1.2.4
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/CONTENT_TYPE_MANAGEMENT.md +42 -2
- package/client.d.ts +1 -0
- package/content-management-types.d.ts +44 -1
- package/content-management.cjs +26 -0
- package/content-management.js +26 -0
- package/index.cjs +58 -1
- package/index.js +58 -1
- package/modules/content.d.ts +1 -0
- package/package.json +1 -1
- package/react.cjs +58 -1
- package/react.js +58 -1
- package/transport/documents.d.ts +1 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
`@bacano/sdk/content-management` permite que una herramienta administrativa o
|
|
4
4
|
un agente autorizado consulte y mantenga **tipos, componentes, grupos, listas
|
|
5
|
-
de opciones, entradas
|
|
5
|
+
de opciones, entradas, imágenes y bloques adicionales de productos** de Bacano Content, y **formularios web**.
|
|
6
6
|
Cada capacidad requiere
|
|
7
|
-
un permiso explícito. El acceso no autoriza productos, ventas, clientes,
|
|
7
|
+
un permiso explícito. El acceso a bloques no autoriza modificar productos, ventas, clientes,
|
|
8
8
|
configuración general, GraphQL, SQL ni acceso directo a Storage.
|
|
9
9
|
|
|
10
10
|
Este contrato es independiente del cliente público del storefront. Debe usarse
|
|
@@ -38,6 +38,10 @@ Scopes disponibles:
|
|
|
38
38
|
- `forms:publish`: publicar, pausar, reactivar o archivar formularios. Requiere
|
|
39
39
|
`forms:manage`. También se exige para cambiar notificaciones de formularios
|
|
40
40
|
que ya fueron publicados, porque afectan envíos en curso.
|
|
41
|
+
- `product_content:manage`: consultar, crear, actualizar, ordenar y retirar
|
|
42
|
+
bloques adicionales exclusivamente en los productos cuyos IDs se incluyeron
|
|
43
|
+
al generar el acceso. Requiere que el usuario mantenga `products.view` y
|
|
44
|
+
`websites.manage`; no modifica precio, inventario ni variantes.
|
|
41
45
|
|
|
42
46
|
Los permisos de Content no conceden acceso a formularios ni viceversa. Los
|
|
43
47
|
tokens existentes no se amplían; solicita uno nuevo con el alcance necesario.
|
|
@@ -89,6 +93,42 @@ El cliente ofrece:
|
|
|
89
93
|
`applyOptionListLifecycle(idOrKey, operation)`.
|
|
90
94
|
- `listMedia(filters)` y `uploadImage(input)` para reutilizar o cargar imágenes
|
|
91
95
|
cuando el acceso incluya `content_media:upload`.
|
|
96
|
+
- `listProductBlocks(productId, filters)`, `createProductBlock(productId, input)`,
|
|
97
|
+
`updateProductBlock(productId, blockId, input)`,
|
|
98
|
+
`reorderProductBlocks(productId, input)` y
|
|
99
|
+
`retireProductBlock(productId, blockId, expectedUpdatedAt)`.
|
|
100
|
+
|
|
101
|
+
## Bloques adicionales de un producto
|
|
102
|
+
|
|
103
|
+
Al generar un acceso con `product_content:manage`, indica de 1 a 50 IDs de
|
|
104
|
+
productos activos de la empresa. El token no habilita otros productos aunque
|
|
105
|
+
el usuario tenga acceso a ellos en el backoffice. Usa un tipo de contenido
|
|
106
|
+
activo de uso `product_additional`; los campos y las referencias de imágenes
|
|
107
|
+
se validan con las mismas reglas de guardado del administrador.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const [block] = await content.listProductBlocks(productId, {
|
|
111
|
+
contentType: "landing_producto",
|
|
112
|
+
locale: "es-CO",
|
|
113
|
+
});
|
|
114
|
+
await content.updateProductBlock(productId, block.id, {
|
|
115
|
+
content: { ...block.content, titulo: "Nuevo título" },
|
|
116
|
+
sortOrder: block.sortOrder,
|
|
117
|
+
expectedUpdatedAt: block.updatedAt,
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`createProductBlock` requiere `contentType`, `locale`, `content` y `sortOrder`.
|
|
122
|
+
Para ordenar, envía **todos** los bloques del idioma en el orden deseado,
|
|
123
|
+
incluyendo el `id` y `expectedUpdatedAt` de cada uno. El servidor aplica el
|
|
124
|
+
cambio atómicamente; una versión obsoleta devuelve conflicto HTTP 409. Vuelve
|
|
125
|
+
a listar y pide revisar el conflicto antes de repetir. Retirar un bloque quita
|
|
126
|
+
su asociación con el producto, sin borrar el tipo, la entrada independiente ni
|
|
127
|
+
los archivos de imagen. Para builds estáticos, el cliente público ofrece
|
|
128
|
+
`client.content.getForProducts(productIds)` y devuelve un registro indexado por
|
|
129
|
+
ID normalizado en minúsculas, incluyendo listas vacías para productos sin
|
|
130
|
+
bloques visibles. Agrupa hasta 50 productos por solicitud; no revela bloques
|
|
131
|
+
de productos no publicados en el sitio. `getForProduct` sigue disponible.
|
|
92
132
|
|
|
93
133
|
## Idioma, nombres y keys
|
|
94
134
|
|
package/client.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export interface BacanoClient {
|
|
|
35
35
|
getByCategory(categoryKey: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
|
|
36
36
|
getByTag(tag: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
|
|
37
37
|
getForProduct(productId: string, opts?: GetContentOptions): Promise<ProductContentBlock[]>;
|
|
38
|
+
getForProducts(productIds: string[], opts?: GetContentOptions): Promise<Record<string, ProductContentBlock[]>>;
|
|
38
39
|
};
|
|
39
40
|
forms: {
|
|
40
41
|
/** Returns the current published definition or null when it is unavailable. */
|
|
@@ -2,7 +2,7 @@ import type { FormManagementClient } from "./form-management-types";
|
|
|
2
2
|
export type ContentManagementTypeKind = "entry" | "component";
|
|
3
3
|
export type ContentManagementTypeStatus = "draft" | "active" | "archived";
|
|
4
4
|
export type ContentManagementTypeUsage = "website_content" | "product_additional" | "product_specification";
|
|
5
|
-
export type ContentManagementScope = "content_types:manage" | "content_entries:manage" | "content_media:upload" | "content_entries:publish" | "forms:manage" | "forms:publish";
|
|
5
|
+
export type ContentManagementScope = "content_types:manage" | "content_entries:manage" | "content_media:upload" | "content_entries:publish" | "forms:manage" | "forms:publish" | "product_content:manage";
|
|
6
6
|
export type ContentManagementFieldType = "boolean" | "component" | "date" | "link" | "measure" | "media" | "number" | "reference" | "repeater" | "rich_text" | "rich_text_full" | "select" | "slug" | "text" | "textarea";
|
|
7
7
|
export interface ContentManagementChildFieldInput {
|
|
8
8
|
id?: string | null;
|
|
@@ -68,8 +68,46 @@ export interface ContentManagementContext {
|
|
|
68
68
|
/** @deprecated Use scopes to inspect the complete grant. */
|
|
69
69
|
scope: ContentManagementScope;
|
|
70
70
|
scopes: ContentManagementScope[];
|
|
71
|
+
productIds: string[];
|
|
71
72
|
expiresAt: string;
|
|
72
73
|
}
|
|
74
|
+
export interface ContentManagementProductBlock {
|
|
75
|
+
id: string;
|
|
76
|
+
productId: string;
|
|
77
|
+
contentType: {
|
|
78
|
+
id: string;
|
|
79
|
+
key: string;
|
|
80
|
+
name: string;
|
|
81
|
+
};
|
|
82
|
+
locale: string;
|
|
83
|
+
content: Record<string, unknown>;
|
|
84
|
+
sortOrder: number;
|
|
85
|
+
createdAt: string;
|
|
86
|
+
updatedAt: string;
|
|
87
|
+
}
|
|
88
|
+
export interface ContentManagementProductBlockListInput {
|
|
89
|
+
contentType?: string;
|
|
90
|
+
locale?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface ContentManagementProductBlockCreateInput {
|
|
93
|
+
contentType: string;
|
|
94
|
+
locale?: string;
|
|
95
|
+
content: Record<string, unknown>;
|
|
96
|
+
sortOrder: number;
|
|
97
|
+
}
|
|
98
|
+
export interface ContentManagementProductBlockUpdateInput {
|
|
99
|
+
content: Record<string, unknown>;
|
|
100
|
+
sortOrder: number;
|
|
101
|
+
expectedUpdatedAt: string;
|
|
102
|
+
}
|
|
103
|
+
export interface ContentManagementProductBlockReorderInput {
|
|
104
|
+
locale: string;
|
|
105
|
+
/** Complete ordered list for this product and locale. */
|
|
106
|
+
items: Array<{
|
|
107
|
+
id: string;
|
|
108
|
+
expectedUpdatedAt: string;
|
|
109
|
+
}>;
|
|
110
|
+
}
|
|
73
111
|
export type ContentManagementEntryStatus = "draft" | "published" | "archived";
|
|
74
112
|
export interface ContentManagementEntryInput {
|
|
75
113
|
contentType: string;
|
|
@@ -262,6 +300,11 @@ export interface ContentManagementClientConfig {
|
|
|
262
300
|
}
|
|
263
301
|
export interface ContentManagementClient extends FormManagementClient {
|
|
264
302
|
getContext(): Promise<ContentManagementContext>;
|
|
303
|
+
listProductBlocks(productId: string, input?: ContentManagementProductBlockListInput): Promise<ContentManagementProductBlock[]>;
|
|
304
|
+
createProductBlock(productId: string, input: ContentManagementProductBlockCreateInput): Promise<ContentManagementProductBlock>;
|
|
305
|
+
updateProductBlock(productId: string, blockId: string, input: ContentManagementProductBlockUpdateInput): Promise<ContentManagementProductBlock>;
|
|
306
|
+
reorderProductBlocks(productId: string, input: ContentManagementProductBlockReorderInput): Promise<ContentManagementProductBlock[]>;
|
|
307
|
+
retireProductBlock(productId: string, blockId: string, expectedUpdatedAt: string): Promise<ContentManagementProductBlock>;
|
|
265
308
|
getReferences(): Promise<ContentManagementReferences>;
|
|
266
309
|
listTypes(): Promise<ContentManagementType[]>;
|
|
267
310
|
getType(identifier: string): Promise<ContentManagementType>;
|
package/content-management.cjs
CHANGED
|
@@ -112,6 +112,13 @@ function mediaListPath(input = {}) {
|
|
|
112
112
|
const query = search.toString();
|
|
113
113
|
return query ? `/media?${query}` : "/media";
|
|
114
114
|
}
|
|
115
|
+
function productBlocksPath(productId, input = {}) {
|
|
116
|
+
const path = `/products/${encodeIdentifier(productId)}/blocks`;
|
|
117
|
+
const search = new URLSearchParams();
|
|
118
|
+
if (input.contentType) search.set("contentType", input.contentType);
|
|
119
|
+
if (input.locale) search.set("locale", input.locale);
|
|
120
|
+
return search.size ? `${path}?${search}` : path;
|
|
121
|
+
}
|
|
115
122
|
function uploadBody(file) {
|
|
116
123
|
if ("arrayBuffer" in file) return file;
|
|
117
124
|
if (file instanceof ArrayBuffer) return file;
|
|
@@ -183,6 +190,25 @@ function createContentManagementClient(config) {
|
|
|
183
190
|
return {
|
|
184
191
|
...createFormManagementMethods(request),
|
|
185
192
|
getContext: () => request("/context"),
|
|
193
|
+
listProductBlocks: (productId, input) => request(
|
|
194
|
+
productBlocksPath(productId, input)
|
|
195
|
+
),
|
|
196
|
+
createProductBlock: (productId, input) => request(productBlocksPath(productId), {
|
|
197
|
+
method: "POST",
|
|
198
|
+
body: JSON.stringify(input)
|
|
199
|
+
}),
|
|
200
|
+
updateProductBlock: (productId, blockId, input) => request(
|
|
201
|
+
`${productBlocksPath(productId)}/${encodeIdentifier(blockId)}`,
|
|
202
|
+
{ method: "PUT", body: JSON.stringify(input) }
|
|
203
|
+
),
|
|
204
|
+
reorderProductBlocks: (productId, input) => request(
|
|
205
|
+
`${productBlocksPath(productId)}/order`,
|
|
206
|
+
{ method: "PUT", body: JSON.stringify(input) }
|
|
207
|
+
),
|
|
208
|
+
retireProductBlock: (productId, blockId, expectedUpdatedAt) => request(
|
|
209
|
+
`${productBlocksPath(productId)}/${encodeIdentifier(blockId)}`,
|
|
210
|
+
{ method: "DELETE", body: JSON.stringify({ expectedUpdatedAt }) }
|
|
211
|
+
),
|
|
186
212
|
getReferences: () => request("/references"),
|
|
187
213
|
listTypes: () => request("/types"),
|
|
188
214
|
getType: (identifier) => request(`/types/${encodeIdentifier(identifier)}`),
|
package/content-management.js
CHANGED
|
@@ -85,6 +85,13 @@ function mediaListPath(input = {}) {
|
|
|
85
85
|
const query = search.toString();
|
|
86
86
|
return query ? `/media?${query}` : "/media";
|
|
87
87
|
}
|
|
88
|
+
function productBlocksPath(productId, input = {}) {
|
|
89
|
+
const path = `/products/${encodeIdentifier(productId)}/blocks`;
|
|
90
|
+
const search = new URLSearchParams();
|
|
91
|
+
if (input.contentType) search.set("contentType", input.contentType);
|
|
92
|
+
if (input.locale) search.set("locale", input.locale);
|
|
93
|
+
return search.size ? `${path}?${search}` : path;
|
|
94
|
+
}
|
|
88
95
|
function uploadBody(file) {
|
|
89
96
|
if ("arrayBuffer" in file) return file;
|
|
90
97
|
if (file instanceof ArrayBuffer) return file;
|
|
@@ -156,6 +163,25 @@ function createContentManagementClient(config) {
|
|
|
156
163
|
return {
|
|
157
164
|
...createFormManagementMethods(request),
|
|
158
165
|
getContext: () => request("/context"),
|
|
166
|
+
listProductBlocks: (productId, input) => request(
|
|
167
|
+
productBlocksPath(productId, input)
|
|
168
|
+
),
|
|
169
|
+
createProductBlock: (productId, input) => request(productBlocksPath(productId), {
|
|
170
|
+
method: "POST",
|
|
171
|
+
body: JSON.stringify(input)
|
|
172
|
+
}),
|
|
173
|
+
updateProductBlock: (productId, blockId, input) => request(
|
|
174
|
+
`${productBlocksPath(productId)}/${encodeIdentifier(blockId)}`,
|
|
175
|
+
{ method: "PUT", body: JSON.stringify(input) }
|
|
176
|
+
),
|
|
177
|
+
reorderProductBlocks: (productId, input) => request(
|
|
178
|
+
`${productBlocksPath(productId)}/order`,
|
|
179
|
+
{ method: "PUT", body: JSON.stringify(input) }
|
|
180
|
+
),
|
|
181
|
+
retireProductBlock: (productId, blockId, expectedUpdatedAt) => request(
|
|
182
|
+
`${productBlocksPath(productId)}/${encodeIdentifier(blockId)}`,
|
|
183
|
+
{ method: "DELETE", body: JSON.stringify({ expectedUpdatedAt }) }
|
|
184
|
+
),
|
|
159
185
|
getReferences: () => request("/references"),
|
|
160
186
|
listTypes: () => request("/types"),
|
|
161
187
|
getType: (identifier) => request(`/types/${encodeIdentifier(identifier)}`),
|
package/index.cjs
CHANGED
|
@@ -1414,6 +1414,35 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
|
|
|
1414
1414
|
}
|
|
1415
1415
|
`
|
|
1416
1416
|
);
|
|
1417
|
+
var WEBSITE_GET_PRODUCT_CONTENT_BATCH = (
|
|
1418
|
+
/* GraphQL */
|
|
1419
|
+
`
|
|
1420
|
+
mutation WebsiteGetProductContentBatch(
|
|
1421
|
+
$websiteId: uuid!
|
|
1422
|
+
$productIds: [uuid!]!
|
|
1423
|
+
$locale: String
|
|
1424
|
+
) {
|
|
1425
|
+
result: websiteGetProductContentBatch(
|
|
1426
|
+
website_id: $websiteId
|
|
1427
|
+
product_ids: $productIds
|
|
1428
|
+
locale: $locale
|
|
1429
|
+
) {
|
|
1430
|
+
success
|
|
1431
|
+
errors
|
|
1432
|
+
blocks {
|
|
1433
|
+
id
|
|
1434
|
+
product_id
|
|
1435
|
+
locale
|
|
1436
|
+
content_json
|
|
1437
|
+
resolved_fields
|
|
1438
|
+
sort_order
|
|
1439
|
+
updated_at
|
|
1440
|
+
content_type { id key name kind }
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
`
|
|
1445
|
+
);
|
|
1417
1446
|
var CHECK_AVAILABILITY = (
|
|
1418
1447
|
/* GraphQL */
|
|
1419
1448
|
`
|
|
@@ -5782,6 +5811,33 @@ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
5782
5811
|
return (data.result.blocks ?? []).map(
|
|
5783
5812
|
(block) => mapProductContentBlock(block, storageUrl)
|
|
5784
5813
|
);
|
|
5814
|
+
},
|
|
5815
|
+
async getForProducts(productIds, opts) {
|
|
5816
|
+
const uniqueIds = [...new Set(productIds.map((id) => id.toLowerCase()))];
|
|
5817
|
+
const blocksByProduct = Object.fromEntries(uniqueIds.map((id) => [id, []]));
|
|
5818
|
+
if (uniqueIds.length === 0) return blocksByProduct;
|
|
5819
|
+
const ctx = getContext();
|
|
5820
|
+
for (let start = 0; start < uniqueIds.length; start += 50) {
|
|
5821
|
+
const batch = uniqueIds.slice(start, start + 50);
|
|
5822
|
+
const data = await executeGraphQL(
|
|
5823
|
+
graphqlUrl,
|
|
5824
|
+
WEBSITE_GET_PRODUCT_CONTENT_BATCH,
|
|
5825
|
+
{
|
|
5826
|
+
websiteId: ctx.websiteId,
|
|
5827
|
+
productIds: batch,
|
|
5828
|
+
locale: opts?.locale ?? DEFAULT_LOCALE
|
|
5829
|
+
},
|
|
5830
|
+
getHeaders
|
|
5831
|
+
);
|
|
5832
|
+
if (!data.result?.success) continue;
|
|
5833
|
+
for (const block of data.result.blocks ?? []) {
|
|
5834
|
+
const id = block.product_id.toLowerCase();
|
|
5835
|
+
if (Object.prototype.hasOwnProperty.call(blocksByProduct, id)) {
|
|
5836
|
+
blocksByProduct[id].push(mapProductContentBlock(block, storageUrl));
|
|
5837
|
+
}
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
return blocksByProduct;
|
|
5785
5841
|
}
|
|
5786
5842
|
};
|
|
5787
5843
|
}
|
|
@@ -7147,7 +7203,8 @@ function createBacanoClient(config) {
|
|
|
7147
7203
|
getByKey: contentModule.getByKey.bind(contentModule),
|
|
7148
7204
|
getByCategory: contentModule.getByCategory.bind(contentModule),
|
|
7149
7205
|
getByTag: contentModule.getByTag.bind(contentModule),
|
|
7150
|
-
getForProduct: contentModule.getForProduct.bind(contentModule)
|
|
7206
|
+
getForProduct: contentModule.getForProduct.bind(contentModule),
|
|
7207
|
+
getForProducts: contentModule.getForProducts.bind(contentModule)
|
|
7151
7208
|
},
|
|
7152
7209
|
forms: {
|
|
7153
7210
|
getDefinition: formsModule.getDefinition.bind(formsModule),
|
package/index.js
CHANGED
|
@@ -1353,6 +1353,35 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
|
|
|
1353
1353
|
}
|
|
1354
1354
|
`
|
|
1355
1355
|
);
|
|
1356
|
+
var WEBSITE_GET_PRODUCT_CONTENT_BATCH = (
|
|
1357
|
+
/* GraphQL */
|
|
1358
|
+
`
|
|
1359
|
+
mutation WebsiteGetProductContentBatch(
|
|
1360
|
+
$websiteId: uuid!
|
|
1361
|
+
$productIds: [uuid!]!
|
|
1362
|
+
$locale: String
|
|
1363
|
+
) {
|
|
1364
|
+
result: websiteGetProductContentBatch(
|
|
1365
|
+
website_id: $websiteId
|
|
1366
|
+
product_ids: $productIds
|
|
1367
|
+
locale: $locale
|
|
1368
|
+
) {
|
|
1369
|
+
success
|
|
1370
|
+
errors
|
|
1371
|
+
blocks {
|
|
1372
|
+
id
|
|
1373
|
+
product_id
|
|
1374
|
+
locale
|
|
1375
|
+
content_json
|
|
1376
|
+
resolved_fields
|
|
1377
|
+
sort_order
|
|
1378
|
+
updated_at
|
|
1379
|
+
content_type { id key name kind }
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
`
|
|
1384
|
+
);
|
|
1356
1385
|
var CHECK_AVAILABILITY = (
|
|
1357
1386
|
/* GraphQL */
|
|
1358
1387
|
`
|
|
@@ -5721,6 +5750,33 @@ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
5721
5750
|
return (data.result.blocks ?? []).map(
|
|
5722
5751
|
(block) => mapProductContentBlock(block, storageUrl)
|
|
5723
5752
|
);
|
|
5753
|
+
},
|
|
5754
|
+
async getForProducts(productIds, opts) {
|
|
5755
|
+
const uniqueIds = [...new Set(productIds.map((id) => id.toLowerCase()))];
|
|
5756
|
+
const blocksByProduct = Object.fromEntries(uniqueIds.map((id) => [id, []]));
|
|
5757
|
+
if (uniqueIds.length === 0) return blocksByProduct;
|
|
5758
|
+
const ctx = getContext();
|
|
5759
|
+
for (let start = 0; start < uniqueIds.length; start += 50) {
|
|
5760
|
+
const batch = uniqueIds.slice(start, start + 50);
|
|
5761
|
+
const data = await executeGraphQL(
|
|
5762
|
+
graphqlUrl,
|
|
5763
|
+
WEBSITE_GET_PRODUCT_CONTENT_BATCH,
|
|
5764
|
+
{
|
|
5765
|
+
websiteId: ctx.websiteId,
|
|
5766
|
+
productIds: batch,
|
|
5767
|
+
locale: opts?.locale ?? DEFAULT_LOCALE
|
|
5768
|
+
},
|
|
5769
|
+
getHeaders
|
|
5770
|
+
);
|
|
5771
|
+
if (!data.result?.success) continue;
|
|
5772
|
+
for (const block of data.result.blocks ?? []) {
|
|
5773
|
+
const id = block.product_id.toLowerCase();
|
|
5774
|
+
if (Object.prototype.hasOwnProperty.call(blocksByProduct, id)) {
|
|
5775
|
+
blocksByProduct[id].push(mapProductContentBlock(block, storageUrl));
|
|
5776
|
+
}
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
return blocksByProduct;
|
|
5724
5780
|
}
|
|
5725
5781
|
};
|
|
5726
5782
|
}
|
|
@@ -7086,7 +7142,8 @@ function createBacanoClient(config) {
|
|
|
7086
7142
|
getByKey: contentModule.getByKey.bind(contentModule),
|
|
7087
7143
|
getByCategory: contentModule.getByCategory.bind(contentModule),
|
|
7088
7144
|
getByTag: contentModule.getByTag.bind(contentModule),
|
|
7089
|
-
getForProduct: contentModule.getForProduct.bind(contentModule)
|
|
7145
|
+
getForProduct: contentModule.getForProduct.bind(contentModule),
|
|
7146
|
+
getForProducts: contentModule.getForProducts.bind(contentModule)
|
|
7090
7147
|
},
|
|
7091
7148
|
forms: {
|
|
7092
7149
|
getDefinition: formsModule.getDefinition.bind(formsModule),
|
package/modules/content.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export interface ContentModule {
|
|
|
5
5
|
getByCategory(categoryKey: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
|
|
6
6
|
getByTag(tag: string, opts?: GetContentOptions): Promise<ContentEntry[]>;
|
|
7
7
|
getForProduct(productId: string, opts?: GetContentOptions): Promise<ProductContentBlock[]>;
|
|
8
|
+
getForProducts(productIds: string[], opts?: GetContentOptions): Promise<Record<string, ProductContentBlock[]>>;
|
|
8
9
|
}
|
|
9
10
|
export declare function createContentModule(graphqlUrl: string, getHeaders: RequestHeadersProvider, getContext: () => BacanoClientContext, storageUrl?: string): ContentModule;
|
package/package.json
CHANGED
package/react.cjs
CHANGED
|
@@ -1122,6 +1122,35 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
|
|
|
1122
1122
|
}
|
|
1123
1123
|
`
|
|
1124
1124
|
);
|
|
1125
|
+
var WEBSITE_GET_PRODUCT_CONTENT_BATCH = (
|
|
1126
|
+
/* GraphQL */
|
|
1127
|
+
`
|
|
1128
|
+
mutation WebsiteGetProductContentBatch(
|
|
1129
|
+
$websiteId: uuid!
|
|
1130
|
+
$productIds: [uuid!]!
|
|
1131
|
+
$locale: String
|
|
1132
|
+
) {
|
|
1133
|
+
result: websiteGetProductContentBatch(
|
|
1134
|
+
website_id: $websiteId
|
|
1135
|
+
product_ids: $productIds
|
|
1136
|
+
locale: $locale
|
|
1137
|
+
) {
|
|
1138
|
+
success
|
|
1139
|
+
errors
|
|
1140
|
+
blocks {
|
|
1141
|
+
id
|
|
1142
|
+
product_id
|
|
1143
|
+
locale
|
|
1144
|
+
content_json
|
|
1145
|
+
resolved_fields
|
|
1146
|
+
sort_order
|
|
1147
|
+
updated_at
|
|
1148
|
+
content_type { id key name kind }
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
`
|
|
1153
|
+
);
|
|
1125
1154
|
var CHECK_AVAILABILITY = (
|
|
1126
1155
|
/* GraphQL */
|
|
1127
1156
|
`
|
|
@@ -6814,6 +6843,33 @@ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
6814
6843
|
return (data.result.blocks ?? []).map(
|
|
6815
6844
|
(block) => mapProductContentBlock(block, storageUrl)
|
|
6816
6845
|
);
|
|
6846
|
+
},
|
|
6847
|
+
async getForProducts(productIds, opts) {
|
|
6848
|
+
const uniqueIds = [...new Set(productIds.map((id) => id.toLowerCase()))];
|
|
6849
|
+
const blocksByProduct = Object.fromEntries(uniqueIds.map((id) => [id, []]));
|
|
6850
|
+
if (uniqueIds.length === 0) return blocksByProduct;
|
|
6851
|
+
const ctx = getContext();
|
|
6852
|
+
for (let start = 0; start < uniqueIds.length; start += 50) {
|
|
6853
|
+
const batch = uniqueIds.slice(start, start + 50);
|
|
6854
|
+
const data = await executeGraphQL(
|
|
6855
|
+
graphqlUrl,
|
|
6856
|
+
WEBSITE_GET_PRODUCT_CONTENT_BATCH,
|
|
6857
|
+
{
|
|
6858
|
+
websiteId: ctx.websiteId,
|
|
6859
|
+
productIds: batch,
|
|
6860
|
+
locale: opts?.locale ?? DEFAULT_LOCALE
|
|
6861
|
+
},
|
|
6862
|
+
getHeaders
|
|
6863
|
+
);
|
|
6864
|
+
if (!data.result?.success) continue;
|
|
6865
|
+
for (const block of data.result.blocks ?? []) {
|
|
6866
|
+
const id = block.product_id.toLowerCase();
|
|
6867
|
+
if (Object.prototype.hasOwnProperty.call(blocksByProduct, id)) {
|
|
6868
|
+
blocksByProduct[id].push(mapProductContentBlock(block, storageUrl));
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6872
|
+
return blocksByProduct;
|
|
6817
6873
|
}
|
|
6818
6874
|
};
|
|
6819
6875
|
}
|
|
@@ -7561,7 +7617,8 @@ function createBacanoClient(config) {
|
|
|
7561
7617
|
getByKey: contentModule.getByKey.bind(contentModule),
|
|
7562
7618
|
getByCategory: contentModule.getByCategory.bind(contentModule),
|
|
7563
7619
|
getByTag: contentModule.getByTag.bind(contentModule),
|
|
7564
|
-
getForProduct: contentModule.getForProduct.bind(contentModule)
|
|
7620
|
+
getForProduct: contentModule.getForProduct.bind(contentModule),
|
|
7621
|
+
getForProducts: contentModule.getForProducts.bind(contentModule)
|
|
7565
7622
|
},
|
|
7566
7623
|
forms: {
|
|
7567
7624
|
getDefinition: formsModule.getDefinition.bind(formsModule),
|
package/react.js
CHANGED
|
@@ -1065,6 +1065,35 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
|
|
|
1065
1065
|
}
|
|
1066
1066
|
`
|
|
1067
1067
|
);
|
|
1068
|
+
var WEBSITE_GET_PRODUCT_CONTENT_BATCH = (
|
|
1069
|
+
/* GraphQL */
|
|
1070
|
+
`
|
|
1071
|
+
mutation WebsiteGetProductContentBatch(
|
|
1072
|
+
$websiteId: uuid!
|
|
1073
|
+
$productIds: [uuid!]!
|
|
1074
|
+
$locale: String
|
|
1075
|
+
) {
|
|
1076
|
+
result: websiteGetProductContentBatch(
|
|
1077
|
+
website_id: $websiteId
|
|
1078
|
+
product_ids: $productIds
|
|
1079
|
+
locale: $locale
|
|
1080
|
+
) {
|
|
1081
|
+
success
|
|
1082
|
+
errors
|
|
1083
|
+
blocks {
|
|
1084
|
+
id
|
|
1085
|
+
product_id
|
|
1086
|
+
locale
|
|
1087
|
+
content_json
|
|
1088
|
+
resolved_fields
|
|
1089
|
+
sort_order
|
|
1090
|
+
updated_at
|
|
1091
|
+
content_type { id key name kind }
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
`
|
|
1096
|
+
);
|
|
1068
1097
|
var CHECK_AVAILABILITY = (
|
|
1069
1098
|
/* GraphQL */
|
|
1070
1099
|
`
|
|
@@ -6761,6 +6790,33 @@ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
6761
6790
|
return (data.result.blocks ?? []).map(
|
|
6762
6791
|
(block) => mapProductContentBlock(block, storageUrl)
|
|
6763
6792
|
);
|
|
6793
|
+
},
|
|
6794
|
+
async getForProducts(productIds, opts) {
|
|
6795
|
+
const uniqueIds = [...new Set(productIds.map((id) => id.toLowerCase()))];
|
|
6796
|
+
const blocksByProduct = Object.fromEntries(uniqueIds.map((id) => [id, []]));
|
|
6797
|
+
if (uniqueIds.length === 0) return blocksByProduct;
|
|
6798
|
+
const ctx = getContext();
|
|
6799
|
+
for (let start = 0; start < uniqueIds.length; start += 50) {
|
|
6800
|
+
const batch = uniqueIds.slice(start, start + 50);
|
|
6801
|
+
const data = await executeGraphQL(
|
|
6802
|
+
graphqlUrl,
|
|
6803
|
+
WEBSITE_GET_PRODUCT_CONTENT_BATCH,
|
|
6804
|
+
{
|
|
6805
|
+
websiteId: ctx.websiteId,
|
|
6806
|
+
productIds: batch,
|
|
6807
|
+
locale: opts?.locale ?? DEFAULT_LOCALE
|
|
6808
|
+
},
|
|
6809
|
+
getHeaders
|
|
6810
|
+
);
|
|
6811
|
+
if (!data.result?.success) continue;
|
|
6812
|
+
for (const block of data.result.blocks ?? []) {
|
|
6813
|
+
const id = block.product_id.toLowerCase();
|
|
6814
|
+
if (Object.prototype.hasOwnProperty.call(blocksByProduct, id)) {
|
|
6815
|
+
blocksByProduct[id].push(mapProductContentBlock(block, storageUrl));
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
}
|
|
6819
|
+
return blocksByProduct;
|
|
6764
6820
|
}
|
|
6765
6821
|
};
|
|
6766
6822
|
}
|
|
@@ -7508,7 +7564,8 @@ function createBacanoClient(config) {
|
|
|
7508
7564
|
getByKey: contentModule.getByKey.bind(contentModule),
|
|
7509
7565
|
getByCategory: contentModule.getByCategory.bind(contentModule),
|
|
7510
7566
|
getByTag: contentModule.getByTag.bind(contentModule),
|
|
7511
|
-
getForProduct: contentModule.getForProduct.bind(contentModule)
|
|
7567
|
+
getForProduct: contentModule.getForProduct.bind(contentModule),
|
|
7568
|
+
getForProducts: contentModule.getForProducts.bind(contentModule)
|
|
7512
7569
|
},
|
|
7513
7570
|
forms: {
|
|
7514
7571
|
getDefinition: formsModule.getDefinition.bind(formsModule),
|
package/transport/documents.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare const WEBSITE_GET_PRODUCT_SPECIFICATIONS = "\n mutation WebsiteG
|
|
|
13
13
|
export declare const GET_BRANDS = "\n query GetBrands($companyId: uuid!, $chainId: uuid!) {\n brands(\n where: {\n company_id: { _eq: $companyId }\n products: {\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n order_by: { name: asc }\n ) {\n id\n name\n }\n }\n";
|
|
14
14
|
export declare const WEBSITE_GET_CONTENT = "\n mutation WebsiteGetContent(\n $websiteId: uuid!\n $key: String\n $categoryKey: String\n $tag: String\n $locale: String\n ) {\n result: websiteGetContent(\n website_id: $websiteId\n key: $key\n category_key: $categoryKey\n tag: $tag\n locale: $locale\n ) {\n success\n errors\n entries {\n \n id\n key\n category_key\n name\n locale\n status\n tags\n content_json\n resolved_fields\n published_at\n updated_at\n content_type {\n id\n key\n name\n kind\n }\n\n }\n }\n }\n";
|
|
15
15
|
export declare const WEBSITE_GET_PRODUCT_CONTENT = "\n mutation WebsiteGetProductContent(\n $websiteId: uuid!\n $productId: uuid!\n $locale: String\n ) {\n result: websiteGetProductContent(\n website_id: $websiteId\n product_id: $productId\n locale: $locale\n ) {\n success\n errors\n blocks {\n id\n product_id\n locale\n content_json\n resolved_fields\n sort_order\n updated_at\n content_type {\n id\n key\n name\n kind\n }\n }\n }\n }\n";
|
|
16
|
+
export declare const WEBSITE_GET_PRODUCT_CONTENT_BATCH = "\n mutation WebsiteGetProductContentBatch(\n $websiteId: uuid!\n $productIds: [uuid!]!\n $locale: String\n ) {\n result: websiteGetProductContentBatch(\n website_id: $websiteId\n product_ids: $productIds\n locale: $locale\n ) {\n success\n errors\n blocks {\n id\n product_id\n locale\n content_json\n resolved_fields\n sort_order\n updated_at\n content_type { id key name kind }\n }\n }\n }\n";
|
|
16
17
|
export declare const CHECK_AVAILABILITY = "\n query CheckAvailability(\n $variantIds: [uuid!]!\n $chainId: uuid!\n $websiteId: uuid\n ) {\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n website_id: $websiteId\n ) {\n product_variant_id\n in_stock\n label\n max_purchasable_quantity\n }\n }\n";
|
|
17
18
|
export declare const GET_PRODUCT_LIVE_STATE = "\n query GetProductLiveState(\n $variantIds: [uuid!]!\n $companyId: uuid!\n $chainId: uuid!\n $websiteId: uuid!\n ) {\n product_variants(\n where: {\n id: { _in: $variantIds }\n is_disabled: { _eq: false }\n product: {\n company_id: { _eq: $companyId }\n product_website_publications: {\n website_id: { _eq: $websiteId }\n is_visible: { _eq: true }\n }\n chain_products: {\n chain_id: { _eq: $chainId }\n is_enabled: { _eq: true }\n status: { _eq: active }\n }\n }\n }\n ) {\n id\n product_variant_prices(\n where: {\n price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }\n _or: [\n { chain_id: { _eq: $chainId } }\n { chain_id: { _is_null: true } }\n ]\n }\n ) {\n id\n price\n price_type\n chain_id\n }\n }\n websiteProductAvailability(\n product_variant_ids: $variantIds\n chain_id: $chainId\n website_id: $websiteId\n ) {\n product_variant_id\n in_stock\n label\n max_purchasable_quantity\n }\n }\n";
|
|
18
19
|
export declare const CREATE_CART = "\n mutation CreateCart(\n $websiteId: uuid!\n $cartToken: String\n $contact: WebsiteGuestContactInput\n $fulfillmentFamily: String\n ) {\n websiteCreateCart(\n website_id: $websiteId\n cart_token: $cartToken\n contact: $contact\n fulfillment_family: $fulfillmentFamily\n ) {\n success\n order_id\n cart_token\n cart {\n id\n status\n discount\n tax\n total_without_tax\n total_with_tax\n total_with_discount\n created_at\n updated_at\n volume_pricing {\n enabled\n eligibleQuantity\n appliedPriceType\n appliedMinQuantity\n nextPriceType\n nextMinQuantity\n remainingQuantity\n totalSavings\n }\n items {\n id\n product_variant_id\n quantity\n unit_price\n line_total\n base_unit_price\n unit_savings\n line_savings\n volume_pricing_eligible\n resolved_price_type\n volume_pricing_min_quantity\n product_variant {\n id\n sku\n product {\n id\n name\n }\n prices {\n id\n price\n price_type\n chain_id\n }\n }\n }\n }\n errors\n }\n }\n";
|