@sixtyninecommerce/admin 0.11.0 → 0.13.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 +1509 -28
- package/dist/auth/index.d.ts +7 -1
- package/dist/auth/index.js +8 -1
- package/dist/catalog/documents.d.ts +142 -0
- package/dist/catalog/documents.js +114 -0
- package/dist/catalog/index.d.ts +48 -7
- package/dist/catalog/index.js +6 -1
- package/dist/catalog/metadata.d.ts +244 -0
- package/dist/catalog/metadata.js +220 -0
- package/dist/catalog/properties.d.ts +38 -0
- package/dist/catalog/properties.js +52 -1
- package/dist/catalog/recommendations.d.ts +33 -6
- package/dist/catalog/recommendations.js +94 -2
- package/dist/catalog/reviews.d.ts +17 -4
- package/dist/catalog/reviews.js +7 -0
- package/dist/catalog/variants.d.ts +60 -0
- package/dist/catalog/variants.js +39 -0
- package/dist/catalog/visibility.d.ts +18 -2
- package/dist/client.d.ts +29 -3
- package/dist/client.js +19 -8
- package/dist/content/assets.d.ts +3 -4
- package/dist/content/forms.d.ts +332 -1
- package/dist/content/forms.js +140 -4
- package/dist/content/items.d.ts +31 -8
- package/dist/content/items.js +1 -1
- 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/customers/delivery-terms.d.ts +44 -0
- package/dist/customers/delivery-terms.js +32 -0
- package/dist/customers/groups.d.ts +16 -5
- package/dist/customers/groups.js +6 -2
- package/dist/customers/index.d.ts +15 -0
- package/dist/customers/index.js +4 -0
- package/dist/documents/emails.d.ts +154 -0
- package/dist/documents/emails.js +220 -0
- package/dist/documents/index.d.ts +13 -0
- package/dist/documents/index.js +2 -0
- package/dist/fulfillments/create.d.ts +1 -1
- package/dist/fulfillments/create.js +5 -2
- package/dist/fulfillments/index.d.ts +119 -1
- package/dist/fulfillments/index.js +124 -4
- package/dist/fulfillments/warehouse.d.ts +29 -17
- package/dist/fulfillments/warehouse.js +32 -1
- package/dist/index.d.ts +43 -23
- package/dist/index.js +11 -5
- package/dist/integrations/exact-online-types.d.ts +542 -0
- package/dist/integrations/exact-online.d.ts +541 -5
- package/dist/integrations/exact-online.js +714 -1
- package/dist/integrations/index.d.ts +23 -8
- package/dist/integrations/index.js +9 -4
- package/dist/integrations/pipeline-discoveries.d.ts +65 -0
- package/dist/integrations/pipeline-discoveries.js +65 -0
- package/dist/integrations/pipeline-runs.d.ts +119 -0
- package/dist/integrations/pipeline-runs.js +87 -0
- package/dist/integrations/pipelines.d.ts +73 -2
- package/dist/integrations/pipelines.js +36 -1
- package/dist/integrations/price-import.d.ts +223 -0
- package/dist/integrations/price-import.js +124 -0
- package/dist/integrations/types.d.ts +221 -3
- package/dist/integrations/types.js +4 -3
- package/dist/meta/index.d.ts +10 -0
- package/dist/metafields/index.d.ts +8 -1
- package/dist/metafields/index.js +10 -0
- package/dist/orders/cancel-with-money.d.ts +10 -0
- package/dist/orders/cancel-with-money.js +2 -0
- package/dist/orders/commit-edit.d.ts +16 -0
- package/dist/orders/commit-edit.js +14 -1
- package/dist/orders/deposit-policy.d.ts +3 -1
- package/dist/orders/index.d.ts +22 -11
- package/dist/orders/index.js +5 -2
- package/dist/orders/list.d.ts +26 -5
- package/dist/orders/list.js +123 -13
- package/dist/orders/manual-discounts.d.ts +13 -5
- package/dist/orders/money-view.d.ts +149 -0
- package/dist/orders/money-view.js +99 -0
- package/dist/orders/plan-edit.d.ts +34 -2
- package/dist/orders/plan-edit.js +110 -12
- package/dist/orders/surcharges.d.ts +12 -0
- package/dist/orders/surcharges.js +34 -9
- package/dist/payments/account-credit.d.ts +17 -1
- package/dist/payments/account-credit.js +25 -2
- package/dist/payments/credit-noted-fields.d.ts +8 -1
- package/dist/payments/credit-noted-fields.js +34 -2
- package/dist/payments/customer-consent.d.ts +30 -0
- package/dist/payments/customer-consent.js +49 -0
- package/dist/payments/index.d.ts +89 -14
- package/dist/payments/index.js +22 -2
- package/dist/payments/requests.d.ts +6 -3
- package/dist/payments/requests.js +1 -0
- package/dist/pickups/index.d.ts +5 -3
- package/dist/pickups/index.js +5 -2
- package/dist/platform/email-gateways.d.ts +165 -0
- package/dist/platform/email-gateways.js +78 -0
- package/dist/platform/tax.d.ts +11 -3
- package/dist/platform/tax.js +30 -0
- package/dist/pos/exchanges.d.ts +6 -1
- package/dist/pos/index.d.ts +229 -10
- package/dist/pos/index.js +113 -4
- package/dist/pos/offline-sales.d.ts +44 -3
- package/dist/pos/offline-sales.js +57 -3
- package/dist/pos/returns.d.ts +28 -1
- package/dist/pos/returns.js +36 -6
- package/dist/pricing/price-lists.d.ts +3 -1
- package/dist/purchasing/alerts.d.ts +19 -4
- package/dist/purchasing/alerts.js +29 -3
- package/dist/purchasing/backorders.d.ts +61 -0
- package/dist/purchasing/backorders.js +92 -0
- package/dist/purchasing/index.d.ts +21 -3
- package/dist/purchasing/index.js +8 -0
- package/dist/purchasing/links.d.ts +263 -0
- package/dist/purchasing/links.js +192 -0
- package/dist/purchasing/purchase-orders.d.ts +4 -1
- package/dist/purchasing/receipts.d.ts +396 -0
- package/dist/purchasing/receipts.js +287 -0
- package/dist/purchasing/returns.d.ts +273 -0
- package/dist/purchasing/returns.js +229 -0
- package/dist/quotes/index.d.ts +20 -3
- package/dist/returns/index.d.ts +62 -1
- package/dist/returns/index.js +61 -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/index.d.ts +6 -0
- package/dist/shipping/index.js +2 -0
- package/dist/shipping/methods.d.ts +8 -1
- package/dist/shipping/myparcel.d.ts +415 -1
- package/dist/shipping/myparcel.js +291 -3
- package/dist/shipping/rate-tables.d.ts +149 -4
- package/dist/shipping/rate-tables.js +229 -23
- 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 +8 -1
- package/dist/stock/index.js +4 -0
- package/dist/stock/settings.d.ts +106 -0
- package/dist/stock/settings.js +103 -0
- package/dist/stock/transfers.d.ts +171 -10
- package/dist/stock/transfers.js +82 -4
- package/dist/stored-value/index.d.ts +98 -7
- package/dist/stored-value/index.js +71 -4
- package/dist/tax/display-policy.d.ts +105 -0
- package/dist/tax/display-policy.js +84 -0
- package/dist/tax/index.d.ts +3 -0
- package/dist/tax/index.js +2 -0
- package/dist/tenant/email-templates.d.ts +222 -1
- package/dist/tenant/email-templates.js +227 -10
- package/package.json +2 -2
|
@@ -29,15 +29,226 @@ export interface EmailPreviewOptions extends Call {
|
|
|
29
29
|
*/
|
|
30
30
|
style?: EmailStylePatch;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* One part of a subject line or of a block's text: literal text (`{ type: 'text', value }`), or a
|
|
34
|
+
* variable the email fills in when it is sent (`{ type: 'var', key }`, a key from the template
|
|
35
|
+
* type's `variables` in {@link EmailTemplateCatalog}). A variable key the type does not offer is
|
|
36
|
+
* printed as `[key]`.
|
|
37
|
+
*/
|
|
38
|
+
export interface EmailTokenSegment {
|
|
39
|
+
type: 'text' | 'var';
|
|
40
|
+
/** The text of a `text` part. */
|
|
41
|
+
value?: string;
|
|
42
|
+
/** The variable of a `var` part. */
|
|
43
|
+
key?: string;
|
|
44
|
+
}
|
|
45
|
+
/** A block's texts in one language. */
|
|
46
|
+
export interface EmailTemplateBlockContent {
|
|
47
|
+
languageCode: string;
|
|
48
|
+
/**
|
|
49
|
+
* One entry per text of the block, keyed by the block type's field names (most hold
|
|
50
|
+
* {@link EmailTokenSegment} lists). The renderer falls back to the block's defaults for a
|
|
51
|
+
* missing entry.
|
|
52
|
+
*/
|
|
53
|
+
content: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
/** One block (the engine calls them chips) of a template's body, e.g. a heading or the order lines. */
|
|
56
|
+
export interface EmailTemplateBlock {
|
|
57
|
+
id: string;
|
|
58
|
+
/** The block type: one of the catalog's `blockTypes`. */
|
|
59
|
+
type: string;
|
|
60
|
+
/** Its place in the body: the blocks are answered in this order. */
|
|
61
|
+
position: number;
|
|
62
|
+
/** The block's switches and settings (e.g. `{ showImage: true }`), the same in every language. */
|
|
63
|
+
config: Record<string, unknown>;
|
|
64
|
+
/** Its texts, one entry per language the store wrote them in. */
|
|
65
|
+
contents: EmailTemplateBlockContent[];
|
|
66
|
+
}
|
|
67
|
+
/** A template's subject line in one language. */
|
|
68
|
+
export interface EmailTemplateSubject {
|
|
69
|
+
languageCode: string;
|
|
70
|
+
segments: EmailTokenSegment[];
|
|
71
|
+
}
|
|
72
|
+
/** The template of one type, as the store sends it now. */
|
|
73
|
+
export interface EmailTemplate {
|
|
74
|
+
/**
|
|
75
|
+
* The template's id. It changes when the store starts or stops taking `settings.email` from its
|
|
76
|
+
* organization, so the methods here address a template by its `type`; keep the id only to tell
|
|
77
|
+
* that the template a page shows was replaced.
|
|
78
|
+
*/
|
|
79
|
+
id: string;
|
|
80
|
+
type: string;
|
|
81
|
+
enabled: boolean;
|
|
82
|
+
/** Who it is sent to, as recipient variables and fixed addresses. */
|
|
83
|
+
recipients: {
|
|
84
|
+
to: EmailTokenSegment[];
|
|
85
|
+
cc: EmailTokenSegment[];
|
|
86
|
+
bcc: EmailTokenSegment[];
|
|
87
|
+
};
|
|
88
|
+
/** The body, in `position` order. */
|
|
89
|
+
blocks: EmailTemplateBlock[];
|
|
90
|
+
subjects: EmailTemplateSubject[];
|
|
91
|
+
}
|
|
92
|
+
export interface EmailTemplateVariable {
|
|
93
|
+
key: string;
|
|
94
|
+
label: string;
|
|
95
|
+
}
|
|
96
|
+
export interface EmailTemplateTypeInfo {
|
|
97
|
+
type: string;
|
|
98
|
+
label: string;
|
|
99
|
+
description: string;
|
|
100
|
+
/** The variables a subject or block text of this type may use. */
|
|
101
|
+
variables: EmailTemplateVariable[];
|
|
102
|
+
}
|
|
103
|
+
export interface EmailBlockTypeInfo {
|
|
104
|
+
type: string;
|
|
105
|
+
label: string;
|
|
106
|
+
/** A grouping for a picker: `structural`, `content`, `commerce_data`, `action`, `cms_data`, … */
|
|
107
|
+
category: string;
|
|
108
|
+
}
|
|
109
|
+
/** What the template editor can offer: the same for every store. */
|
|
110
|
+
export interface EmailTemplateCatalog {
|
|
111
|
+
templateTypes: EmailTemplateTypeInfo[];
|
|
112
|
+
blockTypes: EmailBlockTypeInfo[];
|
|
113
|
+
/** The variables a recipient field may use. */
|
|
114
|
+
recipientVariables: EmailTemplateVariable[];
|
|
115
|
+
}
|
|
116
|
+
export interface AddEmailTemplateBlockInput {
|
|
117
|
+
/** The block type: one of the catalog's `blockTypes`. */
|
|
118
|
+
type: string;
|
|
119
|
+
/**
|
|
120
|
+
* Where to put it: `0` is first, and the blocks at that place and after it move down one. At the
|
|
121
|
+
* end when omitted.
|
|
122
|
+
*/
|
|
123
|
+
position?: number;
|
|
124
|
+
/** Its switches and settings; `{}` when omitted. */
|
|
125
|
+
config?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The template writes, by template type or by block id. Every one is refused while the store takes
|
|
129
|
+
* `settings.email` from its organization (see {@link EmailTemplatesModule}).
|
|
130
|
+
*
|
|
131
|
+
* **Coded refusals shared by every write:**
|
|
132
|
+
* - `ORGANIZATION_SETTINGS_INHERITED` → `BusinessRuleError` (422): the store inherits
|
|
133
|
+
* `settings.email`; `tenant.settingsSources.override('settings.email')` first.
|
|
134
|
+
* - `MANAGED_BY_ORGANIZATION` → `BusinessRuleError` (422): the organization locks it.
|
|
135
|
+
* - `FORBIDDEN` → `PermissionDeniedError` (403): the credential lacks `UpdateSettings`.
|
|
136
|
+
*/
|
|
137
|
+
export interface EmailTemplateBlocksModule {
|
|
138
|
+
/**
|
|
139
|
+
* Add a block to the template of `templateType`, and answer it (with no `contents`: a new block
|
|
140
|
+
* has no texts until `setContent`). Two requests: the template the store uses is looked up
|
|
141
|
+
* first.
|
|
142
|
+
*
|
|
143
|
+
* **Coded refusals:** `EMAIL_TEMPLATE_NOT_FOUND` → `NotFoundError` (404), no template of the
|
|
144
|
+
* type; `EMAIL_CHIP_TYPE_INVALID` → `ValidationError` (400), a block type outside the catalog;
|
|
145
|
+
* `EMAIL_CHIP_CONFIG_INVALID` (400), a `config` that is not an object; `ENTITY_NOT_FOUND` →
|
|
146
|
+
* `NotFoundError` (404), the template was replaced between the two requests (ask again).
|
|
147
|
+
*
|
|
148
|
+
* @effect write
|
|
149
|
+
* @permission UpdateSettings
|
|
150
|
+
*/
|
|
151
|
+
add(templateType: string, block: AddEmailTemplateBlockInput, options?: Call): Promise<EmailTemplateBlock>;
|
|
152
|
+
/**
|
|
153
|
+
* Replace a block's `config` as a whole (send every key it keeps), and answer the block without
|
|
154
|
+
* its `contents` (`getByType` reads them).
|
|
155
|
+
*
|
|
156
|
+
* **Coded refusals:** `EMAIL_CHIP_CONFIG_INVALID` → `ValidationError` (400), a config that is not
|
|
157
|
+
* an object; `ENTITY_NOT_FOUND` → `NotFoundError` (404), no block of the store's with that id.
|
|
158
|
+
*
|
|
159
|
+
* @effect write
|
|
160
|
+
* @permission UpdateSettings
|
|
161
|
+
*/
|
|
162
|
+
update(blockId: string, patch: {
|
|
163
|
+
config: Record<string, unknown>;
|
|
164
|
+
}, options?: Call): Promise<Omit<EmailTemplateBlock, 'contents'>>;
|
|
165
|
+
/**
|
|
166
|
+
* Delete a block with its texts in every language. The blocks after it move up one. There is
|
|
167
|
+
* no undo: a block added back starts from its defaults.
|
|
168
|
+
*
|
|
169
|
+
* **Coded refusal:** `ENTITY_NOT_FOUND` → `NotFoundError` (404), no block of the store's with
|
|
170
|
+
* that id.
|
|
171
|
+
*
|
|
172
|
+
* @effect destructive
|
|
173
|
+
* @permission UpdateSettings
|
|
174
|
+
*/
|
|
175
|
+
remove(blockId: string, options?: Call): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Put the blocks of the template of `templateType` in a new order: `blockIds` lists EVERY block
|
|
178
|
+
* of the template once, first to last. Two requests: the template is looked up first.
|
|
179
|
+
*
|
|
180
|
+
* **Coded refusals:** `EMAIL_CHIP_ORDER_INVALID` → `ValidationError` (400), a block left out,
|
|
181
|
+
* listed twice, or not one of the template's (the message names it); `EMAIL_TEMPLATE_NOT_FOUND`
|
|
182
|
+
* → `NotFoundError` (404), no template of the type; `ENTITY_NOT_FOUND` (404), the template was
|
|
183
|
+
* replaced between the two requests.
|
|
184
|
+
*
|
|
185
|
+
* @effect write
|
|
186
|
+
* @permission UpdateSettings
|
|
187
|
+
*/
|
|
188
|
+
reorder(templateType: string, blockIds: readonly string[], options?: Call): Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* Set a block's texts in one language, replacing what it had in that language (the other
|
|
191
|
+
* languages keep theirs). Answers what is stored.
|
|
192
|
+
*
|
|
193
|
+
* **Coded refusals:** `EMAIL_CHIP_CONTENT_INVALID` → `ValidationError` (400), content that is
|
|
194
|
+
* not an object; `EMAIL_LANGUAGE_CODE_INVALID` (400), a language code that is none (`en`, `nl`,
|
|
195
|
+
* `pt_BR`, …); `ENTITY_NOT_FOUND` → `NotFoundError` (404), no block of the store's with that id.
|
|
196
|
+
*
|
|
197
|
+
* @effect write
|
|
198
|
+
* @permission UpdateSettings
|
|
199
|
+
*/
|
|
200
|
+
setContent(blockId: string, languageCode: string, content: Record<string, unknown>, options?: Call): Promise<EmailTemplateBlockContent>;
|
|
201
|
+
}
|
|
32
202
|
/**
|
|
33
203
|
* `admin.tenant.emailTemplates` — the active store's email templates (part of the `settings.email`
|
|
34
204
|
* group). Always the ACTIVE channel; no channel argument.
|
|
35
205
|
*
|
|
36
206
|
* Templates are addressed by TYPE (`order_confirmation`, `shipping_confirmation`, …): a template's
|
|
37
207
|
* id changes when the store starts or stops taking `settings.email` from its organization, its
|
|
38
|
-
* type never does. While the store inherits, every read answers the organization's template
|
|
208
|
+
* type never does. While the store inherits, every read answers the organization's template, and
|
|
209
|
+
* every write (`setSubject`, `blocks.*`) is refused (`ORGANIZATION_SETTINGS_INHERITED` or
|
|
210
|
+
* `MANAGED_BY_ORGANIZATION`, 422).
|
|
211
|
+
*
|
|
212
|
+
* A change applies to the next email of its type the store sends; an email already sent keeps
|
|
213
|
+
* what it said.
|
|
39
214
|
*/
|
|
40
215
|
export interface EmailTemplatesModule {
|
|
216
|
+
/**
|
|
217
|
+
* What the editor can offer: each template type with its label, description and variables, the
|
|
218
|
+
* block types, and the recipient variables. The same for every store.
|
|
219
|
+
*
|
|
220
|
+
* @effect read
|
|
221
|
+
* @permission ReadSettings
|
|
222
|
+
*/
|
|
223
|
+
catalog(options?: Call): Promise<EmailTemplateCatalog>;
|
|
224
|
+
/**
|
|
225
|
+
* The template of `type` the store sends, with its blocks (in order) and subjects, or `null`
|
|
226
|
+
* for a type the engine does not know.
|
|
227
|
+
*
|
|
228
|
+
* For a store on its own settings, the first read of a known type the store has no template
|
|
229
|
+
* of yet creates it from the platform's default.
|
|
230
|
+
*
|
|
231
|
+
* @effect read
|
|
232
|
+
* @permission ReadSettings
|
|
233
|
+
*/
|
|
234
|
+
getByType(type: string, options?: Call): Promise<EmailTemplate | null>;
|
|
235
|
+
/**
|
|
236
|
+
* Set the subject of the template of `templateType` in one language, replacing what it had in
|
|
237
|
+
* that language. Two requests: the template the store uses is looked up first. Answers what is
|
|
238
|
+
* stored.
|
|
239
|
+
*
|
|
240
|
+
* **Coded refusals:** `EMAIL_SUBJECT_INVALID` → `ValidationError` (400), a part that is neither
|
|
241
|
+
* text with its `value` nor a variable with its `key` (the message names it);
|
|
242
|
+
* `EMAIL_LANGUAGE_CODE_INVALID` (400), a language code that is none; `EMAIL_TEMPLATE_NOT_FOUND`
|
|
243
|
+
* → `NotFoundError` (404), no template of the type; `ENTITY_NOT_FOUND` (404), the template was
|
|
244
|
+
* replaced between the two requests. And those of {@link EmailTemplateBlocksModule}.
|
|
245
|
+
*
|
|
246
|
+
* @effect write
|
|
247
|
+
* @permission UpdateSettings
|
|
248
|
+
*/
|
|
249
|
+
setSubject(templateType: string, languageCode: string, segments: readonly EmailTokenSegment[], options?: Call): Promise<EmailTemplateSubject>;
|
|
250
|
+
/** The blocks of a template's body. */
|
|
251
|
+
blocks: EmailTemplateBlocksModule;
|
|
41
252
|
/**
|
|
42
253
|
* Render the template of `type` as the store would send it now, optionally with unsaved style
|
|
43
254
|
* changes. Two requests: the template the store uses for `type` is looked up first.
|
|
@@ -61,6 +272,16 @@ export interface EmailTemplatesModule {
|
|
|
61
272
|
*/
|
|
62
273
|
preview(type: string, options: EmailPreviewOptions): Promise<EmailPreview>;
|
|
63
274
|
}
|
|
275
|
+
export declare const EMAIL_TEMPLATE_CATALOG_QUERY = "\n query EmailTemplateCatalog {\n emailTemplateCatalog {\n templateTypes { type label description variables { key label } }\n chipTypes { type label category }\n recipientVariables { key label }\n }\n }\n";
|
|
276
|
+
export declare const EMAIL_TEMPLATE_BY_TYPE_QUERY = "\n query EmailTemplateByType($type: String!) {\n emailTemplateByType(type: $type) {\n id\n type\n enabled\n recipientTo { type value key }\n recipientCc { type value key }\n recipientBcc { type value key }\n chips { id type position config translations { languageCode content } }\n subjects { languageCode subject { type value key } }\n }\n }\n";
|
|
64
277
|
export declare const EMAIL_TEMPLATE_ID_BY_TYPE_QUERY = "\n query EmailTemplateIdByType($type: String!) {\n emailTemplateByType(type: $type) { id }\n }\n";
|
|
278
|
+
/** The look-up `blocks.add` makes: the id, and where the body ends. */
|
|
279
|
+
export declare const EMAIL_TEMPLATE_END_BY_TYPE_QUERY = "\n query EmailTemplateEndByType($type: String!) {\n emailTemplateByType(type: $type) { id chips { position } }\n }\n";
|
|
280
|
+
export declare const ADD_EMAIL_TEMPLATE_BLOCK_MUTATION = "\n mutation AddEmailTemplateBlock($templateId: ID!, $input: AddChipInput!) {\n addEmailTemplateChip(templateId: $templateId, input: $input) { id type position config }\n }\n";
|
|
281
|
+
export declare const UPDATE_EMAIL_TEMPLATE_BLOCK_MUTATION = "\n mutation UpdateEmailTemplateBlock($chipId: ID!, $input: UpdateChipInput!) {\n updateEmailTemplateChip(chipId: $chipId, input: $input) { id type position config }\n }\n";
|
|
282
|
+
export declare const REMOVE_EMAIL_TEMPLATE_BLOCK_MUTATION = "\n mutation RemoveEmailTemplateBlock($chipId: ID!) {\n removeEmailTemplateChip(chipId: $chipId)\n }\n";
|
|
283
|
+
export declare const REORDER_EMAIL_TEMPLATE_BLOCKS_MUTATION = "\n mutation ReorderEmailTemplateBlocks($templateId: ID!, $input: ReorderChipsInput!) {\n reorderEmailTemplateChips(templateId: $templateId, input: $input)\n }\n";
|
|
284
|
+
export declare const SET_EMAIL_TEMPLATE_BLOCK_CONTENT_MUTATION = "\n mutation SetEmailTemplateBlockContent($chipId: ID!, $input: UpsertChipTranslationInput!) {\n upsertEmailChipTranslation(chipId: $chipId, input: $input) { languageCode content }\n }\n";
|
|
285
|
+
export declare const SET_EMAIL_TEMPLATE_SUBJECT_MUTATION = "\n mutation SetEmailTemplateSubject($templateId: ID!, $input: UpsertSubjectInput!) {\n upsertEmailTemplateSubject(templateId: $templateId, input: $input) {\n languageCode\n subject { type value key }\n }\n }\n";
|
|
65
286
|
export declare const EMAIL_PREVIEW_QUERY = "\n query EmailPreview(\n $templateId: ID!\n $languageCode: String!\n $styleOverrides: UpdateEmailStyleConfigInput\n ) {\n renderEmailPreview(\n templateId: $templateId\n languageCode: $languageCode\n styleOverrides: $styleOverrides\n ) {\n subject\n html\n recipientTo\n recipientCc\n recipientBcc\n }\n }\n";
|
|
66
287
|
export declare function createEmailTemplatesModule(transport: Transport): EmailTemplatesModule;
|
|
@@ -1,11 +1,81 @@
|
|
|
1
1
|
import { NotFoundError } from '@sixtyninecommerce/core';
|
|
2
2
|
import { customerDataField } from '../customers/wire.js';
|
|
3
3
|
import { emailStyleInputOf } from './email-style.js';
|
|
4
|
+
const SEGMENT_FIELDS = 'type value key';
|
|
5
|
+
const BLOCK_FIELDS = `id type position config translations { languageCode content }`;
|
|
6
|
+
/**
|
|
7
|
+
* A block as the two block WRITES answer it: no `translations`. An engine before 2026-10-15 saves
|
|
8
|
+
* the chip and only then fails to answer that (non-null) relation, so a write selecting it would
|
|
9
|
+
* report a failure for a change that was stored, and a retry would add the block twice.
|
|
10
|
+
*/
|
|
11
|
+
const WRITTEN_BLOCK_FIELDS = 'id type position config';
|
|
12
|
+
export const EMAIL_TEMPLATE_CATALOG_QUERY = /* GraphQL */ `
|
|
13
|
+
query EmailTemplateCatalog {
|
|
14
|
+
emailTemplateCatalog {
|
|
15
|
+
templateTypes { type label description variables { key label } }
|
|
16
|
+
chipTypes { type label category }
|
|
17
|
+
recipientVariables { key label }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
export const EMAIL_TEMPLATE_BY_TYPE_QUERY = /* GraphQL */ `
|
|
22
|
+
query EmailTemplateByType($type: String!) {
|
|
23
|
+
emailTemplateByType(type: $type) {
|
|
24
|
+
id
|
|
25
|
+
type
|
|
26
|
+
enabled
|
|
27
|
+
recipientTo { ${SEGMENT_FIELDS} }
|
|
28
|
+
recipientCc { ${SEGMENT_FIELDS} }
|
|
29
|
+
recipientBcc { ${SEGMENT_FIELDS} }
|
|
30
|
+
chips { ${BLOCK_FIELDS} }
|
|
31
|
+
subjects { languageCode subject { ${SEGMENT_FIELDS} } }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
4
35
|
export const EMAIL_TEMPLATE_ID_BY_TYPE_QUERY = /* GraphQL */ `
|
|
5
36
|
query EmailTemplateIdByType($type: String!) {
|
|
6
37
|
emailTemplateByType(type: $type) { id }
|
|
7
38
|
}
|
|
8
39
|
`;
|
|
40
|
+
/** The look-up `blocks.add` makes: the id, and where the body ends. */
|
|
41
|
+
export const EMAIL_TEMPLATE_END_BY_TYPE_QUERY = /* GraphQL */ `
|
|
42
|
+
query EmailTemplateEndByType($type: String!) {
|
|
43
|
+
emailTemplateByType(type: $type) { id chips { position } }
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
export const ADD_EMAIL_TEMPLATE_BLOCK_MUTATION = /* GraphQL */ `
|
|
47
|
+
mutation AddEmailTemplateBlock($templateId: ID!, $input: AddChipInput!) {
|
|
48
|
+
addEmailTemplateChip(templateId: $templateId, input: $input) { ${WRITTEN_BLOCK_FIELDS} }
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
export const UPDATE_EMAIL_TEMPLATE_BLOCK_MUTATION = /* GraphQL */ `
|
|
52
|
+
mutation UpdateEmailTemplateBlock($chipId: ID!, $input: UpdateChipInput!) {
|
|
53
|
+
updateEmailTemplateChip(chipId: $chipId, input: $input) { ${WRITTEN_BLOCK_FIELDS} }
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
export const REMOVE_EMAIL_TEMPLATE_BLOCK_MUTATION = /* GraphQL */ `
|
|
57
|
+
mutation RemoveEmailTemplateBlock($chipId: ID!) {
|
|
58
|
+
removeEmailTemplateChip(chipId: $chipId)
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
export const REORDER_EMAIL_TEMPLATE_BLOCKS_MUTATION = /* GraphQL */ `
|
|
62
|
+
mutation ReorderEmailTemplateBlocks($templateId: ID!, $input: ReorderChipsInput!) {
|
|
63
|
+
reorderEmailTemplateChips(templateId: $templateId, input: $input)
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
export const SET_EMAIL_TEMPLATE_BLOCK_CONTENT_MUTATION = /* GraphQL */ `
|
|
67
|
+
mutation SetEmailTemplateBlockContent($chipId: ID!, $input: UpsertChipTranslationInput!) {
|
|
68
|
+
upsertEmailChipTranslation(chipId: $chipId, input: $input) { languageCode content }
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
export const SET_EMAIL_TEMPLATE_SUBJECT_MUTATION = /* GraphQL */ `
|
|
72
|
+
mutation SetEmailTemplateSubject($templateId: ID!, $input: UpsertSubjectInput!) {
|
|
73
|
+
upsertEmailTemplateSubject(templateId: $templateId, input: $input) {
|
|
74
|
+
languageCode
|
|
75
|
+
subject { ${SEGMENT_FIELDS} }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
9
79
|
export const EMAIL_PREVIEW_QUERY = /* GraphQL */ `
|
|
10
80
|
query EmailPreview(
|
|
11
81
|
$templateId: ID!
|
|
@@ -25,20 +95,167 @@ export const EMAIL_PREVIEW_QUERY = /* GraphQL */ `
|
|
|
25
95
|
}
|
|
26
96
|
}
|
|
27
97
|
`;
|
|
98
|
+
/** A part as stored, without the null of the field the other kind uses. */
|
|
99
|
+
function segmentOf(wire) {
|
|
100
|
+
const segment = { type: wire.type };
|
|
101
|
+
if (wire.value != null)
|
|
102
|
+
segment.value = wire.value;
|
|
103
|
+
if (wire.key != null)
|
|
104
|
+
segment.key = wire.key;
|
|
105
|
+
return segment;
|
|
106
|
+
}
|
|
107
|
+
/** A part as the engine takes it: only the fields the caller set. */
|
|
108
|
+
function segmentInputOf(segment) {
|
|
109
|
+
const input = { type: segment.type };
|
|
110
|
+
if (segment.value !== undefined)
|
|
111
|
+
input.value = segment.value;
|
|
112
|
+
if (segment.key !== undefined)
|
|
113
|
+
input.key = segment.key;
|
|
114
|
+
return input;
|
|
115
|
+
}
|
|
116
|
+
function contentOf(wire) {
|
|
117
|
+
return { languageCode: wire.languageCode, content: wire.content ?? {} };
|
|
118
|
+
}
|
|
119
|
+
function writtenBlockOf(wire) {
|
|
120
|
+
return {
|
|
121
|
+
id: String(wire.id),
|
|
122
|
+
type: wire.type,
|
|
123
|
+
position: wire.position,
|
|
124
|
+
config: wire.config ?? {},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function blockOf(wire) {
|
|
128
|
+
return { ...writtenBlockOf(wire), contents: (wire.translations ?? []).map(contentOf) };
|
|
129
|
+
}
|
|
130
|
+
function templateOf(wire) {
|
|
131
|
+
return {
|
|
132
|
+
id: String(wire.id),
|
|
133
|
+
type: wire.type,
|
|
134
|
+
enabled: wire.enabled,
|
|
135
|
+
recipients: {
|
|
136
|
+
to: wire.recipientTo.map(segmentOf),
|
|
137
|
+
cc: wire.recipientCc.map(segmentOf),
|
|
138
|
+
bcc: wire.recipientBcc.map(segmentOf),
|
|
139
|
+
},
|
|
140
|
+
// The engine orders them on its own rows; the inherited read builds them in code. Sorted
|
|
141
|
+
// here so the promise holds either way.
|
|
142
|
+
blocks: wire.chips.map(blockOf).sort((a, b) => a.position - b.position),
|
|
143
|
+
subjects: wire.subjects.map((row) => ({
|
|
144
|
+
languageCode: row.languageCode,
|
|
145
|
+
segments: row.subject.map(segmentOf),
|
|
146
|
+
})),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function templateNotFound(type) {
|
|
150
|
+
return new NotFoundError({
|
|
151
|
+
code: 'EMAIL_TEMPLATE_NOT_FOUND',
|
|
152
|
+
message: `The store has no email template of type "${type}".`,
|
|
153
|
+
status: 404,
|
|
154
|
+
service: 'vendure',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
28
157
|
export function createEmailTemplatesModule(transport) {
|
|
158
|
+
/** The id of the template of `type` the store uses, or the coded not-found. */
|
|
159
|
+
const templateIdOf = async (type, call) => {
|
|
160
|
+
const found = await transport.graphql({ api: 'admin', query: EMAIL_TEMPLATE_ID_BY_TYPE_QUERY, variables: { type } }, call);
|
|
161
|
+
const templateId = found.emailTemplateByType?.id;
|
|
162
|
+
if (!templateId)
|
|
163
|
+
throw templateNotFound(type);
|
|
164
|
+
return String(templateId);
|
|
165
|
+
};
|
|
166
|
+
const blocks = {
|
|
167
|
+
async add(templateType, block, options) {
|
|
168
|
+
let templateId;
|
|
169
|
+
let position = block.position;
|
|
170
|
+
if (position === undefined) {
|
|
171
|
+
// "At the end" is after the LAST position, not the count: positions may have gaps.
|
|
172
|
+
const found = await transport.graphql({
|
|
173
|
+
api: 'admin',
|
|
174
|
+
query: EMAIL_TEMPLATE_END_BY_TYPE_QUERY,
|
|
175
|
+
variables: { type: templateType },
|
|
176
|
+
}, options);
|
|
177
|
+
if (!found.emailTemplateByType)
|
|
178
|
+
throw templateNotFound(templateType);
|
|
179
|
+
templateId = String(found.emailTemplateByType.id);
|
|
180
|
+
position = found.emailTemplateByType.chips.reduce((end, chip) => Math.max(end, chip.position + 1), 0);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
templateId = await templateIdOf(templateType, options);
|
|
184
|
+
}
|
|
185
|
+
const data = await transport.graphql({
|
|
186
|
+
api: 'admin',
|
|
187
|
+
query: ADD_EMAIL_TEMPLATE_BLOCK_MUTATION,
|
|
188
|
+
variables: {
|
|
189
|
+
templateId,
|
|
190
|
+
input: {
|
|
191
|
+
type: block.type,
|
|
192
|
+
position,
|
|
193
|
+
...(block.config !== undefined ? { config: block.config } : {}),
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
}, options);
|
|
197
|
+
// The engine writes no texts for a new block (`addChip`), so it has none to answer.
|
|
198
|
+
return { ...writtenBlockOf(data.addEmailTemplateChip), contents: [] };
|
|
199
|
+
},
|
|
200
|
+
async update(blockId, patch, options) {
|
|
201
|
+
const data = await transport.graphql({
|
|
202
|
+
api: 'admin',
|
|
203
|
+
query: UPDATE_EMAIL_TEMPLATE_BLOCK_MUTATION,
|
|
204
|
+
variables: { chipId: blockId, input: { config: patch.config } },
|
|
205
|
+
}, options);
|
|
206
|
+
return writtenBlockOf(data.updateEmailTemplateChip);
|
|
207
|
+
},
|
|
208
|
+
async remove(blockId, options) {
|
|
209
|
+
await transport.graphql({
|
|
210
|
+
api: 'admin',
|
|
211
|
+
query: REMOVE_EMAIL_TEMPLATE_BLOCK_MUTATION,
|
|
212
|
+
variables: { chipId: blockId },
|
|
213
|
+
}, options);
|
|
214
|
+
},
|
|
215
|
+
async reorder(templateType, blockIds, options) {
|
|
216
|
+
const templateId = await templateIdOf(templateType, options);
|
|
217
|
+
await transport.graphql({
|
|
218
|
+
api: 'admin',
|
|
219
|
+
query: REORDER_EMAIL_TEMPLATE_BLOCKS_MUTATION,
|
|
220
|
+
variables: { templateId, input: { chipIds: [...blockIds] } },
|
|
221
|
+
}, options);
|
|
222
|
+
},
|
|
223
|
+
async setContent(blockId, languageCode, content, options) {
|
|
224
|
+
const data = await transport.graphql({
|
|
225
|
+
api: 'admin',
|
|
226
|
+
query: SET_EMAIL_TEMPLATE_BLOCK_CONTENT_MUTATION,
|
|
227
|
+
variables: { chipId: blockId, input: { languageCode, content } },
|
|
228
|
+
}, options);
|
|
229
|
+
return contentOf(data.upsertEmailChipTranslation);
|
|
230
|
+
},
|
|
231
|
+
};
|
|
29
232
|
return {
|
|
233
|
+
async catalog(options) {
|
|
234
|
+
const data = await transport.graphql({ api: 'admin', query: EMAIL_TEMPLATE_CATALOG_QUERY }, options);
|
|
235
|
+
const { templateTypes, chipTypes, recipientVariables } = data.emailTemplateCatalog;
|
|
236
|
+
return { templateTypes, blockTypes: chipTypes, recipientVariables };
|
|
237
|
+
},
|
|
238
|
+
async getByType(type, options) {
|
|
239
|
+
const data = await transport.graphql({ api: 'admin', query: EMAIL_TEMPLATE_BY_TYPE_QUERY, variables: { type } }, options);
|
|
240
|
+
return data.emailTemplateByType ? templateOf(data.emailTemplateByType) : null;
|
|
241
|
+
},
|
|
242
|
+
async setSubject(templateType, languageCode, segments, options) {
|
|
243
|
+
const templateId = await templateIdOf(templateType, options);
|
|
244
|
+
const data = await transport.graphql({
|
|
245
|
+
api: 'admin',
|
|
246
|
+
query: SET_EMAIL_TEMPLATE_SUBJECT_MUTATION,
|
|
247
|
+
variables: {
|
|
248
|
+
templateId,
|
|
249
|
+
input: { languageCode, subject: segments.map(segmentInputOf) },
|
|
250
|
+
},
|
|
251
|
+
}, options);
|
|
252
|
+
const saved = data.upsertEmailTemplateSubject;
|
|
253
|
+
return { languageCode: saved.languageCode, segments: saved.subject.map(segmentOf) };
|
|
254
|
+
},
|
|
255
|
+
blocks,
|
|
30
256
|
async preview(type, options) {
|
|
31
257
|
const { languageCode, style, ...call } = options;
|
|
32
|
-
const
|
|
33
|
-
const templateId = found.emailTemplateByType?.id;
|
|
34
|
-
if (!templateId) {
|
|
35
|
-
throw new NotFoundError({
|
|
36
|
-
code: 'EMAIL_TEMPLATE_NOT_FOUND',
|
|
37
|
-
message: `The store has no email template of type "${type}".`,
|
|
38
|
-
status: 404,
|
|
39
|
-
service: 'vendure',
|
|
40
|
-
});
|
|
41
|
-
}
|
|
258
|
+
const templateId = await templateIdOf(type, call);
|
|
42
259
|
const { value, mark } = await customerDataField(transport, EMAIL_PREVIEW_QUERY, 'renderEmailPreview', {
|
|
43
260
|
templateId,
|
|
44
261
|
languageCode,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sixtyninecommerce/admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Back-office SDK for sixtynine-commerce: every capability of the platform as a method",
|
|
5
5
|
"homepage": "https://github.com/SIXTYNINE-digital/sixtynine-commerce-engine/tree/main/packages/admin-client#readme",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@sixtyninecommerce/core": "^0.
|
|
36
|
+
"@sixtyninecommerce/core": "^0.7.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "22.19.7",
|