@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.
- package/.cursor/rules/notifications.mdc +20 -0
- package/.env-example +2 -0
- package/README.md +218 -0
- package/build.config.ts +13 -0
- package/dist/index.cjs +3027 -0
- package/dist/index.d.cts +792 -0
- package/dist/index.d.mts +792 -0
- package/dist/index.d.ts +792 -0
- package/dist/index.mjs +3016 -0
- package/dist/sync/novu-sync.service.d.ts +30 -0
- package/dist/sync/novu-sync.service.js +128 -0
- package/dist/sync/sync-runner.d.ts +4 -0
- package/dist/sync/sync-runner.js +130 -0
- package/dist/utils/role-utils.d.ts +8 -0
- package/dist/utils/role-utils.js +18 -0
- package/dist/workflows/incoming-transfer/incoming-transfer-workflow.d.ts +3 -0
- package/dist/workflows/incoming-transfer/incoming-transfer-workflow.js +16 -0
- package/dist/workflows/incoming-transfer/index.d.ts +3 -0
- package/dist/workflows/incoming-transfer/index.js +2 -0
- package/dist/workflows/incoming-transfer/types.d.ts +12 -0
- package/dist/workflows/incoming-transfer/types.js +5 -0
- package/dist/workflows/new-agenda-item/index.d.ts +3 -0
- package/dist/workflows/new-agenda-item/index.js +2 -0
- package/dist/workflows/new-agenda-item/new-agenda-item-workflow.d.ts +3 -0
- package/dist/workflows/new-agenda-item/new-agenda-item-workflow.js +16 -0
- package/dist/workflows/new-agenda-item/types.d.ts +27 -0
- package/dist/workflows/new-agenda-item/types.js +10 -0
- package/dist/workflows/role-based-workflows.d.ts +21 -0
- package/dist/workflows/role-based-workflows.js +65 -0
- package/package.json +51 -0
- package/src/base/defaults.ts +66 -0
- package/src/base/workflow-builder.ts +128 -0
- package/src/index.ts +9 -0
- package/src/sync/README.md +54 -0
- package/src/sync/novu-sync.service.ts +246 -0
- package/src/sync/sync-runner.ts +154 -0
- package/src/types/index.ts +99 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/slugify/builtinReplacements.ts +2065 -0
- package/src/utils/slugify/slugify.ts +284 -0
- package/src/utils/slugify/transliterate.ts +48 -0
- package/src/workflows/approval-request/index.ts +52 -0
- package/src/workflows/approval-response/index.ts +54 -0
- package/src/workflows/decision-approved/index.ts +50 -0
- package/src/workflows/email-verification/index.ts +35 -0
- package/src/workflows/incoming-transfer/index.ts +43 -0
- package/src/workflows/index.ts +74 -0
- package/src/workflows/invite/index.ts +34 -0
- package/src/workflows/meet-ended/index.ts +51 -0
- package/src/workflows/meet-initial/index.ts +53 -0
- package/src/workflows/meet-reminder-end/index.ts +52 -0
- package/src/workflows/meet-reminder-start/index.ts +51 -0
- package/src/workflows/meet-restart/index.ts +53 -0
- package/src/workflows/meet-started/index.ts +51 -0
- package/src/workflows/new-agenda-item/index.ts +51 -0
- package/src/workflows/new-deposit-payment-request/index.ts +50 -0
- package/src/workflows/new-initial-payment-request/index.ts +50 -0
- package/src/workflows/payment-cancelled/index.ts +51 -0
- package/src/workflows/payment-paid/index.ts +50 -0
- package/src/workflows/reset-key/index.ts +36 -0
- package/src/workflows/server-provisioned/index.ts +45 -0
- package/src/workflows/welcome/index.ts +43 -0
- 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();
|