@passly-nl/data 1.0.1 → 1.0.3

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.
Files changed (41) hide show
  1. package/dist/index.d.mts +185 -8
  2. package/dist/index.d.mts.map +1 -1
  3. package/dist/index.mjs +11 -1
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +6 -6
  6. package/src/adapter/EmailTemplateAdapter.ts +25 -0
  7. package/src/adapter/EventAdapter.ts +6 -1
  8. package/src/adapter/MerchantAdapter.ts +1 -0
  9. package/src/adapter/ProductAdapter.ts +3 -2
  10. package/src/adapter/PublicShopAdapter.ts +11 -2
  11. package/src/adapter/index.ts +1 -0
  12. package/src/dto/emailTemplate/EmailTemplateDto.ts +80 -0
  13. package/src/dto/emailTemplate/RenderedMailDto.ts +28 -0
  14. package/src/dto/emailTemplate/index.ts +2 -0
  15. package/src/dto/event/ShopElementInformationDto.ts +9 -0
  16. package/src/dto/event/index.ts +1 -0
  17. package/src/dto/index.ts +1 -0
  18. package/src/dto/merchant/MerchantUserDto.ts +12 -1
  19. package/src/dto/product/ProductDto.ts +12 -1
  20. package/src/dto/publicShop/PublicShopElementInformationDto.ts +9 -0
  21. package/src/dto/publicShop/PublicShopEventDto.ts +42 -1
  22. package/src/dto/publicShop/index.ts +1 -0
  23. package/src/service/AdminMerchantService.ts +15 -0
  24. package/src/service/AdminMerchantsService.ts +17 -0
  25. package/src/service/AdminService.ts +4 -0
  26. package/src/service/EmailTemplateEditorService.ts +285 -0
  27. package/src/service/MerchantContractService.ts +60 -0
  28. package/src/service/MerchantEmailTemplateService.ts +43 -0
  29. package/src/service/MerchantEmailTemplatesService.ts +31 -0
  30. package/src/service/MerchantEventEmailTemplateService.ts +43 -0
  31. package/src/service/MerchantEventEmailTemplatesService.ts +31 -0
  32. package/src/service/MerchantEventProductService.ts +3 -1
  33. package/src/service/MerchantEventProductsService.ts +4 -2
  34. package/src/service/MerchantUserService.ts +38 -0
  35. package/src/service/index.ts +10 -0
  36. package/src/types/auth.ts +3 -0
  37. package/src/types/emailTemplate.ts +10 -0
  38. package/src/types/emailTemplateEditor.ts +63 -0
  39. package/src/types/event.ts +1 -0
  40. package/src/types/index.ts +2 -0
  41. package/src/types/publicShop.ts +1 -0
@@ -0,0 +1,285 @@
1
+ import type { EmailTemplateType } from '../types/emailTemplate';
2
+ import type {
3
+ EmailTemplateEditorBlock,
4
+ EmailTemplateEditorButtonBlock,
5
+ EmailTemplateEditorCondition,
6
+ EmailTemplateEditorContext,
7
+ EmailTemplateEditorH1Block,
8
+ EmailTemplateEditorHeaderBlock,
9
+ EmailTemplateEditorIfBlock,
10
+ EmailTemplateEditorParagraphBlock,
11
+ EmailTemplateEditorVariable
12
+ } from '../types/emailTemplateEditor';
13
+
14
+ const SHARED_VARIABLES: EmailTemplateEditorVariable[] = [
15
+ {label: 'variable.merchantName', path: 'merchant.name'},
16
+ {label: 'variable.weekDay', path: 'weekDay'}
17
+ ];
18
+
19
+ const BUYER_VARIABLES: EmailTemplateEditorVariable[] = [
20
+ {label: 'variable.buyerFirstName', path: 'buyer.firstName'},
21
+ {label: 'variable.buyerLastName', path: 'buyer.lastName'},
22
+ {label: 'variable.buyerFullName', path: 'buyer.fullName'},
23
+ {label: 'variable.buyerInitials', path: 'buyer.initials'},
24
+ {label: 'variable.buyerEmail', path: 'buyer.email'},
25
+ {label: 'variable.buyerPhoneNumber', path: 'buyer.phoneNumber'},
26
+ {label: 'variable.buyerDateOfBirth', path: 'buyer.dateOfBirth'},
27
+ {label: 'variable.buyerGender', path: 'buyer.gender'},
28
+ {label: 'variable.buyerLanguage', path: 'buyer.language'},
29
+ {label: 'variable.buyerAddressStreet', path: 'buyer.address.street'},
30
+ {label: 'variable.buyerAddressNumber', path: 'buyer.address.number'},
31
+ {label: 'variable.buyerAddressPostalCode', path: 'buyer.address.postalCode'},
32
+ {label: 'variable.buyerAddressCity', path: 'buyer.address.city'},
33
+ {label: 'variable.buyerAddressCountry', path: 'buyer.address.country'}
34
+ ];
35
+
36
+ const EVENT_VARIABLES: EmailTemplateEditorVariable[] = [
37
+ {label: 'variable.eventName', path: 'event.name'},
38
+ {label: 'variable.eventStartDate', path: 'event.startDate'},
39
+ {label: 'variable.eventStartTime', path: 'event.startTime'}
40
+ ];
41
+
42
+ const ORDER_VARIABLES: EmailTemplateEditorVariable[] = [
43
+ {label: 'variable.orderCode', path: 'order.code'}
44
+ ];
45
+
46
+ const HEADER_CONDITION: EmailTemplateEditorCondition = {label: 'condition.eventHasHeaderImage', condition: 'event.headerFile'};
47
+
48
+ export const EMAIL_TEMPLATE_EDITOR_CONTEXTS: Record<EmailTemplateType, EmailTemplateEditorContext> = {
49
+ merchant_invite: {
50
+ variables: [
51
+ ...SHARED_VARIABLES,
52
+ {label: 'variable.userFirstName', path: 'user.firstName'},
53
+ {label: 'variable.userLastName', path: 'user.lastName'},
54
+ {label: 'variable.acceptUrl', path: 'acceptUrl'}
55
+ ],
56
+ conditions: []
57
+ },
58
+ order_confirmation: {
59
+ variables: [...SHARED_VARIABLES, ...BUYER_VARIABLES, ...EVENT_VARIABLES, ...ORDER_VARIABLES],
60
+ conditions: [
61
+ {label: 'condition.hasNormalTicketsOnly', condition: 'hasNormalTicketsOnly'},
62
+ {label: 'condition.hasPersonalizableTickets', condition: 'hasPersonalizableTickets'},
63
+ {label: 'condition.hasSealedTickets', condition: 'hasSealedTickets'},
64
+ HEADER_CONDITION
65
+ ]
66
+ },
67
+ personalization_invite: {
68
+ variables: [...SHARED_VARIABLES, ...BUYER_VARIABLES, ...EVENT_VARIABLES, {label: 'variable.personalizeUrl', path: 'personalizeUrl'}],
69
+ conditions: [HEADER_CONDITION]
70
+ },
71
+ personalization_request: {
72
+ variables: [...SHARED_VARIABLES, ...BUYER_VARIABLES, ...EVENT_VARIABLES, ...ORDER_VARIABLES, {label: 'variable.personalizeUrl', path: 'personalizeUrl'}],
73
+ conditions: [HEADER_CONDITION]
74
+ },
75
+ tickets: {
76
+ variables: [...SHARED_VARIABLES, ...BUYER_VARIABLES, ...EVENT_VARIABLES, ...ORDER_VARIABLES],
77
+ conditions: [HEADER_CONDITION]
78
+ }
79
+ };
80
+
81
+ interface ScanResult {
82
+ blocks: EmailTemplateEditorBlock[];
83
+ consumed: number;
84
+ }
85
+
86
+ /**
87
+ * Parses quoted or unquoted arguments from a Handlebars helper argument string.
88
+ * Returns an array of string values.
89
+ */
90
+ function parseArgs(args: string): string[] {
91
+ const result: string[] = [];
92
+ let remaining = args.trim();
93
+
94
+ while (remaining.length > 0) {
95
+ if (remaining[0] === '"') {
96
+ const end = remaining.indexOf('"', 1);
97
+ if (end === -1) break;
98
+ result.push(remaining.slice(1, end));
99
+ remaining = remaining.slice(end + 1).trim();
100
+ } else {
101
+ const spaceIdx = remaining.search(/\s/);
102
+ if (spaceIdx === -1) {
103
+ result.push(remaining);
104
+ break;
105
+ }
106
+ result.push(remaining.slice(0, spaceIdx));
107
+ remaining = remaining.slice(spaceIdx + 1).trim();
108
+ }
109
+ }
110
+
111
+ return result;
112
+ }
113
+
114
+ /**
115
+ * Builds a single EmailTemplateEditorBlock from a parsed Handlebars block.
116
+ */
117
+ function buildBlock(
118
+ type: string,
119
+ args: string,
120
+ children: EmailTemplateEditorBlock[],
121
+ rawContent: string
122
+ ): EmailTemplateEditorBlock | null {
123
+ const content = rawContent.trim().split('\n').map(line => line.replace(/^ {4}/, '')).join('\n');
124
+
125
+ switch (type) {
126
+ case 'h1':
127
+ return {type: 'h1', content} satisfies EmailTemplateEditorH1Block;
128
+
129
+ case 'paragraph':
130
+ return {type: 'paragraph', content} satisfies EmailTemplateEditorParagraphBlock;
131
+
132
+ case 'divider':
133
+ return {type: 'divider'};
134
+
135
+ case 'spacer':
136
+ return {type: 'spacer'};
137
+
138
+ case 'logo':
139
+ return {type: 'logo'};
140
+
141
+ case 'button': {
142
+ const [label = '', url = ''] = parseArgs(args);
143
+ return {type: 'button', label, url} satisfies EmailTemplateEditorButtonBlock;
144
+ }
145
+
146
+ case 'header': {
147
+ const [src = ''] = parseArgs(args);
148
+ return {type: 'header', src} satisfies EmailTemplateEditorHeaderBlock;
149
+ }
150
+
151
+ case 'if':
152
+ return {type: 'if', condition: args, children} satisfies EmailTemplateEditorIfBlock;
153
+
154
+ default:
155
+ return null;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Recursively scans the template string starting from startPos, building blocks
161
+ * until a closing tag is found or the end of the string is reached.
162
+ */
163
+ function scan(template: string, startPos: number = 0): ScanResult {
164
+ const blocks: EmailTemplateEditorBlock[] = [];
165
+ let pos = startPos;
166
+
167
+ while (pos < template.length) {
168
+ const tokenStart = template.indexOf('{{', pos);
169
+ if (tokenStart === -1) break;
170
+
171
+ const ch = template[tokenStart + 2];
172
+
173
+ // Partial {{> ...}} — skip
174
+ if (ch === '>') {
175
+ const end = template.indexOf('}}', tokenStart);
176
+ if (end === -1) break;
177
+ pos = end + 2;
178
+ continue;
179
+ }
180
+
181
+ // Comment {{!-- ... --}} or {{! ...}} — skip
182
+ if (ch === '!') {
183
+ const end = template.indexOf('}}', tokenStart);
184
+ if (end === -1) break;
185
+ pos = end + 2;
186
+ continue;
187
+ }
188
+
189
+ // Closing tag {{/type}} — return to parent scanner
190
+ if (ch === '/') {
191
+ return {blocks, consumed: tokenStart};
192
+ }
193
+
194
+ // Opening block tag {{#type args}}
195
+ if (ch === '#') {
196
+ const closeTag = template.indexOf('}}', tokenStart);
197
+ if (closeTag === -1) break;
198
+
199
+ const tagContent = template.slice(tokenStart + 3, closeTag).trim();
200
+ const spaceIdx = tagContent.search(/\s/);
201
+ const type = spaceIdx === -1 ? tagContent : tagContent.slice(0, spaceIdx);
202
+ const args = spaceIdx === -1 ? '' : tagContent.slice(spaceIdx + 1).trim();
203
+
204
+ const innerStart = closeTag + 2;
205
+ const {blocks: children, consumed: innerEnd} = scan(template, innerStart);
206
+ const rawContent = template.slice(innerStart, innerEnd);
207
+
208
+ // Advance past the closing {{/type}} tag
209
+ const expectedClose = `{{/${type}}}`;
210
+ pos = innerEnd + expectedClose.length;
211
+
212
+ const block = buildBlock(type, args, children, rawContent);
213
+ if (block !== null) {
214
+ blocks.push(block);
215
+ }
216
+ continue;
217
+ }
218
+
219
+ // Variable or other expression — skip
220
+ const end = template.indexOf('}}', tokenStart);
221
+ if (end === -1) break;
222
+ pos = end + 2;
223
+ }
224
+
225
+ return {blocks, consumed: pos};
226
+ }
227
+
228
+ /**
229
+ * Serializes a single block back to its Handlebars string representation.
230
+ */
231
+ function serializeBlock(block: EmailTemplateEditorBlock): string {
232
+ switch (block.type) {
233
+ case 'h1': {
234
+ const indented = block.content.split('\n').map(line => ` ${line}`).join('\n');
235
+ return `{{#h1}}\n${indented}\n{{/h1}}`;
236
+ }
237
+
238
+ case 'paragraph': {
239
+ const indented = block.content.split('\n').map(line => ` ${line}`).join('\n');
240
+ return `{{#paragraph}}\n${indented}\n{{/paragraph}}`;
241
+ }
242
+
243
+ case 'divider':
244
+ return `{{#divider}}{{/divider}}`;
245
+
246
+ case 'spacer':
247
+ return `{{#spacer}}{{/spacer}}`;
248
+
249
+ case 'logo':
250
+ return `{{#logo}}{{/logo}}`;
251
+
252
+ case 'button':
253
+ return `{{#button "${block.label}" "${block.url}"}}{{/button}}`;
254
+
255
+ case 'header': {
256
+ const srcArg = block.src.includes('://') ? `"${block.src}"` : block.src;
257
+ return `{{#header ${srcArg}}}{{/header}}`;
258
+ }
259
+
260
+ case 'if': {
261
+ const indented = serializeBlocks(block.children)
262
+ .split('\n')
263
+ .map(line => ` ${line}`)
264
+ .join('\n');
265
+ return `{{#if ${block.condition}}}\n${indented}\n{{/if}}`;
266
+ }
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Parses a Handlebars template string into an array of editor blocks.
272
+ * Strips structure partials; only content blocks are returned.
273
+ */
274
+ export function parseEmailTemplate(template: string): EmailTemplateEditorBlock[] {
275
+ const {blocks} = scan(template);
276
+ return blocks;
277
+ }
278
+
279
+ /**
280
+ * Serializes an array of editor blocks back into a Handlebars content string.
281
+ * Does not include structure partials — those are added by the backend automatically.
282
+ */
283
+ export function serializeBlocks(blocks: EmailTemplateEditorBlock[]): string {
284
+ return blocks.map(serializeBlock).join('\n\n');
285
+ }
@@ -0,0 +1,60 @@
1
+ import { BaseResponse, BaseService, Paginated, QueryString } from '@basmilius/http-client';
2
+ import { MerchantAdapter } from '#data/adapter';
3
+ import type { ContractDto } from '#data/dto';
4
+ import type { DateTime } from 'luxon';
5
+
6
+ export class MerchantContractService extends BaseService {
7
+ async get(merchantId: string, offset: number, limit: number): Promise<BaseResponse<Paginated<ContractDto>>> {
8
+ return await this
9
+ .request(`/merchants/${merchantId}/contracts`)
10
+ .method('get')
11
+ .bearerToken()
12
+ .queryString(QueryString.builder()
13
+ .append('language', 'nl')
14
+ .append('offset', offset)
15
+ .append('limit', limit))
16
+ .runPaginatedAdapter(MerchantAdapter.parseContract);
17
+ }
18
+
19
+ async getSingle(merchantId: string, contractId: string): Promise<BaseResponse<ContractDto>> {
20
+ return await this
21
+ .request(`/merchants/${merchantId}/contracts/${contractId}`)
22
+ .method('get')
23
+ .bearerToken()
24
+ .queryString(QueryString.builder()
25
+ .append('language', 'nl'))
26
+ .runAdapter(MerchantAdapter.parseContract);
27
+ }
28
+
29
+ async post(merchantId: string, startsOn: DateTime, endsOn: DateTime, feeCents: number, remark: string): Promise<BaseResponse<ContractDto>> {
30
+ return await this
31
+ .request(`/merchants/${merchantId}/contracts`)
32
+ .method('post')
33
+ .bearerToken()
34
+ .queryString(QueryString.builder()
35
+ .append('language', 'nl'))
36
+ .body({
37
+ starts_on: startsOn.toISO(),
38
+ ends_on: endsOn.toISO(),
39
+ fee_cents: feeCents,
40
+ remark: remark || undefined
41
+ })
42
+ .runAdapter(MerchantAdapter.parseContract);
43
+ }
44
+
45
+ async put(merchantId: string, contractId: string, startsOn: DateTime, endsOn: DateTime, feeCents: number, remark: string): Promise<BaseResponse<ContractDto>> {
46
+ return await this
47
+ .request(`/merchants/${merchantId}/contracts/${contractId}`)
48
+ .method('put')
49
+ .bearerToken()
50
+ .queryString(QueryString.builder()
51
+ .append('language', 'nl'))
52
+ .body({
53
+ starts_on: startsOn.toISO(),
54
+ ends_on: endsOn.toISO(),
55
+ fee_cents: feeCents,
56
+ remark: remark || undefined
57
+ })
58
+ .runAdapter(MerchantAdapter.parseContract);
59
+ }
60
+ }
@@ -0,0 +1,43 @@
1
+ import { BaseResponse, BaseService } from '@basmilius/http-client';
2
+ import { CommonAdapter, EmailTemplateAdapter } from '#data/adapter';
3
+ import type { EmailTemplateDto, RenderedMailDto, StatusResponseDto } from '#data/dto';
4
+ import type { EmailTemplateLanguage } from '#data/types';
5
+
6
+ export class MerchantEmailTemplateService extends BaseService {
7
+ async getPreview(merchantId: string, emailTemplateId: string): Promise<BaseResponse<RenderedMailDto>> {
8
+ return await this
9
+ .request(`/merchants/${merchantId}/email-templates/${emailTemplateId}/preview`)
10
+ .method('get')
11
+ .bearerToken()
12
+ .runAdapter(EmailTemplateAdapter.parseRenderedMail);
13
+ }
14
+
15
+ async get(merchantId: string, emailTemplateId: string): Promise<BaseResponse<EmailTemplateDto>> {
16
+ return await this
17
+ .request(`/merchants/${merchantId}/email-templates/${emailTemplateId}`)
18
+ .method('get')
19
+ .bearerToken()
20
+ .runAdapter(EmailTemplateAdapter.parseEmailTemplate);
21
+ }
22
+
23
+ async patch(merchantId: string, emailTemplateId: string, subject: string, content: string, language: EmailTemplateLanguage | null = null): Promise<BaseResponse<EmailTemplateDto>> {
24
+ return await this
25
+ .request(`/merchants/${merchantId}/email-templates/${emailTemplateId}`)
26
+ .method('patch')
27
+ .bearerToken()
28
+ .body({
29
+ subject,
30
+ content,
31
+ language
32
+ })
33
+ .runAdapter(EmailTemplateAdapter.parseEmailTemplate);
34
+ }
35
+
36
+ async delete(merchantId: string, emailTemplateId: string): Promise<BaseResponse<StatusResponseDto>> {
37
+ return await this
38
+ .request(`/merchants/${merchantId}/email-templates/${emailTemplateId}`)
39
+ .method('delete')
40
+ .bearerToken()
41
+ .runAdapter(CommonAdapter.parseStatusResponse);
42
+ }
43
+ }
@@ -0,0 +1,31 @@
1
+ import { BaseResponse, BaseService, Paginated, QueryString } from '@basmilius/http-client';
2
+ import { EmailTemplateAdapter } from '#data/adapter';
3
+ import type { EmailTemplateDto } from '#data/dto';
4
+ import type { EmailTemplateLanguage, EmailTemplateType } from '#data/types';
5
+
6
+ export class MerchantEmailTemplatesService extends BaseService {
7
+ async get(merchantId: string, offset: number, limit: number): Promise<BaseResponse<Paginated<EmailTemplateDto>>> {
8
+ return await this
9
+ .request(`/merchants/${merchantId}/email-templates`)
10
+ .method('get')
11
+ .bearerToken()
12
+ .queryString(QueryString.builder()
13
+ .append('offset', offset)
14
+ .append('limit', limit))
15
+ .runPaginatedAdapter(EmailTemplateAdapter.parseEmailTemplate);
16
+ }
17
+
18
+ async post(merchantId: string, type: EmailTemplateType, subject: string, content: string, language: EmailTemplateLanguage | null = null): Promise<BaseResponse<EmailTemplateDto>> {
19
+ return await this
20
+ .request(`/merchants/${merchantId}/email-templates`)
21
+ .method('post')
22
+ .bearerToken()
23
+ .body({
24
+ type,
25
+ subject,
26
+ content,
27
+ language
28
+ })
29
+ .runAdapter(EmailTemplateAdapter.parseEmailTemplate);
30
+ }
31
+ }
@@ -0,0 +1,43 @@
1
+ import { BaseResponse, BaseService } from '@basmilius/http-client';
2
+ import { CommonAdapter, EmailTemplateAdapter } from '#data/adapter';
3
+ import type { EmailTemplateDto, RenderedMailDto, StatusResponseDto } from '#data/dto';
4
+ import type { EmailTemplateLanguage } from '#data/types';
5
+
6
+ export class MerchantEventEmailTemplateService extends BaseService {
7
+ async getPreview(merchantId: string, eventId: string, emailTemplateId: string): Promise<BaseResponse<RenderedMailDto>> {
8
+ return await this
9
+ .request(`/merchants/${merchantId}/events/${eventId}/email-templates/${emailTemplateId}/preview`)
10
+ .method('get')
11
+ .bearerToken()
12
+ .runAdapter(EmailTemplateAdapter.parseRenderedMail);
13
+ }
14
+
15
+ async get(merchantId: string, eventId: string, emailTemplateId: string): Promise<BaseResponse<EmailTemplateDto>> {
16
+ return await this
17
+ .request(`/merchants/${merchantId}/events/${eventId}/email-templates/${emailTemplateId}`)
18
+ .method('get')
19
+ .bearerToken()
20
+ .runAdapter(EmailTemplateAdapter.parseEmailTemplate);
21
+ }
22
+
23
+ async patch(merchantId: string, eventId: string, emailTemplateId: string, subject: string, content: string, language: EmailTemplateLanguage | null = null): Promise<BaseResponse<EmailTemplateDto>> {
24
+ return await this
25
+ .request(`/merchants/${merchantId}/events/${eventId}/email-templates/${emailTemplateId}`)
26
+ .method('patch')
27
+ .bearerToken()
28
+ .body({
29
+ subject,
30
+ content,
31
+ language
32
+ })
33
+ .runAdapter(EmailTemplateAdapter.parseEmailTemplate);
34
+ }
35
+
36
+ async delete(merchantId: string, eventId: string, emailTemplateId: string): Promise<BaseResponse<StatusResponseDto>> {
37
+ return await this
38
+ .request(`/merchants/${merchantId}/events/${eventId}/email-templates/${emailTemplateId}`)
39
+ .method('delete')
40
+ .bearerToken()
41
+ .runAdapter(CommonAdapter.parseStatusResponse);
42
+ }
43
+ }
@@ -0,0 +1,31 @@
1
+ import { BaseResponse, BaseService, Paginated, QueryString } from '@basmilius/http-client';
2
+ import { EmailTemplateAdapter } from '#data/adapter';
3
+ import type { EmailTemplateDto } from '#data/dto';
4
+ import type { EmailTemplateLanguage, EmailTemplateType } from '#data/types';
5
+
6
+ export class MerchantEventEmailTemplatesService extends BaseService {
7
+ async get(merchantId: string, eventId: string, offset: number, limit: number): Promise<BaseResponse<Paginated<EmailTemplateDto>>> {
8
+ return await this
9
+ .request(`/merchants/${merchantId}/events/${eventId}/email-templates`)
10
+ .method('get')
11
+ .bearerToken()
12
+ .queryString(QueryString.builder()
13
+ .append('offset', offset)
14
+ .append('limit', limit))
15
+ .runPaginatedAdapter(EmailTemplateAdapter.parseEmailTemplate);
16
+ }
17
+
18
+ async post(merchantId: string, eventId: string, type: EmailTemplateType, subject: string, content: string, language: EmailTemplateLanguage | null = null): Promise<BaseResponse<EmailTemplateDto>> {
19
+ return await this
20
+ .request(`/merchants/${merchantId}/events/${eventId}/email-templates`)
21
+ .method('post')
22
+ .bearerToken()
23
+ .body({
24
+ type,
25
+ subject,
26
+ content,
27
+ language
28
+ })
29
+ .runAdapter(EmailTemplateAdapter.parseEmailTemplate);
30
+ }
31
+ }
@@ -1,4 +1,5 @@
1
1
  import { BaseResponse, BaseService, QueryString } from '@basmilius/http-client';
2
+ import type { DateTime } from 'luxon';
2
3
  import { ProductAdapter } from '#data/adapter';
3
4
  import type { ProductDto } from '#data/dto';
4
5
 
@@ -39,7 +40,8 @@ export class MerchantEventProductService extends BaseService {
39
40
  description: product.description,
40
41
  price: product.price.cents,
41
42
  max_quantity: product.maxQuantity,
42
- is_swappable: product.isSwappable
43
+ is_swappable: product.isSwappable,
44
+ tickets_released_on: product.ticketsReleasedOn?.toISO() ?? null
43
45
  })
44
46
  .runAdapter(ProductAdapter.parseProduct);
45
47
  }
@@ -1,5 +1,6 @@
1
1
  import { BaseResponse, BaseService, Paginated, QueryString } from '@basmilius/http-client';
2
2
  import type { FluxFormSelectEntry } from '@flux-ui/types';
3
+ import type { DateTime } from 'luxon';
3
4
  import { FluxAdapter, ProductAdapter } from '#data/adapter';
4
5
  import type { ProductDto } from '#data/dto';
5
6
 
@@ -28,7 +29,7 @@ export class MerchantEventProductsService extends BaseService {
28
29
  .runArrayAdapter(FluxAdapter.parseFluxFormSelectEntry);
29
30
  }
30
31
 
31
- async post(merchantId: string, eventId: string, name: string, description: string, price: number, maxQuantity: number, stock: number | null, stockPoolId: string | null): Promise<BaseResponse<ProductDto>> {
32
+ async post(merchantId: string, eventId: string, name: string, description: string, price: number, maxQuantity: number, stock: number | null, stockPoolId: string | null, ticketsReleasedOn: DateTime | null = null): Promise<BaseResponse<ProductDto>> {
32
33
  return await this
33
34
  .request(`/merchants/${merchantId}/events/${eventId}/products`)
34
35
  .method('post')
@@ -41,7 +42,8 @@ export class MerchantEventProductsService extends BaseService {
41
42
  price,
42
43
  stock,
43
44
  max_quantity: maxQuantity,
44
- stock_pool_id: stockPoolId
45
+ stock_pool_id: stockPoolId,
46
+ tickets_released_on: ticketsReleasedOn?.toISO() ?? null
45
47
  })
46
48
  .runAdapter(ProductAdapter.parseProduct);
47
49
  }
@@ -0,0 +1,38 @@
1
+ import { BaseResponse, BaseService, QueryString } from '@basmilius/http-client';
2
+ import { MerchantAdapter } from '#data/adapter';
3
+ import type { MerchantUserDto } from '#data/dto';
4
+ import type { Claim } from '#data/types';
5
+
6
+ export class MerchantUserService extends BaseService {
7
+ async get(merchantId: string, userId: string): Promise<BaseResponse<MerchantUserDto>> {
8
+ return await this
9
+ .request(`/merchants/${merchantId}/users/${userId}`)
10
+ .method('get')
11
+ .bearerToken()
12
+ .queryString(QueryString.builder()
13
+ .append('language', 'nl'))
14
+ .runAdapter(MerchantAdapter.parseMerchantUser);
15
+ }
16
+
17
+ async put(merchantId: string, userId: string, isManager: boolean, claims: Claim[]): Promise<BaseResponse<MerchantUserDto>> {
18
+ return await this
19
+ .request(`/merchants/${merchantId}/users/${userId}`)
20
+ .method('put')
21
+ .bearerToken()
22
+ .queryString(QueryString.builder()
23
+ .append('language', 'nl'))
24
+ .body({
25
+ is_manager: isManager,
26
+ claims
27
+ })
28
+ .runAdapter(MerchantAdapter.parseMerchantUser);
29
+ }
30
+
31
+ async remove(merchantId: string, userId: string): Promise<BaseResponse<void>> {
32
+ return await this
33
+ .request(`/merchants/${merchantId}/users/${userId}`)
34
+ .method('delete')
35
+ .bearerToken()
36
+ .runEmpty();
37
+ }
38
+ }
@@ -1,7 +1,11 @@
1
+ export * from './AdminService';
2
+ export * from './AdminMerchantService';
3
+ export * from './AdminMerchantsService';
1
4
  export * from './AuthService';
2
5
  export * from './InvitationService';
3
6
  export * from './MerchantService';
4
7
  export * from './MerchantsService';
8
+ export * from './MerchantContractService';
5
9
  export * from './MerchantBuyerService';
6
10
  export * from './MerchantBuyersService';
7
11
  export * from './MerchantDashboardService';
@@ -16,6 +20,10 @@ export * from './MerchantEventShopsService';
16
20
  export * from './MerchantEventStockPoolsService';
17
21
  export * from './MerchantEventTicketTemplateService';
18
22
  export * from './MerchantEventTicketTemplatesService';
23
+ export * from './MerchantEmailTemplateService';
24
+ export * from './MerchantEmailTemplatesService';
25
+ export * from './MerchantEventEmailTemplateService';
26
+ export * from './MerchantEventEmailTemplatesService';
19
27
  export * from './MerchantEventsService';
20
28
  export * from './MerchantFinanceInvoiceService';
21
29
  export * from './MerchantFinanceInvoicesService';
@@ -31,10 +39,12 @@ export * from './MerchantStatisticsOverviewService';
31
39
  export * from './MerchantStatisticsSalesService';
32
40
  export * from './MerchantTicketService';
33
41
  export * from './MerchantTicketsService';
42
+ export * from './MerchantUserService';
34
43
  export * from './MerchantUsersService';
35
44
  export * from './MeService';
36
45
  export * from './PublicOrderService';
37
46
  export * from './PublicShopService';
38
47
  export * from './ReservationService';
39
48
  export * from './ServicesService';
49
+ export * from './EmailTemplateEditorService';
40
50
  export * from './UiSelectOptionsService';
package/src/types/auth.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export type Claim =
2
2
  | 'admin:view'
3
+ | 'admin:finance'
4
+ | 'admin:merchants'
5
+ | 'admin:statistics'
3
6
  | 'auth:login'
4
7
  | 'buyers:view'
5
8
  | 'events:create'
@@ -0,0 +1,10 @@
1
+ export type EmailTemplateType =
2
+ | 'merchant_invite'
3
+ | 'order_confirmation'
4
+ | 'personalization_invite'
5
+ | 'personalization_request'
6
+ | 'tickets';
7
+
8
+ export type EmailTemplateLanguage =
9
+ | 'en'
10
+ | 'nl';