@coopenomics/notifications 2025.11.8

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 (63) hide show
  1. package/.cursor/rules/notifications.mdc +20 -0
  2. package/.env-example +2 -0
  3. package/README.md +218 -0
  4. package/build.config.ts +13 -0
  5. package/dist/index.cjs +3027 -0
  6. package/dist/index.d.cts +792 -0
  7. package/dist/index.d.mts +792 -0
  8. package/dist/index.d.ts +792 -0
  9. package/dist/index.mjs +3016 -0
  10. package/dist/sync/novu-sync.service.d.ts +30 -0
  11. package/dist/sync/novu-sync.service.js +128 -0
  12. package/dist/sync/sync-runner.d.ts +4 -0
  13. package/dist/sync/sync-runner.js +130 -0
  14. package/dist/utils/role-utils.d.ts +8 -0
  15. package/dist/utils/role-utils.js +18 -0
  16. package/dist/workflows/incoming-transfer/incoming-transfer-workflow.d.ts +3 -0
  17. package/dist/workflows/incoming-transfer/incoming-transfer-workflow.js +16 -0
  18. package/dist/workflows/incoming-transfer/index.d.ts +3 -0
  19. package/dist/workflows/incoming-transfer/index.js +2 -0
  20. package/dist/workflows/incoming-transfer/types.d.ts +12 -0
  21. package/dist/workflows/incoming-transfer/types.js +5 -0
  22. package/dist/workflows/new-agenda-item/index.d.ts +3 -0
  23. package/dist/workflows/new-agenda-item/index.js +2 -0
  24. package/dist/workflows/new-agenda-item/new-agenda-item-workflow.d.ts +3 -0
  25. package/dist/workflows/new-agenda-item/new-agenda-item-workflow.js +16 -0
  26. package/dist/workflows/new-agenda-item/types.d.ts +27 -0
  27. package/dist/workflows/new-agenda-item/types.js +10 -0
  28. package/dist/workflows/role-based-workflows.d.ts +21 -0
  29. package/dist/workflows/role-based-workflows.js +65 -0
  30. package/package.json +51 -0
  31. package/src/base/defaults.ts +66 -0
  32. package/src/base/workflow-builder.ts +128 -0
  33. package/src/index.ts +9 -0
  34. package/src/sync/README.md +54 -0
  35. package/src/sync/novu-sync.service.ts +246 -0
  36. package/src/sync/sync-runner.ts +154 -0
  37. package/src/types/index.ts +99 -0
  38. package/src/utils/index.ts +1 -0
  39. package/src/utils/slugify/builtinReplacements.ts +2065 -0
  40. package/src/utils/slugify/slugify.ts +284 -0
  41. package/src/utils/slugify/transliterate.ts +48 -0
  42. package/src/workflows/approval-request/index.ts +52 -0
  43. package/src/workflows/approval-response/index.ts +54 -0
  44. package/src/workflows/decision-approved/index.ts +50 -0
  45. package/src/workflows/email-verification/index.ts +35 -0
  46. package/src/workflows/incoming-transfer/index.ts +43 -0
  47. package/src/workflows/index.ts +74 -0
  48. package/src/workflows/invite/index.ts +34 -0
  49. package/src/workflows/meet-ended/index.ts +51 -0
  50. package/src/workflows/meet-initial/index.ts +53 -0
  51. package/src/workflows/meet-reminder-end/index.ts +52 -0
  52. package/src/workflows/meet-reminder-start/index.ts +51 -0
  53. package/src/workflows/meet-restart/index.ts +53 -0
  54. package/src/workflows/meet-started/index.ts +51 -0
  55. package/src/workflows/new-agenda-item/index.ts +51 -0
  56. package/src/workflows/new-deposit-payment-request/index.ts +50 -0
  57. package/src/workflows/new-initial-payment-request/index.ts +50 -0
  58. package/src/workflows/payment-cancelled/index.ts +51 -0
  59. package/src/workflows/payment-paid/index.ts +50 -0
  60. package/src/workflows/reset-key/index.ts +36 -0
  61. package/src/workflows/server-provisioned/index.ts +45 -0
  62. package/src/workflows/welcome/index.ts +43 -0
  63. package/tsconfig.json +18 -0
@@ -0,0 +1,284 @@
1
+ /* cspell:disable */
2
+ /*
3
+ * 15/11/2024
4
+ *
5
+ * Slugify a string.
6
+ *
7
+ * Original code: https://github.com/simov/slugify
8
+ * Enhanced code with custom replacements: https://gist.github.com/glorat/5070ebd2fa275e2012a51300329a7a55
9
+ */
10
+
11
+ import { transliterate } from './transliterate';
12
+
13
+ const builtinOverridableReplacements = [
14
+ ['&', ' and '],
15
+ ['🦄', ' unicorn '],
16
+ ['♥', ' love '],
17
+ ];
18
+
19
+ const matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
20
+ function escapeStringRegexp(str: string) {
21
+ if (typeof str !== 'string') {
22
+ throw new TypeError('Expected a string');
23
+ }
24
+
25
+ return str.replace(matchOperatorsRe, '\\$&');
26
+ }
27
+
28
+ interface Options {
29
+ /**
30
+ *@default '-'
31
+ *@example
32
+ *```
33
+ *import slugify from '@novu/shared';
34
+ *slugify('BAR and baz');
35
+ * //=> 'bar-and-baz'
36
+ *slugify('BAR and baz', {separator: '_'});
37
+ * //=> 'bar_and_baz'
38
+ *slugify('BAR and baz', {separator: ''});
39
+ * //=> 'barandbaz'
40
+ *```
41
+ */
42
+ readonly separator?: string;
43
+
44
+ /**
45
+ *Make the slug lowercase.
46
+ *@default true
47
+ *@example
48
+ *```
49
+ *import slugify from '@novu/shared';
50
+ *slugify('Déjà Vu!');
51
+ * //=> 'deja-vu'
52
+ *slugify('Déjà Vu!', {lowercase: false});
53
+ * //=> 'Deja-Vu'
54
+ *```
55
+ */
56
+ readonly lowercase?: boolean;
57
+
58
+ /**
59
+ *Convert camelcase to separate words. Internally it does `fooBar` → `foo bar`.
60
+ *@default true
61
+ *@example
62
+ *```
63
+ *import slugify from '@novu/shared';
64
+ *slugify('fooBar');
65
+ * //=> 'foo-bar'
66
+ *slugify('fooBar', {decamelize: false});
67
+ * //=> 'foobar'
68
+ *```
69
+ */
70
+ readonly decamelize?: boolean;
71
+
72
+ /**
73
+ *Add your own custom replacements.
74
+ *The replacements are run on the original string before any other transformations.
75
+ *This only overrides a default replacement if you set an item with the same key, like `&`.
76
+ *Add a leading and trailing space to the replacement to have it separated by dashes.
77
+ *@default [ ['&', ' and '], ['🦄', ' unicorn '], ['♥', ' love '] ]
78
+ *@example
79
+ *```
80
+ *import slugify from '@novu/shared';
81
+ *slugify('Foo@unicorn', {
82
+ *customReplacements: [
83
+ *['@', 'at']
84
+ *]
85
+ *});
86
+ * //=> 'fooatunicorn'
87
+ *slugify('foo@unicorn', {
88
+ *customReplacements: [
89
+ *['@', ' at ']
90
+ *]
91
+ *});
92
+ * //=> 'foo-at-unicorn'
93
+ *slugify('I love 🐶', {
94
+ *customReplacements: [
95
+ *['🐶', 'dogs']
96
+ *]
97
+ *});
98
+ * //=> 'i-love-dogs'
99
+ *```
100
+ */
101
+ readonly customReplacements?: ReadonlyArray<[string, string]>;
102
+
103
+ /**
104
+ *If your string starts with an underscore, it will be preserved in the slugified string.
105
+ *Sometimes leading underscores are intentional, for example, filenames representing hidden paths on a website.
106
+ *@default false
107
+ *@example
108
+ *```
109
+ *import slugify from '@novu/shared';
110
+ *slugify('_foo_bar');
111
+ * //=> 'foo-bar'
112
+ *slugify('_foo_bar', {preserveLeadingUnderscore: true});
113
+ * //=> '_foo-bar'
114
+ *```
115
+ */
116
+ readonly preserveLeadingUnderscore?: boolean;
117
+
118
+ /**
119
+ *If your string ends with a dash, it will be preserved in the slugified string.
120
+ *For example, using slugify on an input field would allow for validation while not preventing the user from writing a slug.
121
+ *@default false
122
+ *@example
123
+ *```
124
+ *import slugify from '@novu/shared';
125
+ *slugify('foo-bar-');
126
+ * //=> 'foo-bar'
127
+ *slugify('foo-bar-', {preserveTrailingDash: true});
128
+ * //=> 'foo-bar-'
129
+ *```
130
+ */
131
+ readonly preserveTrailingDash?: boolean;
132
+ }
133
+
134
+ const decamelize = (string: string) => {
135
+ return (
136
+ string
137
+ // Separate capitalized words.
138
+ .replace(/([A-Z]{2,})(\d+)/g, '$1 $2')
139
+ .replace(/([a-z\d]+)([A-Z]{2,})/g, '$1 $2')
140
+
141
+ .replace(/([a-z\d])([A-Z])/g, '$1 $2')
142
+ /*
143
+ * `[a-rt-z]` matches all lowercase characters except `s`.
144
+ * This avoids matching plural acronyms like `APIs`.
145
+ */
146
+ .replace(/([A-Z]+)([A-Z][a-rt-z\d]+)/g, '$1 $2')
147
+ );
148
+ };
149
+
150
+ const removeMootSeparators = (string: string, separator: string) => {
151
+ const escapedSeparator = escapeStringRegexp(separator);
152
+
153
+ return string
154
+ .replace(new RegExp(`${escapedSeparator}{2,}`, 'g'), separator)
155
+ .replace(new RegExp(`^${escapedSeparator}|${escapedSeparator}$`, 'g'), '');
156
+ };
157
+
158
+ /**
159
+ * Slugify a string.
160
+ *
161
+ * Default behavior:
162
+ * - decamelize
163
+ * - lowercase
164
+ * - remove duplicates of the separator character
165
+ * - remove trailing spaces
166
+ * - remove special characters
167
+ * - multilanguage support
168
+ * - emojis support
169
+ *
170
+ * @example
171
+ * ```
172
+ * import { slugify } from '@novu/shared';
173
+ * slugify('Hello World');
174
+ * //=> 'hello-world'
175
+ * ```
176
+ *
177
+ * @example
178
+ * ```
179
+ * import { slugify } from '@novu/shared';
180
+ * slugify('Hello World', { separator: '_' });
181
+ * //=> 'hello_world'
182
+ * ```
183
+ *
184
+ * @example
185
+ * ```
186
+ * import { slugify } from '@novu/shared';
187
+ * slugify('αβγ');
188
+ * //=> 'avg'
189
+ * ```
190
+ *
191
+ * @example
192
+ * ```
193
+ * import { slugify } from '@novu/shared';
194
+ * slugify('💯-1️⃣-2️⃣-3️⃣');
195
+ * //=> '100-1-2-3'
196
+ * ```
197
+ *
198
+ * @example
199
+ * ```
200
+ * import { slugify } from '@novu/shared';
201
+ * slugify('camelCase', { decamelize: true });
202
+ * //=> 'camel-case'
203
+ * ```
204
+ *
205
+ * @example
206
+ * ```
207
+ * import { slugify } from '@novu/shared';
208
+ * slugify('Hello World', { lowercase: false });
209
+ * //=> 'Hello-World'
210
+ * ```
211
+ *
212
+ * @example
213
+ * ```
214
+ * import { slugify } from '@novu/shared';
215
+ * slugify('foo@unicorn', { preserveLeadingUnderscore: true });
216
+ * //=> '_foo-at-unicorn'
217
+ * ```
218
+ *
219
+ * @example
220
+ * ```
221
+ * import { slugify } from '@novu/shared';
222
+ * slugify('foo-bar-', { preserveTrailingDash: true });
223
+ * //=> 'foo-bar-'
224
+ * ```
225
+ */
226
+ export const slugify = (string: string, options?: Options) => {
227
+ if (typeof string !== 'string') {
228
+ throw new TypeError(`Expected a string, got \`${typeof string}\``);
229
+ }
230
+
231
+ options = {
232
+ separator: '-',
233
+ lowercase: true,
234
+ decamelize: true,
235
+ customReplacements: [],
236
+ preserveLeadingUnderscore: false,
237
+ preserveTrailingDash: false,
238
+ ...options,
239
+ };
240
+
241
+ const shouldPrependUnderscore = options.preserveLeadingUnderscore && string.startsWith('_');
242
+ const shouldAppendDash = options.preserveTrailingDash && string.endsWith('-');
243
+
244
+ const customReplacements = new Map([
245
+ ...(builtinOverridableReplacements as [string, string][]),
246
+ ...(options.customReplacements as [string, string][]),
247
+ ]);
248
+
249
+ string = transliterate(string, { customReplacements: Array.from(customReplacements) });
250
+
251
+ if (options.decamelize) {
252
+ string = decamelize(string);
253
+ }
254
+
255
+ let patternSlug = /[^a-zA-Z\d]+/g;
256
+
257
+ if (options.lowercase) {
258
+ string = string.toLowerCase();
259
+ patternSlug = /[^a-z\d]+/g;
260
+ }
261
+
262
+ string = string.replace(patternSlug, options.separator ?? '-');
263
+ string = string.replace(/\\/g, '');
264
+
265
+ /*
266
+ * Detect contractions/possessives by looking for any word followed by a `-t`
267
+ * or `-s` in isolation and then remove it.
268
+ */
269
+ string = string.replace(/([a-zA-Z\d]+)-([ts])(-|$)/g, '$1$2$3');
270
+
271
+ if (options.separator) {
272
+ string = removeMootSeparators(string, options.separator);
273
+ }
274
+
275
+ if (shouldPrependUnderscore) {
276
+ string = `_${string}`;
277
+ }
278
+
279
+ if (shouldAppendDash) {
280
+ string = `${string}-`;
281
+ }
282
+
283
+ return string;
284
+ };
@@ -0,0 +1,48 @@
1
+ import { builtinReplacements } from './builtinReplacements';
2
+
3
+ const matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
4
+ function escapeStringRegexp(str: string) {
5
+ if (typeof str !== 'string') {
6
+ throw new TypeError('Expected a string');
7
+ }
8
+
9
+ return str.replace(matchOperatorsRe, '\\$&');
10
+ }
11
+
12
+ const doCustomReplacements = (string: string, replacements: Map<string, string>) => {
13
+ for (const [key, value] of replacements) {
14
+ // TODO: Use `String#replaceAll()` when targeting Node.js 16.
15
+ string = string.replace(new RegExp(escapeStringRegexp(key), 'g'), value);
16
+ }
17
+
18
+ return string;
19
+ };
20
+
21
+ type TransliterateOptions = {
22
+ customReplacements: [string, string][];
23
+ };
24
+
25
+ export const transliterate = (string: string, options: TransliterateOptions) => {
26
+ if (typeof string !== 'string') {
27
+ throw new TypeError(`Expected a string, got \`${typeof string}\``);
28
+ }
29
+
30
+ options = {
31
+ ...options,
32
+ customReplacements: options.customReplacements || [],
33
+ };
34
+
35
+ const customReplacements = new Map<string, string>([
36
+ ...(builtinReplacements as [string, string][]),
37
+ ...(options.customReplacements as [string, string][]),
38
+ ]);
39
+
40
+ string = string.normalize();
41
+ string = doCustomReplacements(string, customReplacements);
42
+ string = string
43
+ .normalize('NFD')
44
+ .replace(/\p{Diacritic}/gu, '')
45
+ .normalize();
46
+
47
+ return string;
48
+ };
@@ -0,0 +1,52 @@
1
+
2
+ import { WorkflowDefinition } from '../../types';
3
+ import { WorkflowBuilder } from '../../base/workflow-builder';
4
+ import { z } from 'zod';
5
+ import { BaseWorkflowPayload } from '../../types';
6
+ import { createEmailStep, createInAppStep, createPushStep } from '../../base/defaults';
7
+ import { slugify } from '../../utils';
8
+
9
+ // Схема для approval-request воркфлоу
10
+ export const approvalRequestPayloadSchema = z.object({
11
+ chairmanName: z.string(),
12
+ requestTitle: z.string(),
13
+ requestDescription: z.string(),
14
+ authorName: z.string(),
15
+ coopname: z.string(),
16
+ approval_hash: z.string(),
17
+ approvalUrl: z.string().optional(),
18
+ });
19
+
20
+ export type IPayload = z.infer<typeof approvalRequestPayloadSchema>;
21
+
22
+ export interface IWorkflow extends BaseWorkflowPayload, IPayload {}
23
+
24
+ export const name = 'Запрос на одобрение председателя'
25
+ export const id = slugify(name);
26
+
27
+ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
28
+ .create<IWorkflow>()
29
+ .name(name)
30
+ .workflowId(id)
31
+ .description('Уведомление председателю совета о новом запросе на одобрение')
32
+ .payloadSchema(approvalRequestPayloadSchema)
33
+ .tags(['chairman']) // Только для председателя
34
+ .addSteps([
35
+ createEmailStep(
36
+ 'approval-request-email',
37
+ 'Новый запрос на одобрение действия: {{payload.requestTitle}}',
38
+ 'Уважаемый {{payload.chairmanName}}!<br><br>Поступил новый запрос на одобрение:<br><br><strong>{{payload.requestTitle}}</strong><br><br>{{payload.requestDescription}}<br><br>Автор запроса: {{payload.authorName}}<br><br>Ссылка для одобрения или отклонения запроса: {{payload.approvalUrl}}'
39
+ ),
40
+ createInAppStep(
41
+ 'approval-request-notification',
42
+ 'Новый запрос на одобрение действия',
43
+ 'Запрос: {{payload.requestTitle}}<br>Автор: {{payload.authorName}}'
44
+ ),
45
+ createPushStep(
46
+ 'approval-request-push',
47
+ 'Новый запрос на одобрение действия',
48
+ 'Запрос: {{payload.requestTitle}} от {{payload.authorName}}'
49
+ ),
50
+ ])
51
+ .build();
52
+
@@ -0,0 +1,54 @@
1
+ import { WorkflowDefinition } from '../../types';
2
+ import { WorkflowBuilder } from '../../base/workflow-builder';
3
+ import { z } from 'zod';
4
+ import { BaseWorkflowPayload } from '../../types';
5
+ import { createEmailStep, createInAppStep, createPushStep } from '../../base/defaults';
6
+ import { slugify } from '../../utils';
7
+
8
+ // Схема для approval-response воркфлоу
9
+ export const approvalResponsePayloadSchema = z.object({
10
+ userName: z.string(),
11
+ approvalStatus: z.enum(['approved', 'declined']),
12
+ approvalStatusText: z.string(),
13
+ approvalId: z.string(),
14
+ coopname: z.string(),
15
+ coopShortName: z.string(),
16
+ approvalUrl: z.string().optional(),
17
+ });
18
+
19
+ export type IPayload = z.infer<typeof approvalResponsePayloadSchema>;
20
+
21
+ export interface IWorkflow extends BaseWorkflowPayload, IPayload {}
22
+
23
+ export const name = 'Ответ на запрос одобрения';
24
+ export const id = slugify(name);
25
+
26
+ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
27
+ .create<IWorkflow>()
28
+ .name(name)
29
+ .workflowId(id)
30
+ .description('Уведомление пользователю об одобрении или отклонении его запроса председателем')
31
+ .payloadSchema(approvalResponsePayloadSchema)
32
+ .tags(['user']) // Для всех пользователей
33
+ .addSteps([
34
+ createEmailStep(
35
+ 'approval-response-email',
36
+ 'Ваш запрос {{payload.approvalStatusText}} председателем совета',
37
+ `Уважаемый {{payload.userName}}!
38
+
39
+ Ваш запрос {{payload.approvalId}} {{payload.approvalStatusText}} председателем совета {{payload.coopShortName}}.
40
+
41
+ Подробнее по ссылке: {{payload.approvalUrl}}`
42
+ ),
43
+ createInAppStep(
44
+ 'approval-response-notification',
45
+ 'Ответ на запрос одобрения',
46
+ 'Ваш запрос {{payload.approvalId}} {{payload.approvalStatusText}} председателем совета {{payload.coopShortName}}'
47
+ ),
48
+ createPushStep(
49
+ 'approval-response-push',
50
+ 'Ответ на запрос одобрения',
51
+ 'Запрос {{payload.approvalId}} {{payload.approvalStatusText}}'
52
+ ),
53
+ ])
54
+ .build();
@@ -0,0 +1,50 @@
1
+
2
+ import { WorkflowDefinition } from '../../types';
3
+ import { WorkflowBuilder } from '../../base/workflow-builder';
4
+ import { z } from 'zod';
5
+ import { BaseWorkflowPayload } from '../../types';
6
+ import { createEmailStep, createInAppStep, createPushStep } from '../../base/defaults';
7
+ import { slugify } from '../../utils';
8
+
9
+ // Схема для decision-approved воркфлоу
10
+ export const decisionApprovedPayloadSchema = z.object({
11
+ userName: z.string(),
12
+ decisionTitle: z.string(),
13
+ coopname: z.string(),
14
+ decision_id: z.string(),
15
+ decisionUrl: z.string().optional(),
16
+ });
17
+
18
+ export type IPayload = z.infer<typeof decisionApprovedPayloadSchema>;
19
+
20
+ export interface IWorkflow extends BaseWorkflowPayload, IPayload {}
21
+
22
+ export const name = 'Решение совета принято';
23
+ export const id = slugify(name);
24
+
25
+ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
26
+ .create<IWorkflow>()
27
+ .name(name)
28
+ .workflowId(id)
29
+ .description('Уведомление пользователю о принятии решения совета по его вопросу')
30
+ .payloadSchema(decisionApprovedPayloadSchema)
31
+ .tags(['user']) // Для всех пользователей
32
+ .addSteps([
33
+ createEmailStep(
34
+ 'decision-approved-email',
35
+ 'Решение совета принято по вашему вопросу',
36
+ 'Уважаемый {{payload.userName}}!<br><br>Совет кооператива принял решение по вашему вопросу:<br><br><strong>{{payload.decisionTitle}}</strong><br><br>Ссылка для просмотра подробной информации: {{payload.decisionUrl}}'
37
+ ),
38
+ createInAppStep(
39
+ 'decision-approved-notification',
40
+ 'Решение совета принято',
41
+ 'По вашему вопросу принято решение: {{payload.decisionTitle}}'
42
+ ),
43
+ createPushStep(
44
+ 'decision-approved-push',
45
+ 'Решение совета принято',
46
+ 'Принято решение: {{payload.decisionTitle}}'
47
+ ),
48
+ ])
49
+ .build();
50
+
@@ -0,0 +1,35 @@
1
+ import { WorkflowDefinition, type BaseWorkflowPayload } from '../../types';
2
+ import { WorkflowBuilder } from '../../base/workflow-builder';
3
+ import { createEmailStep } from '../../base/defaults';
4
+ import { z } from 'zod';
5
+ import { slugify } from '../../utils';
6
+
7
+ // Схема для email-verification воркфлоу
8
+ export const emailVerificationPayloadSchema = z.object({
9
+ verificationUrl: z.string(),
10
+ });
11
+
12
+ export type IPayload = z.infer<typeof emailVerificationPayloadSchema>;
13
+
14
+ export interface IWorkflow extends BaseWorkflowPayload, IPayload {}
15
+
16
+ export const name = 'Верификация Email';
17
+ export const id = slugify(name);
18
+
19
+ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
20
+ .create<IWorkflow>()
21
+ .name(name)
22
+ .workflowId(id)
23
+ .description('Верификация email адреса пользователя')
24
+ .payloadSchema(emailVerificationPayloadSchema)
25
+ .tags(['auth'])
26
+ .addSteps([
27
+ createEmailStep(
28
+ 'email-verification-email',
29
+ 'Email Verification',
30
+ 'Dear user,<br><br>' +
31
+ 'To verify your email, click on this link: <a href="{{payload.verificationUrl}}">{{payload.verificationUrl}}</a><br><br>' +
32
+ 'If you did not create an account, then ignore this email.'
33
+ ),
34
+ ])
35
+ .build();
@@ -0,0 +1,43 @@
1
+ import { WorkflowDefinition, type BaseWorkflowPayload } from '../../types';
2
+ import { WorkflowBuilder } from '../../base/workflow-builder';
3
+ import { createEmailStep, createInAppStep, createPushStep } from '../../base/defaults';
4
+ import { z } from 'zod';
5
+ import { slugify } from '../../utils';
6
+
7
+ // Схема для incoming-transfer воркфлоу
8
+ export const incomingTransferPayloadSchema = z.object({
9
+ quantity: z.string(),
10
+ });
11
+
12
+ export type IPayload = z.infer<typeof incomingTransferPayloadSchema>;
13
+
14
+ export interface IWorkflow extends BaseWorkflowPayload, IPayload {}
15
+
16
+ export const name = 'Входящий перевод';
17
+ export const id = slugify(name);
18
+
19
+ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
20
+ .create<IWorkflow>()
21
+ .name(name)
22
+ .workflowId(id)
23
+ .description('Уведомление о получении входящего перевода')
24
+ .payloadSchema(incomingTransferPayloadSchema)
25
+ .tags(['user']) // Доступно для всех ролей
26
+ .addSteps([
27
+ createEmailStep(
28
+ 'incoming-transfer-email',
29
+ 'Получен входящий перевод на сумму {{payload.quantity}}',
30
+ 'Уведомляем вас о получении входящего перевода.<br><br><strong>Сумма перевода: {{payload.quantity}}</strong><br><br>Перевод успешно зачислен на ваш счет.'
31
+ ),
32
+ createInAppStep(
33
+ 'incoming-transfer-notification',
34
+ 'Входящий перевод',
35
+ 'Получен входящий перевод на сумму {{payload.quantity}}'
36
+ ),
37
+ createPushStep(
38
+ 'incoming-transfer-push',
39
+ 'Входящий перевод',
40
+ 'Получен перевод на сумму {{payload.quantity}}'
41
+ ),
42
+ ])
43
+ .build();
@@ -0,0 +1,74 @@
1
+ import { WorkflowDefinition } from '../types';
2
+ // Импорты воркфлоу для регистрации
3
+ import { workflow as welcomeWorkflow } from './welcome';
4
+ import { workflow as newAgendaItemWorkflow } from './new-agenda-item';
5
+ import { workflow as incomingTransferWorkflow } from './incoming-transfer';
6
+ import { workflow as approvalRequestWorkflow } from './approval-request';
7
+ import { workflow as decisionApprovedWorkflow } from './decision-approved';
8
+ import { workflow as paymentPaidWorkflow } from './payment-paid';
9
+ import { workflow as paymentCancelledWorkflow } from './payment-cancelled';
10
+ import { workflow as meetInitialWorkflow } from './meet-initial';
11
+ import { workflow as meetReminderStartWorkflow } from './meet-reminder-start';
12
+ import { workflow as meetStartedWorkflow } from './meet-started';
13
+ import { workflow as meetReminderEndWorkflow } from './meet-reminder-end';
14
+ import { workflow as meetRestartWorkflow } from './meet-restart';
15
+ import { workflow as meetEndedWorkflow } from './meet-ended';
16
+ import { workflow as approvalResponseWorkflow } from './approval-response';
17
+ import { workflow as newInitialPaymentRequestWorkflow } from './new-initial-payment-request';
18
+ import { workflow as newDepositPaymentRequestWorkflow } from './new-deposit-payment-request';
19
+ import { workflow as resetKeyWorkflow } from './reset-key';
20
+ import { workflow as inviteWorkflow } from './invite';
21
+ import { workflow as emailVerificationWorkflow } from './email-verification';
22
+ import { workflow as serverProvisionedWorkflow } from './server-provisioned';
23
+
24
+ // Импортируем все воркфлоу
25
+ export * as Welcome from './welcome';
26
+ export * as NewAgenda from './new-agenda-item';
27
+ export * as NewTransfer from './incoming-transfer';
28
+ export * as ApprovalRequest from './approval-request';
29
+ export * as DecisionApproved from './decision-approved';
30
+ export * as PaymentPaid from './payment-paid';
31
+ export * as PaymentCancelled from './payment-cancelled';
32
+ export * as MeetInitial from './meet-initial';
33
+ export * as MeetReminderStart from './meet-reminder-start';
34
+ export * as MeetStarted from './meet-started';
35
+ export * as MeetReminderEnd from './meet-reminder-end';
36
+ export * as MeetRestart from './meet-restart';
37
+ export * as MeetEnded from './meet-ended';
38
+ export * as ApprovalResponse from './approval-response';
39
+ export * as NewInitialPaymentRequest from './new-initial-payment-request';
40
+ export * as NewDepositPaymentRequest from './new-deposit-payment-request';
41
+ export * as ResetKey from './reset-key';
42
+ export * as Invite from './invite';
43
+ export * as EmailVerification from './email-verification';
44
+ export * as ServerProvisioned from './server-provisioned';
45
+
46
+ // Массив всех воркфлоу для автоматической регистрации
47
+ export const allWorkflows: WorkflowDefinition[] = [
48
+ welcomeWorkflow,
49
+ newAgendaItemWorkflow,
50
+ incomingTransferWorkflow,
51
+ approvalRequestWorkflow,
52
+ decisionApprovedWorkflow,
53
+ paymentPaidWorkflow,
54
+ paymentCancelledWorkflow,
55
+ meetInitialWorkflow,
56
+ meetReminderStartWorkflow,
57
+ meetStartedWorkflow,
58
+ meetReminderEndWorkflow,
59
+ meetRestartWorkflow,
60
+ meetEndedWorkflow,
61
+ approvalResponseWorkflow,
62
+ newInitialPaymentRequestWorkflow,
63
+ newDepositPaymentRequestWorkflow,
64
+ resetKeyWorkflow,
65
+ inviteWorkflow,
66
+ emailVerificationWorkflow,
67
+ serverProvisionedWorkflow,
68
+ ];
69
+
70
+ // Экспортируем воркфлоу по ID для удобного доступа
71
+ export const workflowsById = allWorkflows.reduce((acc, workflow) => {
72
+ acc[workflow.workflowId] = workflow;
73
+ return acc;
74
+ }, {} as Record<string, WorkflowDefinition>);
@@ -0,0 +1,34 @@
1
+ import { WorkflowDefinition, type BaseWorkflowPayload } from '../../types';
2
+ import { WorkflowBuilder } from '../../base/workflow-builder';
3
+ import { createEmailStep } from '../../base/defaults';
4
+ import { z } from 'zod';
5
+ import { slugify } from '../../utils';
6
+
7
+ // Схема для invite воркфлоу
8
+ export const invitePayloadSchema = z.object({
9
+ inviteUrl: z.string(),
10
+ });
11
+
12
+ export type IPayload = z.infer<typeof invitePayloadSchema>;
13
+
14
+ export interface IWorkflow extends BaseWorkflowPayload, IPayload {}
15
+
16
+ export const name = 'Приглашение в кооператив';
17
+ export const id = slugify(name);
18
+
19
+ export const workflow: WorkflowDefinition<IWorkflow> = WorkflowBuilder
20
+ .create<IWorkflow>()
21
+ .name(name)
22
+ .workflowId(id)
23
+ .description('Приглашение на подключение к цифровому кооперативу')
24
+ .payloadSchema(invitePayloadSchema)
25
+ .tags(['auth'])
26
+ .addSteps([
27
+ createEmailStep(
28
+ 'invite-email',
29
+ 'Приглашение в Цифровой Кооператив',
30
+ 'Вам отправлено приглашение на подключение к Цифровому Кооперативу в качестве действующего пайщика.<br><br>' +
31
+ 'Для того, чтобы воспользоваться приглашением и получить ключ доступа, пожалуйста, перейдите по ссылке: <a href="{{payload.inviteUrl}}">{{payload.inviteUrl}}</a>'
32
+ ),
33
+ ])
34
+ .build();