@sixtyninecommerce/admin 0.10.0 → 0.12.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 +5772 -118
- package/dist/auth/index.d.ts +7 -1
- package/dist/auth/index.js +8 -1
- 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/documents.d.ts +142 -0
- package/dist/catalog/documents.js +114 -0
- package/dist/catalog/index.d.ts +54 -6
- package/dist/catalog/index.js +10 -1
- package/dist/catalog/metadata.d.ts +244 -0
- package/dist/catalog/metadata.js +220 -0
- package/dist/catalog/products.d.ts +159 -19
- package/dist/catalog/products.js +88 -13
- package/dist/catalog/properties.d.ts +140 -14
- package/dist/catalog/properties.js +65 -1
- package/dist/catalog/recommendations.d.ts +288 -0
- package/dist/catalog/recommendations.js +237 -0
- package/dist/catalog/reviews.d.ts +381 -0
- package/dist/catalog/reviews.js +325 -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 +293 -26
- package/dist/catalog/variants.js +164 -7
- package/dist/catalog/visibility.d.ts +38 -7
- package/dist/catalog/visibility.js +19 -1
- package/dist/client.d.ts +121 -9
- package/dist/client.js +53 -14
- package/dist/content/assets.d.ts +285 -0
- package/dist/content/assets.js +181 -0
- package/dist/content/forms.d.ts +332 -1
- package/dist/content/forms.js +140 -4
- package/dist/content/items.d.ts +292 -0
- package/dist/content/items.js +170 -0
- package/dist/content/menus.d.ts +254 -0
- package/dist/content/menus.js +70 -0
- package/dist/content/strings.d.ts +137 -0
- package/dist/content/strings.js +82 -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/delivery-terms.d.ts +44 -0
- package/dist/customers/delivery-terms.js +32 -0
- package/dist/customers/groups.d.ts +207 -0
- package/dist/customers/groups.js +438 -0
- package/dist/customers/index.d.ts +462 -37
- package/dist/customers/index.js +317 -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/emails.d.ts +150 -0
- package/dist/documents/emails.js +220 -0
- package/dist/documents/index.d.ts +744 -21
- package/dist/documents/index.js +438 -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 +163 -11
- package/dist/fulfillments/index.js +135 -6
- 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 +147 -39
- package/dist/index.js +51 -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 +965 -0
- package/dist/integrations/exact-online-types.js +6 -0
- package/dist/integrations/exact-online.d.ts +784 -0
- package/dist/integrations/exact-online.js +1089 -0
- package/dist/integrations/index.d.ts +44 -3
- package/dist/integrations/index.js +23 -3
- package/dist/integrations/pipeline-discoveries.d.ts +65 -0
- package/dist/integrations/pipeline-discoveries.js +65 -0
- package/dist/integrations/pipeline-keys.d.ts +13 -0
- package/dist/integrations/pipeline-keys.js +22 -0
- package/dist/integrations/pipeline-runs.d.ts +119 -0
- package/dist/integrations/pipeline-runs.js +87 -0
- package/dist/integrations/pipelines.d.ts +87 -15
- package/dist/integrations/pipelines.js +41 -22
- package/dist/integrations/price-import.d.ts +223 -0
- package/dist/integrations/price-import.js +124 -0
- package/dist/integrations/types.d.ts +367 -3
- package/dist/integrations/types.js +4 -3
- package/dist/locations/index.d.ts +66 -0
- package/dist/locations/index.js +31 -0
- package/dist/meta/index.d.ts +172 -0
- package/dist/metafields/index.d.ts +8 -1
- package/dist/metafields/index.js +10 -0
- package/dist/orders/cancel-with-money.d.ts +237 -0
- package/dist/orders/cancel-with-money.js +155 -0
- package/dist/orders/commit-edit.d.ts +23 -1
- package/dist/orders/commit-edit.js +19 -2
- package/dist/orders/deposit-policy.d.ts +3 -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 +66 -15
- package/dist/orders/index.js +25 -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 +243 -0
- package/dist/orders/list.js +487 -0
- package/dist/orders/manual-discounts.d.ts +13 -5
- package/dist/orders/manual-pricing.d.ts +5 -0
- package/dist/orders/money-view.d.ts +149 -0
- package/dist/orders/money-view.js +99 -0
- package/dist/orders/order-entry.d.ts +20 -7
- package/dist/orders/plan-edit.d.ts +34 -2
- package/dist/orders/plan-edit.js +112 -11
- 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 +20 -1
- package/dist/orders/surcharges.js +37 -11
- package/dist/payments/account-credit.d.ts +46 -0
- package/dist/payments/account-credit.js +42 -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 +19 -0
- package/dist/payments/credit-noted-fields.js +69 -0
- package/dist/payments/customer-consent.d.ts +30 -0
- package/dist/payments/customer-consent.js +49 -0
- package/dist/payments/index.d.ts +340 -19
- package/dist/payments/index.js +151 -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 +147 -0
- package/dist/payments/requests.js +93 -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 +106 -0
- package/dist/pickups/index.js +108 -0
- package/dist/platform/channels.d.ts +132 -0
- package/dist/platform/channels.js +128 -0
- package/dist/platform/email-gateways.d.ts +165 -0
- package/dist/platform/email-gateways.js +78 -0
- package/dist/platform/geo.d.ts +123 -0
- package/dist/platform/geo.js +144 -0
- package/dist/platform/tax.d.ts +162 -0
- package/dist/platform/tax.js +169 -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 +108 -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 +2370 -58
- package/dist/pos/index.js +854 -7
- package/dist/pos/no-receipt.d.ts +94 -0
- package/dist/pos/no-receipt.js +25 -0
- package/dist/pos/offline-sales.d.ts +228 -0
- package/dist/pos/offline-sales.js +170 -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 +244 -0
- package/dist/pos/returns.js +83 -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 +606 -0
- package/dist/pricing/price-lists.js +600 -0
- package/dist/purchasing/alerts.d.ts +104 -0
- package/dist/purchasing/alerts.js +88 -0
- package/dist/purchasing/backorders.d.ts +61 -0
- package/dist/purchasing/backorders.js +92 -0
- package/dist/purchasing/cost.d.ts +110 -0
- package/dist/purchasing/cost.js +86 -0
- package/dist/purchasing/index.d.ts +54 -0
- package/dist/purchasing/index.js +24 -0
- package/dist/purchasing/links.d.ts +263 -0
- package/dist/purchasing/links.js +192 -0
- package/dist/purchasing/purchase-orders.d.ts +551 -0
- package/dist/purchasing/purchase-orders.js +491 -0
- package/dist/purchasing/receipts.d.ts +396 -0
- package/dist/purchasing/receipts.js +287 -0
- package/dist/purchasing/replenishment.d.ts +281 -0
- package/dist/purchasing/replenishment.js +240 -0
- package/dist/purchasing/returns.d.ts +273 -0
- package/dist/purchasing/returns.js +229 -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 +518 -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 +675 -21
- package/dist/returns/index.js +756 -11
- package/dist/search/index.d.ts +76 -1
- package/dist/search/index.js +63 -0
- package/dist/seo/config.d.ts +128 -0
- package/dist/seo/config.js +95 -0
- package/dist/seo/redirects.d.ts +125 -0
- package/dist/seo/redirects.js +60 -1
- package/dist/seo/settings.d.ts +147 -0
- package/dist/seo/settings.js +209 -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 +54 -0
- package/dist/shipping/index.js +27 -0
- package/dist/shipping/methods.d.ts +205 -0
- package/dist/shipping/methods.js +240 -0
- package/dist/shipping/myparcel.d.ts +668 -0
- package/dist/shipping/myparcel.js +466 -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 +324 -0
- package/dist/shipping/rate-tables.js +390 -0
- package/dist/shipping/ship-to-countries.d.ts +75 -0
- package/dist/shipping/ship-to-countries.js +47 -0
- package/dist/stock/by-location.d.ts +86 -0
- package/dist/stock/by-location.js +94 -0
- package/dist/stock/index.d.ts +18 -4
- package/dist/stock/index.js +7 -0
- package/dist/stock/locations.d.ts +144 -0
- package/dist/stock/locations.js +168 -0
- package/dist/stock/settings.d.ts +106 -0
- package/dist/stock/settings.js +103 -0
- package/dist/stock/transfers.d.ts +386 -52
- package/dist/stock/transfers.js +222 -17
- package/dist/stored-value/index.d.ts +312 -11
- package/dist/stored-value/index.js +223 -4
- package/dist/subscriptions/index.d.ts +682 -0
- package/dist/subscriptions/index.js +815 -0
- package/dist/tax/display-policy.d.ts +105 -0
- package/dist/tax/display-policy.js +84 -0
- package/dist/tax/index.d.ts +79 -1
- package/dist/tax/index.js +52 -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 +287 -0
- package/dist/tenant/email-templates.js +271 -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
|
@@ -1,4 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pageFromVendureList, ValidationError, } from '@sixtyninecommerce/core';
|
|
2
|
+
import { createSearchModule } from '../search/index.js';
|
|
3
|
+
import { ASSET_REF_FIELDS, assertDeleted, CATALOGUE_OWNERSHIP_FIELDS, DEFAULT_PAGE_SIZE, DELETION_FIELDS, fieldOf, toAssetRef, vendureListPage, } from './types.js';
|
|
4
|
+
/** The most collections one {@link CollectionsModule.search} answers. */
|
|
5
|
+
export const MAX_COLLECTION_SEARCH_TAKE = 100;
|
|
6
|
+
const SEO_FIELDS = /* GraphQL */ `
|
|
7
|
+
customFields {
|
|
8
|
+
isRoutable
|
|
9
|
+
isCanonical
|
|
10
|
+
noindex
|
|
11
|
+
nofollow
|
|
12
|
+
metaRobotsMaxImagePreview
|
|
13
|
+
socialImageId
|
|
14
|
+
socialOgType
|
|
15
|
+
twitterCardType
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
const TRANSLATION_SEO_FIELDS = /* GraphQL */ `
|
|
19
|
+
customFields {
|
|
20
|
+
seoTitle
|
|
21
|
+
seoDescription
|
|
22
|
+
canonicalUrl
|
|
23
|
+
socialTitle
|
|
24
|
+
socialDescription
|
|
25
|
+
socialImageAlt
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
2
28
|
export const COLLECTION_FIELDS = /* GraphQL */ `
|
|
3
29
|
id
|
|
4
30
|
${CATALOGUE_OWNERSHIP_FIELDS}
|
|
@@ -26,12 +52,14 @@ export const COLLECTION_FIELDS = /* GraphQL */ `
|
|
|
26
52
|
slug
|
|
27
53
|
}
|
|
28
54
|
productVariantCount
|
|
55
|
+
${SEO_FIELDS}
|
|
29
56
|
translations {
|
|
30
57
|
id
|
|
31
58
|
languageCode
|
|
32
59
|
name
|
|
33
60
|
slug
|
|
34
61
|
description
|
|
62
|
+
${TRANSLATION_SEO_FIELDS}
|
|
35
63
|
}
|
|
36
64
|
`;
|
|
37
65
|
export const COLLECTIONS_QUERY = /* GraphQL */ `
|
|
@@ -44,7 +72,19 @@ export const COLLECTIONS_QUERY = /* GraphQL */ `
|
|
|
44
72
|
`;
|
|
45
73
|
export const COLLECTION_QUERY = /* GraphQL */ `
|
|
46
74
|
query CatalogCollection($id: ID!) {
|
|
47
|
-
collection(id: $id) {
|
|
75
|
+
collection(id: $id) {
|
|
76
|
+
${COLLECTION_FIELDS}
|
|
77
|
+
parent {
|
|
78
|
+
id
|
|
79
|
+
name
|
|
80
|
+
}
|
|
81
|
+
children {
|
|
82
|
+
id
|
|
83
|
+
name
|
|
84
|
+
slug
|
|
85
|
+
position
|
|
86
|
+
}
|
|
87
|
+
}
|
|
48
88
|
}
|
|
49
89
|
`;
|
|
50
90
|
export const CREATE_COLLECTION_MUTATION = /* GraphQL */ `
|
|
@@ -67,25 +107,216 @@ export const MOVE_COLLECTION_MUTATION = /* GraphQL */ `
|
|
|
67
107
|
moveCollection(input: $input) { ${COLLECTION_FIELDS} }
|
|
68
108
|
}
|
|
69
109
|
`;
|
|
110
|
+
export const COLLECTION_FILTER_DEFINITIONS_QUERY = /* GraphQL */ `
|
|
111
|
+
query CatalogCollectionFilterDefinitions {
|
|
112
|
+
collectionFilters {
|
|
113
|
+
code
|
|
114
|
+
description
|
|
115
|
+
args {
|
|
116
|
+
name
|
|
117
|
+
type
|
|
118
|
+
list
|
|
119
|
+
required
|
|
120
|
+
defaultValue
|
|
121
|
+
label
|
|
122
|
+
description
|
|
123
|
+
ui
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
/**
|
|
129
|
+
* The preview's price is `storePrice` (null for a variant the store has no price for), never
|
|
130
|
+
* core's `price` / `priceWithTax` / `currencyCode`, which raise "No price information was
|
|
131
|
+
* found…" for such a variant and, being non-null, empty the whole preview (#1504).
|
|
132
|
+
*/
|
|
133
|
+
export const PREVIEW_COLLECTION_VARIANTS_QUERY = /* GraphQL */ `
|
|
134
|
+
query CatalogPreviewCollectionVariants(
|
|
135
|
+
$input: PreviewCollectionVariantsInput!
|
|
136
|
+
$options: ProductVariantListOptions
|
|
137
|
+
) {
|
|
138
|
+
previewCollectionVariants(input: $input, options: $options) {
|
|
139
|
+
items {
|
|
140
|
+
id
|
|
141
|
+
name
|
|
142
|
+
sku
|
|
143
|
+
storePrice {
|
|
144
|
+
price
|
|
145
|
+
priceWithTax
|
|
146
|
+
currencyCode
|
|
147
|
+
}
|
|
148
|
+
product {
|
|
149
|
+
id
|
|
150
|
+
name
|
|
151
|
+
featuredAsset { ${ASSET_REF_FIELDS} }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
totalItems
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
const COLLECTION_SORT_FIELDS = [
|
|
159
|
+
'position',
|
|
160
|
+
'name',
|
|
161
|
+
'createdAt',
|
|
162
|
+
'updatedAt',
|
|
163
|
+
'id',
|
|
164
|
+
];
|
|
165
|
+
function toPreviewVariant(wire) {
|
|
166
|
+
const { featuredAsset, ...product } = wire.product;
|
|
167
|
+
return {
|
|
168
|
+
...wire,
|
|
169
|
+
product: { ...product, featuredAsset: featuredAsset ? toAssetRef(featuredAsset) : null },
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function toSeo(wire) {
|
|
173
|
+
return {
|
|
174
|
+
isRoutable: wire?.isRoutable ?? null,
|
|
175
|
+
isCanonical: wire?.isCanonical ?? null,
|
|
176
|
+
noindex: wire?.noindex ?? null,
|
|
177
|
+
nofollow: wire?.nofollow ?? null,
|
|
178
|
+
metaRobotsMaxImagePreview: wire?.metaRobotsMaxImagePreview ?? null,
|
|
179
|
+
socialImageId: wire?.socialImageId ?? null,
|
|
180
|
+
socialOgType: wire?.socialOgType ?? null,
|
|
181
|
+
twitterCardType: wire?.twitterCardType ?? null,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function toTranslationSeo(wire) {
|
|
185
|
+
return {
|
|
186
|
+
seoTitle: wire?.seoTitle ?? null,
|
|
187
|
+
seoDescription: wire?.seoDescription ?? null,
|
|
188
|
+
canonicalUrl: wire?.canonicalUrl ?? null,
|
|
189
|
+
socialTitle: wire?.socialTitle ?? null,
|
|
190
|
+
socialDescription: wire?.socialDescription ?? null,
|
|
191
|
+
socialImageAlt: wire?.socialImageAlt ?? null,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function toCollection(wire) {
|
|
195
|
+
const { customFields, translations, featuredAsset, assets, ...rest } = wire;
|
|
196
|
+
return {
|
|
197
|
+
...rest,
|
|
198
|
+
featuredAsset: featuredAsset ? toAssetRef(featuredAsset) : null,
|
|
199
|
+
assets: (assets ?? []).map(toAssetRef),
|
|
200
|
+
seo: toSeo(customFields),
|
|
201
|
+
translations: (translations ?? []).map(({ customFields: seo, ...translation }) => ({
|
|
202
|
+
...translation,
|
|
203
|
+
seo: toTranslationSeo(seo),
|
|
204
|
+
})),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** `seo` goes on the wire as the engine's `customFields`, only the keys the caller named. */
|
|
208
|
+
function toWireTranslation(translation) {
|
|
209
|
+
const { seo, ...rest } = translation;
|
|
210
|
+
return seo === undefined ? rest : { ...rest, customFields: { ...seo } };
|
|
211
|
+
}
|
|
212
|
+
function toWireInput(input) {
|
|
213
|
+
const { seo, translations, ...rest } = input;
|
|
214
|
+
return {
|
|
215
|
+
...rest,
|
|
216
|
+
...(translations === undefined ? {} : { translations: translations.map(toWireTranslation) }),
|
|
217
|
+
...(seo === undefined ? {} : { customFields: { ...seo } }),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function invalidSort(sort) {
|
|
221
|
+
return new ValidationError({
|
|
222
|
+
code: 'INVALID_COLLECTION_SORT',
|
|
223
|
+
message: `sort must name one of ${COLLECTION_SORT_FIELDS.join(', ')} and the direction ASC or DESC; got ${JSON.stringify(sort)}. Nothing was sent.`,
|
|
224
|
+
status: 400,
|
|
225
|
+
service: 'vendure',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function listOptionsOf(options) {
|
|
229
|
+
const filter = {};
|
|
230
|
+
if (options?.parentId !== undefined)
|
|
231
|
+
filter.parentId = { eq: options.parentId };
|
|
232
|
+
if (options?.name !== undefined)
|
|
233
|
+
filter.name = { contains: options.name };
|
|
234
|
+
if (options?.ids !== undefined)
|
|
235
|
+
filter.id = { in: [...options.ids] };
|
|
236
|
+
const sort = options?.sort;
|
|
237
|
+
if (sort !== undefined &&
|
|
238
|
+
(!COLLECTION_SORT_FIELDS.includes(sort.field) ||
|
|
239
|
+
(sort.direction !== 'ASC' && sort.direction !== 'DESC'))) {
|
|
240
|
+
throw invalidSort(sort);
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
...(options?.topLevelOnly === true ? { topLevelOnly: true } : {}),
|
|
244
|
+
...(Object.keys(filter).length > 0 ? { filter } : {}),
|
|
245
|
+
...(sort === undefined ? {} : { sort: { [sort.field]: sort.direction } }),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function invalidSearch(message) {
|
|
249
|
+
return new ValidationError({
|
|
250
|
+
code: 'SEARCH_INPUT_INVALID',
|
|
251
|
+
message: `${message} Nothing was sent.`,
|
|
252
|
+
status: 400,
|
|
253
|
+
service: 'vendure',
|
|
254
|
+
});
|
|
255
|
+
}
|
|
70
256
|
export function createCollectionsModule(transport) {
|
|
257
|
+
const write = async (document, field, variables, options) => toCollection(await fieldOf(transport, document, field, variables, options));
|
|
258
|
+
const list = async (options) => {
|
|
259
|
+
const listOptions = listOptionsOf(options);
|
|
260
|
+
if (options?.ids !== undefined && options.ids.length === 0) {
|
|
261
|
+
return pageFromVendureList({ items: [], totalItems: 0 }, 0, options.limit ?? DEFAULT_PAGE_SIZE, () => Promise.reject(new Error('an empty page has no next page')));
|
|
262
|
+
}
|
|
263
|
+
return vendureListPage(transport, COLLECTIONS_QUERY, 'collections', (skip, take) => ({ options: { skip, take, ...listOptions } }), options, toCollection);
|
|
264
|
+
};
|
|
71
265
|
return {
|
|
72
|
-
list
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (options?.name !== undefined)
|
|
77
|
-
filter.name = { contains: options.name };
|
|
78
|
-
return vendureListPage(transport, COLLECTIONS_QUERY, 'collections', (skip, take) => ({
|
|
79
|
-
options: { skip, take, ...(Object.keys(filter).length > 0 ? { filter } : {}) },
|
|
80
|
-
}), options);
|
|
266
|
+
list,
|
|
267
|
+
async get(id, options) {
|
|
268
|
+
const collection = await fieldOf(transport, COLLECTION_QUERY, 'collection', { id }, options);
|
|
269
|
+
return collection ? toCollection(collection) : null;
|
|
81
270
|
},
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
271
|
+
async search(term, options = {}) {
|
|
272
|
+
const { take = DEFAULT_PAGE_SIZE, skip = 0, ...call } = options;
|
|
273
|
+
if (!Number.isInteger(take) || take < 1 || take > MAX_COLLECTION_SEARCH_TAKE) {
|
|
274
|
+
throw invalidSearch(`take must be a whole number from 1 to ${String(MAX_COLLECTION_SEARCH_TAKE)}; got ${String(take)}.`);
|
|
275
|
+
}
|
|
276
|
+
if (!Number.isInteger(skip) || skip < 0) {
|
|
277
|
+
throw invalidSearch(`skip must be a whole number from 0; got ${String(skip)}.`);
|
|
278
|
+
}
|
|
279
|
+
const hits = await createSearchModule(transport).index('collections', { term, take, skip }, call);
|
|
280
|
+
const ids = [...new Set(hits.items.map((hit) => hit.id))];
|
|
281
|
+
if (ids.length === 0)
|
|
282
|
+
return { items: [], total: hits.total };
|
|
283
|
+
// The index says which and in what order; the rows are the engine's, read in the store the
|
|
284
|
+
// call acts on, so a collection the store no longer holds is not answered.
|
|
285
|
+
const page = await list({ ...call, ids, limit: ids.length });
|
|
286
|
+
const byId = new Map(page.items.map((collection) => [collection.id, collection]));
|
|
287
|
+
return {
|
|
288
|
+
items: ids.flatMap((id) => {
|
|
289
|
+
const collection = byId.get(id);
|
|
290
|
+
return collection ? [collection] : [];
|
|
291
|
+
}),
|
|
292
|
+
total: hits.total,
|
|
293
|
+
};
|
|
294
|
+
},
|
|
295
|
+
create: (input, options) => write(CREATE_COLLECTION_MUTATION, 'createCollection', { input: toWireInput(input) }, options),
|
|
296
|
+
update: (input, options) => write(UPDATE_COLLECTION_MUTATION, 'updateCollection', { input: toWireInput(input) }, options),
|
|
85
297
|
async delete(id, options) {
|
|
86
298
|
const response = await fieldOf(transport, DELETE_COLLECTION_MUTATION, 'deleteCollection', { id }, options);
|
|
87
299
|
assertDeleted('COLLECTION_NOT_DELETED', [response]);
|
|
88
300
|
},
|
|
89
|
-
move: (collectionId, parentId, index, options) =>
|
|
301
|
+
move: (collectionId, parentId, index, options) => write(MOVE_COLLECTION_MUTATION, 'moveCollection', { input: { collectionId, parentId, index } }, options),
|
|
302
|
+
filterDefinitions: (options) => fieldOf(transport, COLLECTION_FILTER_DEFINITIONS_QUERY, 'collectionFilters', {}, options),
|
|
303
|
+
previewVariants(input, options = {}) {
|
|
304
|
+
const { term: rawTerm, ...listOptions } = options;
|
|
305
|
+
const term = rawTerm?.trim() ?? '';
|
|
306
|
+
const filter = term === ''
|
|
307
|
+
? {}
|
|
308
|
+
: {
|
|
309
|
+
filter: { name: { contains: term }, sku: { contains: term } },
|
|
310
|
+
filterOperator: 'OR',
|
|
311
|
+
};
|
|
312
|
+
return vendureListPage(transport, PREVIEW_COLLECTION_VARIANTS_QUERY, 'previewCollectionVariants', (skip, take) => ({
|
|
313
|
+
input: {
|
|
314
|
+
filters: input.filters,
|
|
315
|
+
inheritFilters: input.inheritFilters,
|
|
316
|
+
...(input.parentId === undefined ? {} : { parentId: input.parentId }),
|
|
317
|
+
},
|
|
318
|
+
options: { skip, take, ...filter },
|
|
319
|
+
}), listOptions, toPreviewVariant);
|
|
320
|
+
},
|
|
90
321
|
};
|
|
91
322
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A product's documents (headless spec §9.2 `catalog`: "documents"; W10-10): the downloads the
|
|
3
|
+
* storefront lists on a product page (a data sheet, a manual, a certificate), each pointing at a
|
|
4
|
+
* file in the asset library.
|
|
5
|
+
*
|
|
6
|
+
* - **Universal or localized.** A universal document (`isLocalized: false`) has exactly ONE
|
|
7
|
+
* translation, with no language code, and it names the file. A localized one has one
|
|
8
|
+
* translation per language (each language once), and at least one of them names a file; the
|
|
9
|
+
* storefront shows the request's language, else the store's default, else one with a file.
|
|
10
|
+
* - **Who sees it** on the storefront: everyone (`PUBLIC`), signed-in customers
|
|
11
|
+
* (`AUTHENTICATED`), or customers in the listed groups (`CUSTOMER_GROUP`, which needs at least
|
|
12
|
+
* one group). The Admin API always lists every document.
|
|
13
|
+
* - **Order.** A new document goes after the product's last; `reorder` sets the order.
|
|
14
|
+
*
|
|
15
|
+
* Every document is the store's own: a product, document or customer group of another store is
|
|
16
|
+
* never answered and never written.
|
|
17
|
+
*
|
|
18
|
+
* Permissions: `list` needs `ReadCatalog`, the writes `UpdateCatalog`.
|
|
19
|
+
*
|
|
20
|
+
* Refusals, GraphQL errors mapped by `core`:
|
|
21
|
+
* - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a product of another
|
|
22
|
+
* store or none ("Product … not found"), a document of another store or none
|
|
23
|
+
* ("ProductDocument … not found"), a translation set that breaks the rules above, a
|
|
24
|
+
* `CUSTOMER_GROUP` document with no group, a `reorder` naming a document that is not one of the
|
|
25
|
+
* product's.
|
|
26
|
+
* - `ENTITY_NOT_FOUND` → `NotFoundError` (404): a customer group the store does not hold.
|
|
27
|
+
* - `TRANSLATION_LANGUAGE_NOT_OFFERED` → `ValidationError` (400): a translation in a language the
|
|
28
|
+
* store does not offer (a language the document already has stays editable).
|
|
29
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
|
|
30
|
+
*
|
|
31
|
+
* No release gate: every operation here is served by every platform release this SDK supports.
|
|
32
|
+
* No method deduplicates on `idempotencyKey`: repeating a `create` adds a second document.
|
|
33
|
+
*/
|
|
34
|
+
import { type Transport } from '@sixtyninecommerce/core';
|
|
35
|
+
import type { Call } from '../types.js';
|
|
36
|
+
import { type LanguageCode } from './types.js';
|
|
37
|
+
export type ProductDocumentVisibility = 'PUBLIC' | 'AUTHENTICATED' | 'CUSTOMER_GROUP';
|
|
38
|
+
/** The file a document translation points at: an asset of the library. */
|
|
39
|
+
export interface ProductDocumentFile {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
/** A string on the wire: a newer engine may add a type. */
|
|
43
|
+
type: 'IMAGE' | 'VIDEO' | 'BINARY' | (string & {});
|
|
44
|
+
mimeType: string;
|
|
45
|
+
/** Bytes. */
|
|
46
|
+
fileSize: number;
|
|
47
|
+
/** The asset's own URL (absolute or relative to the engine). */
|
|
48
|
+
source: string;
|
|
49
|
+
preview: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ProductDocumentTranslation {
|
|
52
|
+
id: string;
|
|
53
|
+
/** Null on a universal document's one translation. */
|
|
54
|
+
languageCode: LanguageCode | null;
|
|
55
|
+
title: string;
|
|
56
|
+
description: string | null;
|
|
57
|
+
assetId: string | null;
|
|
58
|
+
/** Null when the translation names no file, or its file is gone. */
|
|
59
|
+
asset: ProductDocumentFile | null;
|
|
60
|
+
}
|
|
61
|
+
export interface ProductDocument {
|
|
62
|
+
id: string;
|
|
63
|
+
productId: string;
|
|
64
|
+
/** 0 first. */
|
|
65
|
+
position: number;
|
|
66
|
+
visibility: ProductDocumentVisibility | (string & {});
|
|
67
|
+
isLocalized: boolean;
|
|
68
|
+
translations: ProductDocumentTranslation[];
|
|
69
|
+
/** The groups a `CUSTOMER_GROUP` document is shown to; empty under the other two. */
|
|
70
|
+
customerGroups: Array<{
|
|
71
|
+
id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
}>;
|
|
74
|
+
/** ISO-8601 UTC. */
|
|
75
|
+
createdAt: string;
|
|
76
|
+
/** ISO-8601 UTC. */
|
|
77
|
+
updatedAt: string;
|
|
78
|
+
}
|
|
79
|
+
export interface ProductDocumentTranslationInput {
|
|
80
|
+
/** Null (or omitted) on a universal document; required, once per language, on a localized one. */
|
|
81
|
+
languageCode?: LanguageCode | null;
|
|
82
|
+
title: string;
|
|
83
|
+
description?: string | null;
|
|
84
|
+
/** The asset (`catalog.assets`) the translation points at. */
|
|
85
|
+
assetId?: string | null;
|
|
86
|
+
}
|
|
87
|
+
export interface CreateProductDocumentInput {
|
|
88
|
+
productId: string;
|
|
89
|
+
visibility: ProductDocumentVisibility;
|
|
90
|
+
isLocalized: boolean;
|
|
91
|
+
/** Required (one or more) under `CUSTOMER_GROUP`; ignored under the other two. */
|
|
92
|
+
customerGroupIds?: string[];
|
|
93
|
+
translations: ProductDocumentTranslationInput[];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Only the fields named change. `translations`, when sent, REPLACES the document's translations;
|
|
97
|
+
* `customerGroupIds`, when sent, replaces its groups. Leaving `CUSTOMER_GROUP` drops its groups.
|
|
98
|
+
*/
|
|
99
|
+
export interface UpdateProductDocumentInput {
|
|
100
|
+
visibility?: ProductDocumentVisibility;
|
|
101
|
+
isLocalized?: boolean;
|
|
102
|
+
customerGroupIds?: string[];
|
|
103
|
+
translations?: ProductDocumentTranslationInput[];
|
|
104
|
+
}
|
|
105
|
+
export interface ProductDocumentsModule {
|
|
106
|
+
/**
|
|
107
|
+
* The product's documents, in their order (ties: the older first). A product of another store,
|
|
108
|
+
* or none, answers `[]`.
|
|
109
|
+
*
|
|
110
|
+
* @effect read
|
|
111
|
+
*/
|
|
112
|
+
list(productId: string, options?: Call): Promise<ProductDocument[]>;
|
|
113
|
+
/**
|
|
114
|
+
* Adds a document after the product's last.
|
|
115
|
+
*
|
|
116
|
+
* @effect write
|
|
117
|
+
*/
|
|
118
|
+
create(input: CreateProductDocumentInput, options?: Call): Promise<ProductDocument>;
|
|
119
|
+
/** @effect write */
|
|
120
|
+
update(id: string, input: UpdateProductDocumentInput, options?: Call): Promise<ProductDocument>;
|
|
121
|
+
/**
|
|
122
|
+
* Deletes the document; its file stays in the asset library. A document the store does not
|
|
123
|
+
* have (another store's, or already deleted) is refused `PRODUCT_DOCUMENT_NOT_FOUND`
|
|
124
|
+
* (`NotFoundError`, 404, raised by this SDK: the engine answers it as `false`).
|
|
125
|
+
*
|
|
126
|
+
* @effect destructive
|
|
127
|
+
*/
|
|
128
|
+
delete(id: string, options?: Call): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Puts the named documents in this order (`0, 1, 2…`). Name the product's documents: one that
|
|
131
|
+
* is not the product's refuses the whole call and nothing moves; one left out keeps its position.
|
|
132
|
+
*
|
|
133
|
+
* @effect write
|
|
134
|
+
*/
|
|
135
|
+
reorder(productId: string, orderedIds: string[], options?: Call): Promise<void>;
|
|
136
|
+
}
|
|
137
|
+
export declare const PRODUCT_DOCUMENTS_QUERY = "\n query CatalogProductDocuments($productId: ID!) {\n productDocuments(productId: $productId) { \n id\n productId\n position\n visibility\n isLocalized\n createdAt\n updatedAt\n translations {\n id\n languageCode\n title\n description\n assetId\n asset {\n id\n name\n type\n mimeType\n fileSize\n source\n preview\n }\n }\n customerGroups {\n id\n name\n }\n }\n }\n";
|
|
138
|
+
export declare const CREATE_PRODUCT_DOCUMENT_MUTATION = "\n mutation CatalogCreateProductDocument($input: CreateProductDocumentInput!) {\n createProductDocument(input: $input) { \n id\n productId\n position\n visibility\n isLocalized\n createdAt\n updatedAt\n translations {\n id\n languageCode\n title\n description\n assetId\n asset {\n id\n name\n type\n mimeType\n fileSize\n source\n preview\n }\n }\n customerGroups {\n id\n name\n }\n }\n }\n";
|
|
139
|
+
export declare const UPDATE_PRODUCT_DOCUMENT_MUTATION = "\n mutation CatalogUpdateProductDocument($input: UpdateProductDocumentInput!) {\n updateProductDocument(input: $input) { \n id\n productId\n position\n visibility\n isLocalized\n createdAt\n updatedAt\n translations {\n id\n languageCode\n title\n description\n assetId\n asset {\n id\n name\n type\n mimeType\n fileSize\n source\n preview\n }\n }\n customerGroups {\n id\n name\n }\n }\n }\n";
|
|
140
|
+
export declare const DELETE_PRODUCT_DOCUMENT_MUTATION = "\n mutation CatalogDeleteProductDocument($id: ID!) {\n deleteProductDocument(id: $id)\n }\n";
|
|
141
|
+
export declare const REORDER_PRODUCT_DOCUMENTS_MUTATION = "\n mutation CatalogReorderProductDocuments($productId: ID!, $orderedIds: [ID!]!) {\n reorderProductDocuments(productId: $productId, orderedIds: $orderedIds)\n }\n";
|
|
142
|
+
export declare function createProductDocumentsModule(transport: Transport): ProductDocumentsModule;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A product's documents (headless spec §9.2 `catalog`: "documents"; W10-10): the downloads the
|
|
3
|
+
* storefront lists on a product page (a data sheet, a manual, a certificate), each pointing at a
|
|
4
|
+
* file in the asset library.
|
|
5
|
+
*
|
|
6
|
+
* - **Universal or localized.** A universal document (`isLocalized: false`) has exactly ONE
|
|
7
|
+
* translation, with no language code, and it names the file. A localized one has one
|
|
8
|
+
* translation per language (each language once), and at least one of them names a file; the
|
|
9
|
+
* storefront shows the request's language, else the store's default, else one with a file.
|
|
10
|
+
* - **Who sees it** on the storefront: everyone (`PUBLIC`), signed-in customers
|
|
11
|
+
* (`AUTHENTICATED`), or customers in the listed groups (`CUSTOMER_GROUP`, which needs at least
|
|
12
|
+
* one group). The Admin API always lists every document.
|
|
13
|
+
* - **Order.** A new document goes after the product's last; `reorder` sets the order.
|
|
14
|
+
*
|
|
15
|
+
* Every document is the store's own: a product, document or customer group of another store is
|
|
16
|
+
* never answered and never written.
|
|
17
|
+
*
|
|
18
|
+
* Permissions: `list` needs `ReadCatalog`, the writes `UpdateCatalog`.
|
|
19
|
+
*
|
|
20
|
+
* Refusals, GraphQL errors mapped by `core`:
|
|
21
|
+
* - `USER_INPUT_ERROR` → `ValidationError` (400), in the engine's words: a product of another
|
|
22
|
+
* store or none ("Product … not found"), a document of another store or none
|
|
23
|
+
* ("ProductDocument … not found"), a translation set that breaks the rules above, a
|
|
24
|
+
* `CUSTOMER_GROUP` document with no group, a `reorder` naming a document that is not one of the
|
|
25
|
+
* product's.
|
|
26
|
+
* - `ENTITY_NOT_FOUND` → `NotFoundError` (404): a customer group the store does not hold.
|
|
27
|
+
* - `TRANSLATION_LANGUAGE_NOT_OFFERED` → `ValidationError` (400): a translation in a language the
|
|
28
|
+
* store does not offer (a language the document already has stays editable).
|
|
29
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403) without the permission.
|
|
30
|
+
*
|
|
31
|
+
* No release gate: every operation here is served by every platform release this SDK supports.
|
|
32
|
+
* No method deduplicates on `idempotencyKey`: repeating a `create` adds a second document.
|
|
33
|
+
*/
|
|
34
|
+
import { NotFoundError } from '@sixtyninecommerce/core';
|
|
35
|
+
import { fieldOf } from './types.js';
|
|
36
|
+
const DOCUMENT_FIELDS = /* GraphQL */ `
|
|
37
|
+
id
|
|
38
|
+
productId
|
|
39
|
+
position
|
|
40
|
+
visibility
|
|
41
|
+
isLocalized
|
|
42
|
+
createdAt
|
|
43
|
+
updatedAt
|
|
44
|
+
translations {
|
|
45
|
+
id
|
|
46
|
+
languageCode
|
|
47
|
+
title
|
|
48
|
+
description
|
|
49
|
+
assetId
|
|
50
|
+
asset {
|
|
51
|
+
id
|
|
52
|
+
name
|
|
53
|
+
type
|
|
54
|
+
mimeType
|
|
55
|
+
fileSize
|
|
56
|
+
source
|
|
57
|
+
preview
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
customerGroups {
|
|
61
|
+
id
|
|
62
|
+
name
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
export const PRODUCT_DOCUMENTS_QUERY = /* GraphQL */ `
|
|
66
|
+
query CatalogProductDocuments($productId: ID!) {
|
|
67
|
+
productDocuments(productId: $productId) { ${DOCUMENT_FIELDS} }
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
export const CREATE_PRODUCT_DOCUMENT_MUTATION = /* GraphQL */ `
|
|
71
|
+
mutation CatalogCreateProductDocument($input: CreateProductDocumentInput!) {
|
|
72
|
+
createProductDocument(input: $input) { ${DOCUMENT_FIELDS} }
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
export const UPDATE_PRODUCT_DOCUMENT_MUTATION = /* GraphQL */ `
|
|
76
|
+
mutation CatalogUpdateProductDocument($input: UpdateProductDocumentInput!) {
|
|
77
|
+
updateProductDocument(input: $input) { ${DOCUMENT_FIELDS} }
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
export const DELETE_PRODUCT_DOCUMENT_MUTATION = /* GraphQL */ `
|
|
81
|
+
mutation CatalogDeleteProductDocument($id: ID!) {
|
|
82
|
+
deleteProductDocument(id: $id)
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
export const REORDER_PRODUCT_DOCUMENTS_MUTATION = /* GraphQL */ `
|
|
86
|
+
mutation CatalogReorderProductDocuments($productId: ID!, $orderedIds: [ID!]!) {
|
|
87
|
+
reorderProductDocuments(productId: $productId, orderedIds: $orderedIds)
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
/** The engine reads the list in `position` order; keep that, with the older first on a tie. */
|
|
91
|
+
function byPosition(documents) {
|
|
92
|
+
return [...documents].sort((a, b) => a.position - b.position || a.createdAt.localeCompare(b.createdAt));
|
|
93
|
+
}
|
|
94
|
+
export function createProductDocumentsModule(transport) {
|
|
95
|
+
return {
|
|
96
|
+
list: async (productId, options) => byPosition(await fieldOf(transport, PRODUCT_DOCUMENTS_QUERY, 'productDocuments', { productId }, options)),
|
|
97
|
+
create: (input, options) => fieldOf(transport, CREATE_PRODUCT_DOCUMENT_MUTATION, 'createProductDocument', { input }, options),
|
|
98
|
+
update: (id, input, options) => fieldOf(transport, UPDATE_PRODUCT_DOCUMENT_MUTATION, 'updateProductDocument', { input: { ...input, id } }, options),
|
|
99
|
+
async delete(id, options) {
|
|
100
|
+
const deleted = await fieldOf(transport, DELETE_PRODUCT_DOCUMENT_MUTATION, 'deleteProductDocument', { id }, options);
|
|
101
|
+
if (!deleted) {
|
|
102
|
+
throw new NotFoundError({
|
|
103
|
+
code: 'PRODUCT_DOCUMENT_NOT_FOUND',
|
|
104
|
+
message: `This store has no product document ${id}.`,
|
|
105
|
+
status: 404,
|
|
106
|
+
service: 'vendure',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
async reorder(productId, orderedIds, options) {
|
|
111
|
+
await fieldOf(transport, REORDER_PRODUCT_DOCUMENTS_MUTATION, 'reorderProductDocuments', { productId, orderedIds }, options);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
package/dist/catalog/index.d.ts
CHANGED
|
@@ -2,16 +2,21 @@ import type { Transport } from '@sixtyninecommerce/core';
|
|
|
2
2
|
import type { Call } from '../types.js';
|
|
3
3
|
import { type AssetsModule } from './assets.js';
|
|
4
4
|
import { type CollectionsModule } from './collections.js';
|
|
5
|
+
import { type ProductDocumentsModule } from './documents.js';
|
|
5
6
|
import { type FacetsModule } from './facets.js';
|
|
7
|
+
import { type ProductMetadataModule } from './metadata.js';
|
|
6
8
|
import { type OptionsModule } from './options.js';
|
|
7
9
|
import { type ProductsModule } from './products.js';
|
|
8
|
-
import { type ContentFacetSyncResult, type PropertiesModule } from './properties.js';
|
|
10
|
+
import { type ContentFacetSyncResult, type ContentItemFacetSyncResult, type PropertiesModule } from './properties.js';
|
|
11
|
+
import { type RecommendationsModule } from './recommendations.js';
|
|
12
|
+
import { type ReviewsModule } from './reviews.js';
|
|
9
13
|
import { type VariantsModule } from './variants.js';
|
|
10
14
|
import { type VisibilityModule } from './visibility.js';
|
|
11
15
|
/**
|
|
12
16
|
* The catalogue (spec §9.2 `catalog`): products, variants, options, facets,
|
|
13
|
-
* collections, assets, storefront visibility
|
|
14
|
-
* resource per namespace, over the
|
|
17
|
+
* collections, assets, storefront visibility, product documents, product SEO metadata, product
|
|
18
|
+
* properties, product recommendations and product reviews — one resource per namespace, over the
|
|
19
|
+
* Admin API the dashboard itself uses.
|
|
15
20
|
*
|
|
16
21
|
* **Per-resource, not per-intent.** Saving a product form is several calls
|
|
17
22
|
* here (the product, its variants, its properties, its collections), and they
|
|
@@ -69,7 +74,15 @@ export interface CatalogModule {
|
|
|
69
74
|
collections: CollectionsModule;
|
|
70
75
|
assets: AssetsModule;
|
|
71
76
|
visibility: VisibilityModule;
|
|
77
|
+
/** A product's downloads: data sheets, manuals, certificates (W10-10). */
|
|
78
|
+
documents: ProductDocumentsModule;
|
|
79
|
+
/** A product's SEO, social and AI-agent metadata, and the SEO coverage list (W10-10). */
|
|
80
|
+
metadata: ProductMetadataModule;
|
|
72
81
|
properties: PropertiesModule;
|
|
82
|
+
/** Recommendation groups, their rules and the products picked per product (W10-13). */
|
|
83
|
+
recommendations: RecommendationsModule;
|
|
84
|
+
/** Product reviews: moderation, the store's replies, statistics, review attributes (W10-117). */
|
|
85
|
+
reviews: ReviewsModule;
|
|
73
86
|
/**
|
|
74
87
|
* "Sync filter values" for one filterable `content_item` or `product` property, done by the
|
|
75
88
|
* engine (headless spec §7 `catalog.syncContentFacets`, by property, the unit the merchant syncs).
|
|
@@ -86,9 +99,11 @@ export interface CatalogModule {
|
|
|
86
99
|
* anything changes. Repeating the call is safe: a label that did not change is not written
|
|
87
100
|
* again, so a repeat answers the same counts and changes nothing.
|
|
88
101
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
102
|
+
* Automatic for PUBLISHED content items (headless W10-19, ruling G115): when a published item
|
|
103
|
+
* is edited, published or unpublished, the platform syncs the properties naming it by itself
|
|
104
|
+
* within about a minute ({@link CatalogModule.syncContentItemFacets}). Call this after renaming
|
|
105
|
+
* a DRAFT or archived item (the content system announces nothing for one until it is
|
|
106
|
+
* published), after renaming a product, or to sync one property at once.
|
|
92
107
|
*
|
|
93
108
|
* Needs `UpdateCatalog`. Platform releases before this method do not have the mutation and refuse
|
|
94
109
|
* it as `ValidationError` `GRAPHQL_VALIDATION_FAILED` (400).
|
|
@@ -109,7 +124,40 @@ export interface CatalogModule {
|
|
|
109
124
|
* - `PROPERTY_SYNC_SOURCE_UNAVAILABLE` → `BusinessRuleError` (422): a `content_item` property on
|
|
110
125
|
* a platform with no content system wired.
|
|
111
126
|
* - `FORBIDDEN` → `PermissionDeniedError` (403): no `UpdateCatalog`.
|
|
127
|
+
*
|
|
128
|
+
* @effect write
|
|
112
129
|
*/
|
|
113
130
|
syncContentFacets(propertyId: string, options?: Call): Promise<ContentFacetSyncResult>;
|
|
131
|
+
/**
|
|
132
|
+
* "Sync filter values" for every filterable `content_item` property of the store whose values
|
|
133
|
+
* name one of `contentItemIds` (headless W10-19): each is synced as
|
|
134
|
+
* {@link CatalogModule.syncContentFacets} syncs it, so a renamed content item's filter label
|
|
135
|
+
* follows its title. The platform calls it by itself, within about a minute, for the items a
|
|
136
|
+
* change to a PUBLISHED item touched (published, edited while published, unpublished); call it
|
|
137
|
+
* yourself for items you know changed otherwise (an app importing content, a draft renamed).
|
|
138
|
+
*
|
|
139
|
+
* Per property: a property its own sync refuses is reported in `skipped` with that refusal
|
|
140
|
+
* (`MANAGED_BY_ORGANIZATION` for the organization workspace's property, synced in the
|
|
141
|
+
* workspace) and nothing of it is written; the others are synced. `changed` says whether
|
|
142
|
+
* anything was written. Every label is read before anything is written, and the writes are
|
|
143
|
+
* one transaction: a content system that cannot be read, or any failure other than a
|
|
144
|
+
* property's refusal, refuses the whole call and writes nothing. Repeating it is safe.
|
|
145
|
+
*
|
|
146
|
+
* At most {@link MAX_CONTENT_ITEM_FACET_SYNC_IDS} different ids per call (repeats count once);
|
|
147
|
+
* an id no property names syncs nothing. Needs `UpdateCatalog`. Platform releases before this
|
|
148
|
+
* method do not have the mutation and refuse it as `ValidationError`
|
|
149
|
+
* `GRAPHQL_VALIDATION_FAILED` (400).
|
|
150
|
+
*
|
|
151
|
+
* Refusals of the whole call (nothing is written):
|
|
152
|
+
* - `CONTENT_ITEM_IDS_INVALID` → `ValidationError` (400): a blank id, or more than 500
|
|
153
|
+
* different ids; checked before sending as well.
|
|
154
|
+
* - `ENTITY_NOT_FOUND` → `NotFoundError` (404): an id that is not a uuid.
|
|
155
|
+
* - `CMS_REQUEST_FAILED` → `BusinessRuleError` (422): the content system could not be read;
|
|
156
|
+
* try again.
|
|
157
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403): no `UpdateCatalog`.
|
|
158
|
+
*
|
|
159
|
+
* @effect write
|
|
160
|
+
*/
|
|
161
|
+
syncContentItemFacets(contentItemIds: readonly string[], options?: Call): Promise<ContentItemFacetSyncResult>;
|
|
114
162
|
}
|
|
115
163
|
export declare function createCatalogModule(transport: Transport): CatalogModule;
|