@sixtyninecommerce/admin 0.10.0 → 0.11.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.
- package/README.md +4347 -171
- package/dist/catalog/assets.d.ts +120 -8
- package/dist/catalog/assets.js +98 -12
- package/dist/catalog/collections.d.ts +279 -14
- package/dist/catalog/collections.js +246 -15
- package/dist/catalog/index.d.ts +9 -2
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +102 -14
- package/dist/catalog/properties.js +13 -0
- package/dist/catalog/recommendations.d.ts +261 -0
- package/dist/catalog/recommendations.js +145 -0
- package/dist/catalog/reviews.d.ts +368 -0
- package/dist/catalog/reviews.js +318 -0
- package/dist/catalog/types.d.ts +17 -1
- package/dist/catalog/types.js +10 -0
- package/dist/catalog/variant-find.d.ts +136 -0
- package/dist/catalog/variant-find.js +239 -0
- package/dist/catalog/variants.d.ts +233 -26
- package/dist/catalog/variants.js +125 -7
- package/dist/catalog/visibility.d.ts +20 -5
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +95 -9
- package/dist/client.js +35 -7
- package/dist/content/assets.d.ts +286 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/items.d.ts +269 -0
- package/dist/content/items.js +170 -0
- package/dist/content/types.d.ts +282 -0
- package/dist/content/types.js +100 -0
- package/dist/customers/age-verification.d.ts +66 -0
- package/dist/customers/age-verification.js +79 -0
- package/dist/customers/credit.d.ts +263 -0
- package/dist/customers/credit.js +244 -0
- package/dist/customers/delivery-preferences.d.ts +120 -0
- package/dist/customers/delivery-preferences.js +97 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +447 -37
- package/dist/customers/index.js +313 -40
- package/dist/customers/invitation-status.d.ts +57 -0
- package/dist/customers/invitation-status.js +21 -0
- package/dist/customers/notes.d.ts +67 -0
- package/dist/customers/notes.js +51 -0
- package/dist/customers/notification-recipients.d.ts +97 -0
- package/dist/customers/notification-recipients.js +84 -0
- package/dist/customers/saved-lists.d.ts +42 -0
- package/dist/customers/saved-lists.js +61 -0
- package/dist/customers/vat.d.ts +83 -0
- package/dist/customers/vat.js +46 -0
- package/dist/customers/wire.d.ts +20 -0
- package/dist/customers/wire.js +35 -0
- package/dist/documents/ar.d.ts +307 -0
- package/dist/documents/ar.js +254 -0
- package/dist/documents/index.d.ts +731 -21
- package/dist/documents/index.js +436 -24
- package/dist/documents/pdf.d.ts +2 -0
- package/dist/documents/pdf.js +11 -0
- package/dist/fulfillments/create.d.ts +167 -0
- package/dist/fulfillments/create.js +216 -0
- package/dist/fulfillments/index.d.ts +44 -10
- package/dist/fulfillments/index.js +11 -2
- package/dist/fulfillments/payment-policy.d.ts +64 -0
- package/dist/fulfillments/payment-policy.js +140 -0
- package/dist/fulfillments/warehouse.d.ts +724 -0
- package/dist/fulfillments/warehouse.js +914 -0
- package/dist/geo/index.d.ts +119 -0
- package/dist/geo/index.js +107 -0
- package/dist/index.d.ts +123 -35
- package/dist/index.js +45 -12
- package/dist/integrations/afas-actions.d.ts +234 -0
- package/dist/integrations/afas-actions.js +310 -0
- package/dist/integrations/afas-types.d.ts +483 -0
- package/dist/integrations/afas-types.js +5 -0
- package/dist/integrations/afas.d.ts +234 -0
- package/dist/integrations/afas.js +290 -0
- package/dist/integrations/connections.d.ts +103 -0
- package/dist/integrations/connections.js +55 -0
- package/dist/integrations/esp.d.ts +313 -0
- package/dist/integrations/esp.js +169 -0
- package/dist/integrations/exact-online-types.d.ts +423 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +248 -0
- package/dist/integrations/exact-online.js +376 -0
- package/dist/integrations/index.d.ts +29 -3
- package/dist/integrations/index.js +18 -3
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipelines.d.ts +15 -14
- package/dist/integrations/pipelines.js +6 -22
- package/dist/integrations/types.d.ts +146 -0
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +162 -0
- package/dist/orders/cancel-with-money.d.ts +227 -0
- package/dist/orders/cancel-with-money.js +153 -0
- package/dist/orders/commit-edit.d.ts +7 -1
- package/dist/orders/commit-edit.js +5 -1
- package/dist/orders/drafts.d.ts +800 -0
- package/dist/orders/drafts.js +771 -0
- package/dist/orders/fields.d.ts +245 -0
- package/dist/orders/fields.js +188 -0
- package/dist/orders/get.d.ts +29 -19
- package/dist/orders/get.js +17 -8
- package/dist/orders/index.d.ts +49 -9
- package/dist/orders/index.js +22 -2
- package/dist/orders/lifecycle.d.ts +128 -0
- package/dist/orders/lifecycle.js +158 -0
- package/dist/orders/line-price-preview.d.ts +7 -2
- package/dist/orders/list.d.ts +222 -0
- package/dist/orders/list.js +377 -0
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +1 -1
- package/dist/orders/plan-edit.js +4 -1
- package/dist/orders/settings.d.ts +201 -0
- package/dist/orders/settings.js +325 -0
- package/dist/orders/status.d.ts +159 -13
- package/dist/orders/status.js +21 -0
- package/dist/orders/surcharges.d.ts +8 -1
- package/dist/orders/surcharges.js +3 -2
- package/dist/payments/account-credit.d.ts +30 -0
- package/dist/payments/account-credit.js +19 -0
- package/dist/payments/ar.d.ts +292 -0
- package/dist/payments/ar.js +223 -0
- package/dist/payments/connected-account.d.ts +47 -0
- package/dist/payments/connected-account.js +61 -0
- package/dist/payments/credit-noted-fields.d.ts +12 -0
- package/dist/payments/credit-noted-fields.js +37 -0
- package/dist/payments/index.d.ts +257 -11
- package/dist/payments/index.js +131 -1
- package/dist/payments/methods.d.ts +303 -8
- package/dist/payments/methods.js +337 -7
- package/dist/payments/mollie-methods.d.ts +159 -0
- package/dist/payments/mollie-methods.js +141 -0
- package/dist/payments/payment-links.d.ts +180 -0
- package/dist/payments/payment-links.js +97 -0
- package/dist/payments/provider-keys.d.ts +150 -0
- package/dist/payments/provider-keys.js +118 -0
- package/dist/payments/providers.d.ts +86 -11
- package/dist/payments/providers.js +29 -0
- package/dist/payments/requests.d.ts +144 -0
- package/dist/payments/requests.js +92 -0
- package/dist/payments/store-credit-fields.d.ts +15 -0
- package/dist/payments/store-credit-fields.js +51 -0
- package/dist/pickups/index.d.ts +104 -0
- package/dist/pickups/index.js +105 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +154 -0
- package/dist/platform/tax.js +139 -0
- package/dist/pos/anomalies.d.ts +158 -0
- package/dist/pos/anomalies.js +64 -0
- package/dist/pos/assisted-selling.d.ts +79 -0
- package/dist/pos/assisted-selling.js +38 -0
- package/dist/pos/exchanges.d.ts +103 -0
- package/dist/pos/exchanges.js +22 -0
- package/dist/pos/gift-cards.d.ts +135 -0
- package/dist/pos/gift-cards.js +47 -0
- package/dist/pos/index.d.ts +2125 -32
- package/dist/pos/index.js +742 -4
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +187 -0
- package/dist/pos/offline-sales.js +116 -0
- package/dist/pos/pay-by-link.d.ts +143 -0
- package/dist/pos/pay-by-link.js +57 -0
- package/dist/pos/quick-keys.d.ts +103 -0
- package/dist/pos/quick-keys.js +45 -0
- package/dist/pos/reports.d.ts +360 -0
- package/dist/pos/reports.js +62 -0
- package/dist/pos/returns.d.ts +217 -0
- package/dist/pos/returns.js +53 -0
- package/dist/pricing/bulk-coupons.d.ts +139 -0
- package/dist/pricing/bulk-coupons.js +112 -0
- package/dist/pricing/index.d.ts +174 -4
- package/dist/pricing/index.js +174 -1
- package/dist/pricing/price-lists.d.ts +604 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +89 -0
- package/dist/purchasing/alerts.js +62 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +36 -0
- package/dist/purchasing/index.js +16 -0
- package/dist/purchasing/purchase-orders.d.ts +548 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/settings.d.ts +152 -0
- package/dist/purchasing/settings.js +181 -0
- package/dist/purchasing/suppliers.d.ts +476 -0
- package/dist/purchasing/suppliers.js +438 -0
- package/dist/quotes/index.d.ts +501 -0
- package/dist/quotes/index.js +439 -0
- package/dist/reports/index.d.ts +71 -8
- package/dist/reports/index.js +74 -11
- package/dist/returns/index.d.ts +614 -21
- package/dist/returns/index.js +695 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/shipping/definitions.d.ts +29 -0
- package/dist/shipping/definitions.js +47 -0
- package/dist/shipping/fulfillment-profiles.d.ts +174 -0
- package/dist/shipping/fulfillment-profiles.js +104 -0
- package/dist/shipping/hendriks.d.ts +97 -0
- package/dist/shipping/hendriks.js +50 -0
- package/dist/shipping/index.d.ts +48 -0
- package/dist/shipping/index.js +25 -0
- package/dist/shipping/methods.d.ts +198 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +254 -0
- package/dist/shipping/myparcel.js +178 -0
- package/dist/shipping/parcel-count-suggestion.d.ts +2 -0
- package/dist/shipping/parcel-rule.d.ts +6 -5
- package/dist/shipping/parcel-rule.js +21 -23
- package/dist/shipping/rate-tables.d.ts +179 -0
- package/dist/shipping/rate-tables.js +184 -0
- package/dist/stock/index.d.ts +10 -3
- package/dist/stock/index.js +3 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/transfers.d.ts +216 -43
- package/dist/stock/transfers.js +140 -13
- package/dist/stored-value/index.d.ts +214 -4
- package/dist/stored-value/index.js +152 -0
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/index.d.ts +76 -1
- package/dist/tax/index.js +50 -0
- package/dist/tenant/email-style.d.ts +102 -0
- package/dist/tenant/email-style.js +72 -0
- package/dist/tenant/email-templates.d.ts +66 -0
- package/dist/tenant/email-templates.js +54 -0
- package/dist/tenant/notifications.d.ts +23 -17
- package/dist/tenant/settings-sources.d.ts +1 -1
- package/dist/tenant/time-zone.d.ts +17 -0
- package/dist/tenant/time-zone.js +11 -0
- package/dist/types.d.ts +50 -3
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +2 -0
- package/dist/users/warehouses.d.ts +50 -0
- package/dist/users/warehouses.js +51 -0
- package/package.json +2 -2
package/dist/catalog/products.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ValidationError } from '@sixtyninecommerce/core';
|
|
2
2
|
import { bulkProducts, readProductBulkRun, } from './products-bulk.js';
|
|
3
|
-
import { ASSET_REF_FIELDS, assertDeleted, availabilityCustomFields, CATALOGUE_OWNERSHIP_FIELDS, DELETION_FIELDS, FACET_VALUE_REF_FIELDS, fieldOf, unwrapResult, vendureListPage, } from './types.js';
|
|
3
|
+
import { ASSET_REF_FIELDS, assertDeleted, availabilityCustomFields, CATALOGUE_OWNERSHIP_FIELDS, DELETION_FIELDS, FACET_VALUE_REF_FIELDS, fieldOf, toAssetRef, unwrapResult, vendureListPage, } from './types.js';
|
|
4
4
|
import { assertPricesStateBasis, } from './variants.js';
|
|
5
5
|
/** The engine's bound on one `exclude` or `include` call (`STORE_EXCLUSION_BATCH_LIMIT`). */
|
|
6
6
|
export const MAX_STORE_EXCLUSION_PRODUCTS = 500;
|
|
7
|
+
/** At most this many distinct products per {@link ProductsModule.storePriceRanges} call. */
|
|
8
|
+
export const MAX_STORE_PRICE_RANGE_PRODUCTS = 100;
|
|
7
9
|
/**
|
|
8
10
|
* The engine's bounds on one `setAvailabilityInStores` or `availabilityInStores` call
|
|
9
11
|
* (`AVAILABILITY_*_LIMIT`).
|
|
@@ -29,11 +31,19 @@ export const PRODUCT_FIELDS = /* GraphQL */ `
|
|
|
29
31
|
id
|
|
30
32
|
code
|
|
31
33
|
name
|
|
34
|
+
customFields {
|
|
35
|
+
isShared
|
|
36
|
+
}
|
|
32
37
|
options {
|
|
33
38
|
id
|
|
34
39
|
code
|
|
35
40
|
name
|
|
36
41
|
}
|
|
42
|
+
translations {
|
|
43
|
+
id
|
|
44
|
+
languageCode
|
|
45
|
+
name
|
|
46
|
+
}
|
|
37
47
|
}
|
|
38
48
|
translations {
|
|
39
49
|
id
|
|
@@ -50,6 +60,8 @@ export const PRODUCT_FIELDS = /* GraphQL */ `
|
|
|
50
60
|
availabilityState
|
|
51
61
|
availableFrom
|
|
52
62
|
preorderLimit
|
|
63
|
+
brand
|
|
64
|
+
condition
|
|
53
65
|
}
|
|
54
66
|
`;
|
|
55
67
|
export const PRODUCTS_QUERY = /* GraphQL */ `
|
|
@@ -60,9 +72,36 @@ export const PRODUCTS_QUERY = /* GraphQL */ `
|
|
|
60
72
|
}
|
|
61
73
|
}
|
|
62
74
|
`;
|
|
75
|
+
const STORE_PRICE_FIELDS = 'price priceWithTax currencyCode';
|
|
76
|
+
export const PRODUCT_STORE_PRICE_RANGES_QUERY = /* GraphQL */ `
|
|
77
|
+
query CatalogProductStorePriceRanges($options: ProductListOptions) {
|
|
78
|
+
products(options: $options) {
|
|
79
|
+
items {
|
|
80
|
+
id
|
|
81
|
+
storePriceRange {
|
|
82
|
+
min { ${STORE_PRICE_FIELDS} }
|
|
83
|
+
max { ${STORE_PRICE_FIELDS} }
|
|
84
|
+
someVariantsUnpriced
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
/**
|
|
91
|
+
* One product, with the collections it is in. No variant data beyond `variantList`'s count: a
|
|
92
|
+
* product can have 100,000 variants, and core's `variants` would hand back the first 1,000 with
|
|
93
|
+
* no sign of the rest (`catalog.variants.list` pages them).
|
|
94
|
+
*/
|
|
63
95
|
export const PRODUCT_QUERY = /* GraphQL */ `
|
|
64
96
|
query CatalogProduct($id: ID!) {
|
|
65
|
-
product(id: $id) {
|
|
97
|
+
product(id: $id) {
|
|
98
|
+
${PRODUCT_FIELDS}
|
|
99
|
+
collections {
|
|
100
|
+
id
|
|
101
|
+
name
|
|
102
|
+
slug
|
|
103
|
+
}
|
|
104
|
+
}
|
|
66
105
|
}
|
|
67
106
|
`;
|
|
68
107
|
export const CREATE_PRODUCT_MUTATION = /* GraphQL */ `
|
|
@@ -196,15 +235,28 @@ export const SAVE_PRODUCT_MUTATION = /* GraphQL */ `
|
|
|
196
235
|
* outright, so the defaults below only cover a NULL value, never an absent one.
|
|
197
236
|
*/
|
|
198
237
|
export function toProduct(wire) {
|
|
199
|
-
const { variantList, customFields, ...rest } = wire;
|
|
238
|
+
const { variantList, customFields, featuredAsset, assets, optionGroups, ...rest } = wire;
|
|
200
239
|
return {
|
|
201
240
|
...rest,
|
|
241
|
+
featuredAsset: featuredAsset ? toAssetRef(featuredAsset) : null,
|
|
242
|
+
assets: assets.map(toAssetRef),
|
|
243
|
+
optionGroups: optionGroups.map(({ customFields: groupFields, ...group }) => ({
|
|
244
|
+
...group,
|
|
245
|
+
isShared: groupFields?.isShared === true,
|
|
246
|
+
})),
|
|
202
247
|
variantCount: variantList.totalItems,
|
|
203
248
|
availabilityState: customFields?.availabilityState ?? 'none',
|
|
204
249
|
availableFrom: customFields?.availableFrom ?? null,
|
|
205
250
|
preorderLimit: customFields?.preorderLimit ?? null,
|
|
251
|
+
brand: customFields?.brand ?? null,
|
|
252
|
+
condition: customFields?.condition ?? null,
|
|
206
253
|
};
|
|
207
254
|
}
|
|
255
|
+
/** {@link toProduct}, plus the collections. */
|
|
256
|
+
function toProductDetail(wire) {
|
|
257
|
+
const { collections, ...rest } = wire;
|
|
258
|
+
return { ...toProduct(rest), collections };
|
|
259
|
+
}
|
|
208
260
|
function toWireInput(input) {
|
|
209
261
|
const { availabilityState, availableFrom, preorderLimit, brand, condition, ...rest } = input;
|
|
210
262
|
const customFields = availabilityCustomFields({
|
|
@@ -237,6 +289,16 @@ export function productFilter(filter) {
|
|
|
237
289
|
}
|
|
238
290
|
return out;
|
|
239
291
|
}
|
|
292
|
+
/** The engine's `ProductListOptions` for one page of {@link ProductListOptions}. */
|
|
293
|
+
export function productListOptions(options, skip, take) {
|
|
294
|
+
const filter = productFilter(options?.filter);
|
|
295
|
+
return {
|
|
296
|
+
skip,
|
|
297
|
+
take,
|
|
298
|
+
...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
|
|
299
|
+
...(options?.sort ? { sort: { [options.sort.field]: options.sort.direction } } : {}),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
240
302
|
function refuse(code, message) {
|
|
241
303
|
return new ValidationError({ code, message, status: 400, service: 'vendure' });
|
|
242
304
|
}
|
|
@@ -290,19 +352,11 @@ export function createProductsModule(transport) {
|
|
|
290
352
|
};
|
|
291
353
|
return {
|
|
292
354
|
async list(options) {
|
|
293
|
-
|
|
294
|
-
return vendureListPage(transport, PRODUCTS_QUERY, 'products', (skip, take) => ({
|
|
295
|
-
options: {
|
|
296
|
-
skip,
|
|
297
|
-
take,
|
|
298
|
-
...(Object.keys(filter).length > 0 ? { filter, filterOperator: 'AND' } : {}),
|
|
299
|
-
...(options?.sort ? { sort: { [options.sort.field]: options.sort.direction } } : {}),
|
|
300
|
-
},
|
|
301
|
-
}), options, toProduct);
|
|
355
|
+
return vendureListPage(transport, PRODUCTS_QUERY, 'products', (skip, take) => ({ options: productListOptions(options, skip, take) }), options, toProduct);
|
|
302
356
|
},
|
|
303
357
|
async get(id, options) {
|
|
304
358
|
const product = await fieldOf(transport, PRODUCT_QUERY, 'product', { id }, options);
|
|
305
|
-
return product ?
|
|
359
|
+
return product ? toProductDetail(product) : null;
|
|
306
360
|
},
|
|
307
361
|
// `async` so every refusal below — including `toWireInput`'s datetime
|
|
308
362
|
// check — arrives as a rejection, never as a synchronous throw a caller's
|
|
@@ -362,6 +416,27 @@ export function createProductsModule(transport) {
|
|
|
362
416
|
assertAvailabilityInput(input, 'read');
|
|
363
417
|
return fieldOf(transport, ORGANIZATION_PRODUCT_AVAILABILITY_QUERY, 'organizationProductAvailability', { productIds: input.productIds, channelIds: input.channelIds }, options);
|
|
364
418
|
},
|
|
419
|
+
async storePriceRanges(productIds, options) {
|
|
420
|
+
// The engine stores a uuid lower-case, so an upper-case repeat is the same product.
|
|
421
|
+
const named = new Map();
|
|
422
|
+
for (const id of productIds) {
|
|
423
|
+
const key = String(id).toLowerCase();
|
|
424
|
+
if (!named.has(key))
|
|
425
|
+
named.set(key, id);
|
|
426
|
+
}
|
|
427
|
+
if (named.size > MAX_STORE_PRICE_RANGE_PRODUCTS) {
|
|
428
|
+
throw refuse('PRODUCT_STORE_PRICE_RANGE_INPUT_INVALID', `Ask for at most ${String(MAX_STORE_PRICE_RANGE_PRODUCTS)} products per call; this one named ${String(named.size)}. Nothing was sent.`);
|
|
429
|
+
}
|
|
430
|
+
if (named.size === 0)
|
|
431
|
+
return [];
|
|
432
|
+
const ids = [...named.values()];
|
|
433
|
+
const page = await fieldOf(transport, PRODUCT_STORE_PRICE_RANGES_QUERY, 'products', { options: { filter: { id: { in: ids } }, take: ids.length } }, options);
|
|
434
|
+
const answered = new Map(page.items.map((item) => [item.id.toLowerCase(), item]));
|
|
435
|
+
return [...named.keys()].flatMap((key) => {
|
|
436
|
+
const item = answered.get(key);
|
|
437
|
+
return item ? [{ productId: item.id, range: item.storePriceRange }] : [];
|
|
438
|
+
});
|
|
439
|
+
},
|
|
365
440
|
async makeLocalCopy(productId, options) {
|
|
366
441
|
const { skus, ...call } = options ?? {};
|
|
367
442
|
const result = await fieldOf(transport, MAKE_LOCAL_COPY_MUTATION, 'makeLocalCopy', {
|
|
@@ -33,6 +33,11 @@ export interface Property {
|
|
|
33
33
|
position: number;
|
|
34
34
|
config: PropertyConfig | null;
|
|
35
35
|
facetId: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* A filterable `content_item` property's field-level facets, one per content field it filters
|
|
38
|
+
* by, as `configureFieldFacets` last wrote them; null when it has none.
|
|
39
|
+
*/
|
|
40
|
+
fieldFacets: PropertyFieldFacet[] | null;
|
|
36
41
|
translations: Array<{
|
|
37
42
|
id: string;
|
|
38
43
|
languageCode: LanguageCode;
|
|
@@ -40,6 +45,25 @@ export interface Property {
|
|
|
40
45
|
helpText: string | null;
|
|
41
46
|
}>;
|
|
42
47
|
}
|
|
48
|
+
/** One field-level facet of a `content_item` property: the facet a content field's values fill. */
|
|
49
|
+
export interface PropertyFieldFacet {
|
|
50
|
+
fieldSlug: string;
|
|
51
|
+
/** The content field's type (`enum`, `multi_enum`, `boolean`). */
|
|
52
|
+
fieldType: string;
|
|
53
|
+
facetId: string;
|
|
54
|
+
}
|
|
55
|
+
/** A content field a `content_item` property filters by (`configureFieldFacets`). */
|
|
56
|
+
export interface FieldFacetDefinition {
|
|
57
|
+
/** The content type's field slug. */
|
|
58
|
+
fieldSlug: string;
|
|
59
|
+
/** The content field's type (`enum`, `multi_enum`, `boolean`). */
|
|
60
|
+
fieldType: string;
|
|
61
|
+
/** For an `enum` / `multi_enum` field: its options, each becoming one facet value. */
|
|
62
|
+
options?: Array<{
|
|
63
|
+
value: string;
|
|
64
|
+
label: string;
|
|
65
|
+
}>;
|
|
66
|
+
}
|
|
43
67
|
export interface PropertyGroup {
|
|
44
68
|
id: string;
|
|
45
69
|
createdAt: string;
|
|
@@ -186,47 +210,111 @@ export interface ContentFacetSyncResult {
|
|
|
186
210
|
*/
|
|
187
211
|
export interface PropertiesModule {
|
|
188
212
|
groups: {
|
|
189
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* The store's own groups, the complete list as one terminal page (the query takes no paging).
|
|
215
|
+
*
|
|
216
|
+
* @effect read
|
|
217
|
+
*/
|
|
190
218
|
list(options?: Call): Promise<Page<PropertyGroup>>;
|
|
219
|
+
/**
|
|
220
|
+
* One of the store's own groups, or null.
|
|
221
|
+
*
|
|
222
|
+
* @effect read
|
|
223
|
+
*/
|
|
191
224
|
get(id: string, options?: Call): Promise<PropertyGroup | null>;
|
|
225
|
+
/** @effect write */
|
|
192
226
|
create(input: CreatePropertyGroupInput, options?: Call): Promise<PropertyGroup>;
|
|
227
|
+
/** @effect write */
|
|
193
228
|
update(input: UpdatePropertyGroupInput, options?: Call): Promise<PropertyGroup>;
|
|
194
|
-
/**
|
|
229
|
+
/**
|
|
230
|
+
* Deletes the group AND every property in it, with their values on every product and the
|
|
231
|
+
* filter values (facets) the filterable ones mirror. Not undoable.
|
|
232
|
+
*
|
|
233
|
+
* @effect destructive
|
|
234
|
+
*/
|
|
195
235
|
delete(id: string, options?: Call): Promise<void>;
|
|
196
236
|
/**
|
|
197
237
|
* Pass EVERY group id of the store, in the order wanted. Only the store's own groups:
|
|
198
238
|
* a workspace group (`editableHere` false) or another store's id refuses the whole call.
|
|
239
|
+
*
|
|
240
|
+
* @effect write
|
|
199
241
|
*/
|
|
200
242
|
reorder(ids: string[], options?: Call): Promise<PropertyGroup[]>;
|
|
201
243
|
};
|
|
244
|
+
/** @effect write */
|
|
202
245
|
create(input: CreatePropertyInput, options?: Call): Promise<Property>;
|
|
246
|
+
/** @effect write */
|
|
203
247
|
update(input: UpdatePropertyInput, options?: Call): Promise<Property>;
|
|
248
|
+
/**
|
|
249
|
+
* Deletes the property with its values on every product, and the filter values (facets) a
|
|
250
|
+
* filterable one mirrors. Not undoable.
|
|
251
|
+
*
|
|
252
|
+
* @effect destructive
|
|
253
|
+
*/
|
|
204
254
|
delete(id: string, options?: Call): Promise<void>;
|
|
205
|
-
/**
|
|
255
|
+
/**
|
|
256
|
+
* Pass every property id of the group, in the order wanted.
|
|
257
|
+
*
|
|
258
|
+
* @effect write
|
|
259
|
+
*/
|
|
206
260
|
reorder(groupId: string, propertyIds: string[], options?: Call): Promise<Property[]>;
|
|
261
|
+
/**
|
|
262
|
+
* Sets which content fields a filterable `content_item` property filters by: one field-level
|
|
263
|
+
* facet per field named, its values the field's options (an `enum` / `multi_enum` field) or
|
|
264
|
+
* yes/no (a `boolean` one). Needs `UpdateCatalog`.
|
|
265
|
+
*
|
|
266
|
+
* `fields` is the WHOLE set: a field already configured keeps its facet (its values follow the
|
|
267
|
+
* options sent), a new one gets a facet, and a field left out loses its facet, which is removed
|
|
268
|
+
* from every product of the store. `[]` removes them all. Send the fields the property was
|
|
269
|
+
* SAVED with (`config.filterableFieldSlugs`), never an unsaved form's. The products' field
|
|
270
|
+
* values are filled by `catalog.syncContentFacets`, not here. It resolves with nothing (the
|
|
271
|
+
* engine answers only that it is done): the property's `fieldFacets`, read again through
|
|
272
|
+
* `groups.get`, holds what was written.
|
|
273
|
+
*
|
|
274
|
+
* Refusals (nothing is written):
|
|
275
|
+
* - `PROPERTY_NOT_FOUND` → `NotFoundError` (404): no such property among the store's own groups.
|
|
276
|
+
* - `PROPERTY_FACET_SYNC_INVALID` → `ValidationError` (400): not a `content_item` property, or
|
|
277
|
+
* not saved as filterable.
|
|
278
|
+
* - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): in a store of an organization, a
|
|
279
|
+
* property of the workspace's groups (`editableHere` false): configure it in the workspace.
|
|
280
|
+
*
|
|
281
|
+
* Not idempotent by key; repeating the same `fields` writes the same facets again.
|
|
282
|
+
*
|
|
283
|
+
* Destructive: a dropped field's facet and a dropped option's filter value leave every product.
|
|
284
|
+
*
|
|
285
|
+
* @effect destructive
|
|
286
|
+
*/
|
|
287
|
+
configureFieldFacets(propertyId: string, fields: FieldFacetDefinition[], options?: Call): Promise<void>;
|
|
207
288
|
values: {
|
|
208
289
|
/**
|
|
209
290
|
* Every group of the store, each with this product's stored values in every language. For a
|
|
210
291
|
* product the store holds from its organization, the workspace's groups and values come too,
|
|
211
292
|
* with `group.editableHere` false.
|
|
293
|
+
*
|
|
294
|
+
* @effect read
|
|
212
295
|
*/
|
|
213
296
|
get(productId: string, options?: Call): Promise<ProductPropertyGroupValues[]>;
|
|
214
|
-
/**
|
|
297
|
+
/**
|
|
298
|
+
* Upsert the given values; properties not named keep theirs.
|
|
299
|
+
*
|
|
300
|
+
* @effect write
|
|
301
|
+
*/
|
|
215
302
|
set(productId: string, values: SetPropertyValueInput[], options?: Call): Promise<void>;
|
|
216
303
|
};
|
|
217
304
|
}
|
|
218
|
-
export declare const PROPERTY_GROUP_FIELDS = "\n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n";
|
|
219
|
-
export declare const PROPERTY_GROUPS_QUERY = "\n query CatalogPropertyGroups {\n propertyGroups { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
220
|
-
export declare const PROPERTY_GROUP_QUERY = "\n query CatalogPropertyGroup($id: ID!) {\n propertyGroup(id: $id) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
221
|
-
export declare const CREATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogCreatePropertyGroup($input: CreatePropertyGroupInput!) {\n createPropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
222
|
-
export declare const UPDATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogUpdatePropertyGroup($input: UpdatePropertyGroupInput!) {\n updatePropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
305
|
+
export declare const PROPERTY_GROUP_FIELDS = "\n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n";
|
|
306
|
+
export declare const PROPERTY_GROUPS_QUERY = "\n query CatalogPropertyGroups {\n propertyGroups { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
307
|
+
export declare const PROPERTY_GROUP_QUERY = "\n query CatalogPropertyGroup($id: ID!) {\n propertyGroup(id: $id) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
308
|
+
export declare const CREATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogCreatePropertyGroup($input: CreatePropertyGroupInput!) {\n createPropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
309
|
+
export declare const UPDATE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogUpdatePropertyGroup($input: UpdatePropertyGroupInput!) {\n updatePropertyGroup(input: $input) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
223
310
|
export declare const DELETE_PROPERTY_GROUP_MUTATION = "\n mutation CatalogDeletePropertyGroup($id: ID!) {\n deletePropertyGroup(id: $id) { \n result\n message\n }\n }\n";
|
|
224
|
-
export declare const REORDER_PROPERTY_GROUPS_MUTATION = "\n mutation CatalogReorderPropertyGroups($ids: [ID!]!) {\n reorderPropertyGroups(ids: $ids) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
225
|
-
export declare const CREATE_PROPERTY_MUTATION = "\n mutation CatalogCreateProperty($input: CreatePropertyInput!) {\n createProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
|
|
226
|
-
export declare const UPDATE_PROPERTY_MUTATION = "\n mutation CatalogUpdateProperty($input: UpdatePropertyInput!) {\n updateProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
|
|
311
|
+
export declare const REORDER_PROPERTY_GROUPS_MUTATION = "\n mutation CatalogReorderPropertyGroups($ids: [ID!]!) {\n reorderPropertyGroups(ids: $ids) { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n }\n";
|
|
312
|
+
export declare const CREATE_PROPERTY_MUTATION = "\n mutation CatalogCreateProperty($input: CreatePropertyInput!) {\n createProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
|
|
313
|
+
export declare const UPDATE_PROPERTY_MUTATION = "\n mutation CatalogUpdateProperty($input: UpdatePropertyInput!) {\n updateProperty(input: $input) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
|
|
227
314
|
export declare const DELETE_PROPERTY_MUTATION = "\n mutation CatalogDeleteProperty($id: ID!) {\n deleteProperty(id: $id) { \n result\n message\n }\n }\n";
|
|
228
|
-
export declare const REORDER_PROPERTIES_MUTATION = "\n mutation CatalogReorderProperties($groupId: ID!, $propertyIds: [ID!]!) {\n reorderProperties(groupId: $groupId, propertyIds: $propertyIds) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
|
|
229
|
-
export declare const
|
|
315
|
+
export declare const REORDER_PROPERTIES_MUTATION = "\n mutation CatalogReorderProperties($groupId: ID!, $propertyIds: [ID!]!) {\n reorderProperties(groupId: $groupId, propertyIds: $propertyIds) { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n }\n";
|
|
316
|
+
export declare const CONFIGURE_FIELD_FACETS_MUTATION = "\n mutation CatalogConfigureFieldFacets($propertyId: ID!, $fields: [FieldFacetDefinitionInput!]!) {\n configureFieldFacets(propertyId: $propertyId, fields: $fields)\n }\n";
|
|
317
|
+
export declare const PRODUCT_PROPERTY_VALUES_QUERY = "\n query CatalogProductPropertyValues($productId: ID!) {\n productPropertyValues(productId: $productId) {\n group { \n id\n createdAt\n updatedAt\n position\n translations {\n id\n languageCode\n name\n }\n properties { \n id\n createdAt\n updatedAt\n slug\n fieldType\n isFilterable\n isRequired\n position\n config {\n options {\n value\n label\n }\n min\n max\n maxLength\n allowedCollectionIds\n filterableFieldSlugs\n productFilter\n }\n facetId\n fieldFacets {\n fieldSlug\n fieldType\n facetId\n }\n translations {\n id\n languageCode\n name\n helpText\n }\n }\n editableHere\n }\n values {\n id\n propertyId\n languageCode\n value\n }\n }\n }\n";
|
|
230
318
|
export declare const SET_PRODUCT_PROPERTY_VALUES_MUTATION = "\n mutation CatalogSetProductPropertyValues($productId: ID!, $values: [SetProductPropertyValueInput!]!) {\n setProductPropertyValues(productId: $productId, values: $values)\n }\n";
|
|
231
319
|
export declare const SYNC_PROPERTY_FACETS_MUTATION = "\n mutation CatalogSyncPropertyFacets($propertyId: ID!) {\n syncPropertyFacets(propertyId: $propertyId) {\n propertyId\n items\n unresolved\n fieldFacets\n }\n }\n";
|
|
232
320
|
/** `catalog.syncContentFacets`; its contract is on {@link CatalogModule.syncContentFacets}. */
|
|
@@ -22,6 +22,11 @@ const PROPERTY_FIELDS = /* GraphQL */ `
|
|
|
22
22
|
productFilter
|
|
23
23
|
}
|
|
24
24
|
facetId
|
|
25
|
+
fieldFacets {
|
|
26
|
+
fieldSlug
|
|
27
|
+
fieldType
|
|
28
|
+
facetId
|
|
29
|
+
}
|
|
25
30
|
translations {
|
|
26
31
|
id
|
|
27
32
|
languageCode
|
|
@@ -92,6 +97,11 @@ export const REORDER_PROPERTIES_MUTATION = /* GraphQL */ `
|
|
|
92
97
|
reorderProperties(groupId: $groupId, propertyIds: $propertyIds) { ${PROPERTY_FIELDS} }
|
|
93
98
|
}
|
|
94
99
|
`;
|
|
100
|
+
export const CONFIGURE_FIELD_FACETS_MUTATION = /* GraphQL */ `
|
|
101
|
+
mutation CatalogConfigureFieldFacets($propertyId: ID!, $fields: [FieldFacetDefinitionInput!]!) {
|
|
102
|
+
configureFieldFacets(propertyId: $propertyId, fields: $fields)
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
95
105
|
export const PRODUCT_PROPERTY_VALUES_QUERY = /* GraphQL */ `
|
|
96
106
|
query CatalogProductPropertyValues($productId: ID!) {
|
|
97
107
|
productPropertyValues(productId: $productId) {
|
|
@@ -148,6 +158,9 @@ export function createPropertiesModule(transport) {
|
|
|
148
158
|
update: (input, options) => call(UPDATE_PROPERTY_MUTATION, 'updateProperty', { input }, options),
|
|
149
159
|
delete: (id, options) => deleted(DELETE_PROPERTY_MUTATION, 'deleteProperty', 'PROPERTY_NOT_DELETED', id, options),
|
|
150
160
|
reorder: (groupId, propertyIds, options) => call(REORDER_PROPERTIES_MUTATION, 'reorderProperties', { groupId, propertyIds }, options),
|
|
161
|
+
async configureFieldFacets(propertyId, fields, options) {
|
|
162
|
+
await call(CONFIGURE_FIELD_FACETS_MUTATION, 'configureFieldFacets', { propertyId, fields }, options);
|
|
163
|
+
},
|
|
151
164
|
values: {
|
|
152
165
|
get: (productId, options) => call(PRODUCT_PROPERTY_VALUES_QUERY, 'productPropertyValues', { productId }, options),
|
|
153
166
|
async set(productId, values, options) {
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product recommendations (headless spec §9.2 `catalog`: "recommendations"; W10-13): the store's
|
|
3
|
+
* recommendation groups ("Customers also bought", "Complete the look"), what each one draws its
|
|
4
|
+
* products from, and the products hand-picked per product for a manual group.
|
|
5
|
+
*
|
|
6
|
+
* - **A `DYNAMIC` group's entries are its rules**, read in `position` order: one named product
|
|
7
|
+
* (`PRODUCT`), the products of a collection (`COLLECTION`) or with a facet value
|
|
8
|
+
* (`FACET_VALUE`), or the viewed product's own collections (`SAME_COLLECTION`) or properties
|
|
9
|
+
* (`SAME_PROPERTIES`). The storefront fills the group up to `maxItems` from the first entry on,
|
|
10
|
+
* never with the viewed product, a disabled product or one the store hides from the shopper.
|
|
11
|
+
* - **A `MANUAL` group has no entries**: its products are picked per viewed product
|
|
12
|
+
* (`manual.set`), and the storefront shows them in the order picked. A group's mode is chosen
|
|
13
|
+
* when it is created and never changes.
|
|
14
|
+
* - **A disabled group** (`enabled: false`) is kept but never shown on the storefront.
|
|
15
|
+
* - **Positions** are integers; a group or entry created without one goes after the last.
|
|
16
|
+
* `reorder` rewrites them as `0, 1, 2…` in the order given.
|
|
17
|
+
*
|
|
18
|
+
* Permissions: reads need `ReadCatalog`, writes `UpdateCatalog`. Every row is the store's own: a
|
|
19
|
+
* group or entry of another store is never answered and never changed (a read answers `null` or
|
|
20
|
+
* leaves it out; a write refuses it as unknown), and a rule only ever draws from the store's own
|
|
21
|
+
* products, collections and facet values.
|
|
22
|
+
*
|
|
23
|
+
* Refusals, GraphQL errors mapped by `core`. An unknown id and another store's get the same answer,
|
|
24
|
+
* so none tells whether another store holds the row:
|
|
25
|
+
* - `ENTITY_NOT_FOUND` → `NotFoundError` (404): a group or entry the store does not have, and the
|
|
26
|
+
* viewed product of `manual.set` when the store does not hold it.
|
|
27
|
+
* - `RECOMMENDATION_SOURCE_NOT_FOUND` → `NotFoundError` (404): a rule's `sourceId` that is not a
|
|
28
|
+
* product, collection or facet value of the store (`entries.create`, `entries.update`).
|
|
29
|
+
* - `RECOMMENDATION_TARGET_NOT_FOUND` → `NotFoundError` (404): `manual.set` picks the store does
|
|
30
|
+
* not hold, each named in the message and in `details` (`field` `/targetProductIds/<index>`).
|
|
31
|
+
* - `RECOMMENDATION_GROUP_SLUG_TAKEN` → `ConflictError` (409): another of the store's groups holds
|
|
32
|
+
* the slug, also when two writes race for one free slug.
|
|
33
|
+
* - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a rule that does not fit
|
|
34
|
+
* its group or its type (a `MANUAL` group, a `sourceId` missing or sent where none goes), picks
|
|
35
|
+
* in a `DYNAMIC` group.
|
|
36
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
|
|
37
|
+
*
|
|
38
|
+
* An id that is not a uuid (any id argument, a `sourceId` and each pick included) is refused
|
|
39
|
+
* before all of these with core's `ENTITY_NOT_FOUND` (`NotFoundError`, 404, no `details`), so
|
|
40
|
+
* branch on the class for not-found and on the code only to tell which row is missing.
|
|
41
|
+
*
|
|
42
|
+
* A platform release older than these codes answers each not-found and a taken slug as
|
|
43
|
+
* `USER_INPUT_ERROR` (400) in its own words ("Recommendation group not found"), a slug lost in a
|
|
44
|
+
* race as `DUPLICATE_RECORD`, and takes another store's collection or facet value as a rule's
|
|
45
|
+
* source.
|
|
46
|
+
*
|
|
47
|
+
* No release gate: every operation here is served by every platform release this SDK supports.
|
|
48
|
+
* No method deduplicates on `idempotencyKey`: repeating a `create` makes a second row.
|
|
49
|
+
*/
|
|
50
|
+
import type { Transport } from '@sixtyninecommerce/core';
|
|
51
|
+
import type { Call } from '../types.js';
|
|
52
|
+
/** What a `DYNAMIC` group's entry draws its products from. */
|
|
53
|
+
export type RecommendationSourceType = 'PRODUCT' | 'COLLECTION' | 'FACET_VALUE' | 'SAME_COLLECTION' | 'SAME_PROPERTIES';
|
|
54
|
+
/** `DYNAMIC`: rules (entries). `MANUAL`: products picked per viewed product (`manual.set`). */
|
|
55
|
+
export type RecommendationGroupMode = 'DYNAMIC' | 'MANUAL';
|
|
56
|
+
/** The source types whose entry names a row (`sourceId`); the others must name none. */
|
|
57
|
+
export declare const RECOMMENDATION_SOURCE_TYPES_WITH_ID: readonly RecommendationSourceType[];
|
|
58
|
+
/** One rule of a `DYNAMIC` group. */
|
|
59
|
+
export interface RecommendationEntry {
|
|
60
|
+
id: string;
|
|
61
|
+
groupId: string;
|
|
62
|
+
sourceType: RecommendationSourceType;
|
|
63
|
+
/**
|
|
64
|
+
* The product, collection or facet value for `PRODUCT`, `COLLECTION` and `FACET_VALUE`; `null`
|
|
65
|
+
* for `SAME_COLLECTION` and `SAME_PROPERTIES`. Always a row of this store when it was written:
|
|
66
|
+
* `entries.create` and `entries.update` refuse any other (`RECOMMENDATION_SOURCE_NOT_FOUND`). A
|
|
67
|
+
* row deleted or taken out of the store since adds nothing, and the storefront never draws from
|
|
68
|
+
* another store's collection or facet value, a rule stored before the refusal included. A deleted
|
|
69
|
+
* product's `PRODUCT` entries are removed with it.
|
|
70
|
+
*/
|
|
71
|
+
sourceId: string | null;
|
|
72
|
+
position: number;
|
|
73
|
+
/** ISO-8601 UTC. */
|
|
74
|
+
createdAt: string;
|
|
75
|
+
/** ISO-8601 UTC. */
|
|
76
|
+
updatedAt: string;
|
|
77
|
+
}
|
|
78
|
+
/** A group without its entries, as `manual.get` and `manual.set` answer it. */
|
|
79
|
+
export interface RecommendationGroupSummary {
|
|
80
|
+
id: string;
|
|
81
|
+
name: string;
|
|
82
|
+
/** Unique within the store; the storefront asks for one group by it. */
|
|
83
|
+
slug: string;
|
|
84
|
+
description: string | null;
|
|
85
|
+
/** The most products the storefront shows for this group. */
|
|
86
|
+
maxItems: number;
|
|
87
|
+
position: number;
|
|
88
|
+
enabled: boolean;
|
|
89
|
+
mode: RecommendationGroupMode;
|
|
90
|
+
/** ISO-8601 UTC. */
|
|
91
|
+
createdAt: string;
|
|
92
|
+
/** ISO-8601 UTC. */
|
|
93
|
+
updatedAt: string;
|
|
94
|
+
}
|
|
95
|
+
export interface RecommendationGroup extends RecommendationGroupSummary {
|
|
96
|
+
/** In `position` order (ties: the older first). Always empty on a `MANUAL` group. */
|
|
97
|
+
entries: RecommendationEntry[];
|
|
98
|
+
}
|
|
99
|
+
/** One `MANUAL` group and the products picked in it for one viewed product. */
|
|
100
|
+
export interface ProductManualRecommendations {
|
|
101
|
+
group: RecommendationGroupSummary;
|
|
102
|
+
/** In the order picked; empty when none are. */
|
|
103
|
+
productIds: string[];
|
|
104
|
+
}
|
|
105
|
+
export interface CreateRecommendationGroupInput {
|
|
106
|
+
name: string;
|
|
107
|
+
/** Defaults to the name, lower-cased, every run of other characters than `a-z0-9` a `-`. */
|
|
108
|
+
slug?: string;
|
|
109
|
+
description?: string;
|
|
110
|
+
/** Defaults to 10. The engine sets no bounds. */
|
|
111
|
+
maxItems?: number;
|
|
112
|
+
/** Defaults to after the store's last group. */
|
|
113
|
+
position?: number;
|
|
114
|
+
/** Defaults to true. */
|
|
115
|
+
enabled?: boolean;
|
|
116
|
+
/** Defaults to `DYNAMIC`; never changes afterwards. */
|
|
117
|
+
mode?: RecommendationGroupMode;
|
|
118
|
+
}
|
|
119
|
+
/** Only the fields named change; `description: null` clears it. */
|
|
120
|
+
export interface UpdateRecommendationGroupInput {
|
|
121
|
+
name?: string;
|
|
122
|
+
slug?: string;
|
|
123
|
+
description?: string | null;
|
|
124
|
+
maxItems?: number;
|
|
125
|
+
position?: number;
|
|
126
|
+
enabled?: boolean;
|
|
127
|
+
}
|
|
128
|
+
export interface CreateRecommendationEntryInput {
|
|
129
|
+
groupId: string;
|
|
130
|
+
sourceType: RecommendationSourceType;
|
|
131
|
+
/** Required for `PRODUCT`, `COLLECTION` and `FACET_VALUE`; refused for the other two. */
|
|
132
|
+
sourceId?: string;
|
|
133
|
+
/** Defaults to after the group's last entry. */
|
|
134
|
+
position?: number;
|
|
135
|
+
}
|
|
136
|
+
/** Only the fields named change; `sourceId: null` clears it (for a `SAME_*` source type). */
|
|
137
|
+
export interface UpdateRecommendationEntryInput {
|
|
138
|
+
sourceType?: RecommendationSourceType;
|
|
139
|
+
sourceId?: string | null;
|
|
140
|
+
position?: number;
|
|
141
|
+
}
|
|
142
|
+
export interface RecommendationsModule {
|
|
143
|
+
groups: {
|
|
144
|
+
/**
|
|
145
|
+
* Every group of the store, in `position` order (ties: the older first), each with its
|
|
146
|
+
* entries. The engine answers them all at once; there are no pages.
|
|
147
|
+
*
|
|
148
|
+
* @effect read
|
|
149
|
+
*/
|
|
150
|
+
list(options?: Call): Promise<RecommendationGroup[]>;
|
|
151
|
+
/**
|
|
152
|
+
* One group with its entries; `null` when the store has no group with that id.
|
|
153
|
+
*
|
|
154
|
+
* @effect read
|
|
155
|
+
*/
|
|
156
|
+
get(id: string, options?: Call): Promise<RecommendationGroup | null>;
|
|
157
|
+
/**
|
|
158
|
+
* Refused (`RECOMMENDATION_GROUP_SLUG_TAKEN`) when the store already has a group with that
|
|
159
|
+
* slug, the defaulted one included.
|
|
160
|
+
*
|
|
161
|
+
* @effect write
|
|
162
|
+
*/
|
|
163
|
+
create(input: CreateRecommendationGroupInput, options?: Call): Promise<RecommendationGroup>;
|
|
164
|
+
/**
|
|
165
|
+
* Refused: a group the store does not have (`ENTITY_NOT_FOUND`), a slug another of its groups
|
|
166
|
+
* holds (`RECOMMENDATION_GROUP_SLUG_TAKEN`).
|
|
167
|
+
*
|
|
168
|
+
* @effect write
|
|
169
|
+
*/
|
|
170
|
+
update(id: string, input: UpdateRecommendationGroupInput, options?: Call): Promise<RecommendationGroup>;
|
|
171
|
+
/**
|
|
172
|
+
* Deletes the group, its entries and every product's picks in it. Refused
|
|
173
|
+
* (`ENTITY_NOT_FOUND`) for a group the store does not have.
|
|
174
|
+
*
|
|
175
|
+
* @effect destructive
|
|
176
|
+
*/
|
|
177
|
+
delete(id: string, options?: Call): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Gives the named groups the positions `0, 1, 2…` in the order given, and answers every group
|
|
180
|
+
* as `list` does. An id the store does not hold is skipped without a word; a group left out
|
|
181
|
+
* keeps its position, which may then equal a named one's: name them all.
|
|
182
|
+
*
|
|
183
|
+
* @effect write
|
|
184
|
+
*/
|
|
185
|
+
reorder(ids: string[], options?: Call): Promise<RecommendationGroup[]>;
|
|
186
|
+
};
|
|
187
|
+
/** The rules of `DYNAMIC` groups. */
|
|
188
|
+
entries: {
|
|
189
|
+
/**
|
|
190
|
+
* Refused: a group the store does not have (`ENTITY_NOT_FOUND`); a `sourceId` that is not a
|
|
191
|
+
* product, collection or facet value of the store (`RECOMMENDATION_SOURCE_NOT_FOUND`: another
|
|
192
|
+
* store's is refused like an unknown id); a `MANUAL` group, or a `sourceId` missing for
|
|
193
|
+
* `PRODUCT` / `COLLECTION` / `FACET_VALUE` or sent for `SAME_COLLECTION` / `SAME_PROPERTIES`
|
|
194
|
+
* (`USER_INPUT_ERROR`).
|
|
195
|
+
*
|
|
196
|
+
* @effect write
|
|
197
|
+
*/
|
|
198
|
+
create(input: CreateRecommendationEntryInput, options?: Call): Promise<RecommendationEntry>;
|
|
199
|
+
/**
|
|
200
|
+
* Refused: an entry the store does not have (`ENTITY_NOT_FOUND`); a source type and
|
|
201
|
+
* `sourceId` that do not go together once the change is applied (`USER_INPUT_ERROR`); a
|
|
202
|
+
* change of either that leaves the rule naming no row of the store
|
|
203
|
+
* (`RECOMMENDATION_SOURCE_NOT_FOUND`). A change of `position` alone is not judged.
|
|
204
|
+
*
|
|
205
|
+
* @effect write
|
|
206
|
+
*/
|
|
207
|
+
update(id: string, input: UpdateRecommendationEntryInput, options?: Call): Promise<RecommendationEntry>;
|
|
208
|
+
/**
|
|
209
|
+
* Refused (`ENTITY_NOT_FOUND`) for an entry the store does not have.
|
|
210
|
+
*
|
|
211
|
+
* @effect destructive
|
|
212
|
+
*/
|
|
213
|
+
delete(id: string, options?: Call): Promise<void>;
|
|
214
|
+
/**
|
|
215
|
+
* Gives the group's named entries the positions `0, 1, 2…` in the order given, and answers
|
|
216
|
+
* the group's entries in `position` order. An id that is not one of THIS group's entries is
|
|
217
|
+
* skipped without a word (another group's entry never moves); an entry left out keeps its
|
|
218
|
+
* position. Refused (`ENTITY_NOT_FOUND`) for a group the store does not have.
|
|
219
|
+
*
|
|
220
|
+
* @effect write
|
|
221
|
+
*/
|
|
222
|
+
reorder(groupId: string, ids: string[], options?: Call): Promise<RecommendationEntry[]>;
|
|
223
|
+
};
|
|
224
|
+
/** The products picked per viewed product in `MANUAL` groups (the product page's card). */
|
|
225
|
+
manual: {
|
|
226
|
+
/**
|
|
227
|
+
* Every `MANUAL` group of the store, in `position` order, each with the products picked for
|
|
228
|
+
* `productId`. A product with no picks (or none of this store) answers every group with an
|
|
229
|
+
* empty `productIds`; a store with no manual group answers `[]`.
|
|
230
|
+
*
|
|
231
|
+
* @effect read
|
|
232
|
+
*/
|
|
233
|
+
get(productId: string, options?: Call): Promise<ProductManualRecommendations[]>;
|
|
234
|
+
/**
|
|
235
|
+
* Replaces the products picked for `productId` in one `MANUAL` group, in the order given:
|
|
236
|
+
* `[]` clears them. The product itself and repeats are dropped (the first one kept). One
|
|
237
|
+
* write: refused, nothing changes.
|
|
238
|
+
*
|
|
239
|
+
* Refused: a group the store does not have, or `productId` not a product of this store
|
|
240
|
+
* (`ENTITY_NOT_FOUND`); a picked product that is not (`RECOMMENDATION_TARGET_NOT_FOUND`,
|
|
241
|
+
* naming each, `details` pointing at where it was sent); a `DYNAMIC` group
|
|
242
|
+
* (`USER_INPUT_ERROR`).
|
|
243
|
+
*
|
|
244
|
+
* @effect write
|
|
245
|
+
*/
|
|
246
|
+
set(productId: string, groupId: string, productIds: string[], options?: Call): Promise<RecommendationGroupSummary>;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export declare const RECOMMENDATION_GROUPS_QUERY = "\n query CatalogRecommendationGroups {\n recommendationGroups { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
|
|
250
|
+
export declare const RECOMMENDATION_GROUP_QUERY = "\n query CatalogRecommendationGroup($id: ID!) {\n recommendationGroup(id: $id) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
|
|
251
|
+
export declare const CREATE_RECOMMENDATION_GROUP_MUTATION = "\n mutation CatalogCreateRecommendationGroup($input: CreateRecommendationGroupInput!) {\n createRecommendationGroup(input: $input) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
|
|
252
|
+
export declare const UPDATE_RECOMMENDATION_GROUP_MUTATION = "\n mutation CatalogUpdateRecommendationGroup($id: ID!, $input: UpdateRecommendationGroupInput!) {\n updateRecommendationGroup(id: $id, input: $input) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
|
|
253
|
+
export declare const DELETE_RECOMMENDATION_GROUP_MUTATION = "\n mutation CatalogDeleteRecommendationGroup($id: ID!) {\n deleteRecommendationGroup(id: $id) { \n result\n message\n }\n }\n";
|
|
254
|
+
export declare const REORDER_RECOMMENDATION_GROUPS_MUTATION = "\n mutation CatalogReorderRecommendationGroups($ids: [ID!]!) {\n reorderRecommendationGroups(ids: $ids) { \n \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n\n entries { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n }\n";
|
|
255
|
+
export declare const CREATE_RECOMMENDATION_ENTRY_MUTATION = "\n mutation CatalogCreateRecommendationEntry($input: CreateRecommendationEntryInput!) {\n createRecommendationEntry(input: $input) { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n";
|
|
256
|
+
export declare const UPDATE_RECOMMENDATION_ENTRY_MUTATION = "\n mutation CatalogUpdateRecommendationEntry($id: ID!, $input: UpdateRecommendationEntryInput!) {\n updateRecommendationEntry(id: $id, input: $input) { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n";
|
|
257
|
+
export declare const DELETE_RECOMMENDATION_ENTRY_MUTATION = "\n mutation CatalogDeleteRecommendationEntry($id: ID!) {\n deleteRecommendationEntry(id: $id) { \n result\n message\n }\n }\n";
|
|
258
|
+
export declare const REORDER_RECOMMENDATION_ENTRIES_MUTATION = "\n mutation CatalogReorderRecommendationEntries($groupId: ID!, $ids: [ID!]!) {\n reorderRecommendationEntries(groupId: $groupId, ids: $ids) { \n id\n groupId\n sourceType\n sourceId\n position\n createdAt\n updatedAt\n }\n }\n";
|
|
259
|
+
export declare const PRODUCT_MANUAL_RECOMMENDATIONS_QUERY = "\n query CatalogProductManualRecommendations($productId: ID!) {\n productManualRecommendations(productId: $productId) {\n group { \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n }\n productIds\n }\n }\n";
|
|
260
|
+
export declare const SET_PRODUCT_MANUAL_RECOMMENDATIONS_MUTATION = "\n mutation CatalogSetProductManualRecommendations(\n $productId: ID!\n $groupId: ID!\n $targetProductIds: [ID!]!\n ) {\n setProductManualRecommendations(\n productId: $productId\n groupId: $groupId\n targetProductIds: $targetProductIds\n ) { \n id\n name\n slug\n description\n maxItems\n position\n enabled\n mode\n createdAt\n updatedAt\n }\n }\n";
|
|
261
|
+
export declare function createRecommendationsModule(transport: Transport): RecommendationsModule;
|