@flusys/ng-email 1.1.0-beta
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 +660 -0
- package/fesm2022/flusys-ng-email-email-config-form.component-8dOBD-Q-.mjs +651 -0
- package/fesm2022/flusys-ng-email-email-config-form.component-8dOBD-Q-.mjs.map +1 -0
- package/fesm2022/flusys-ng-email-email-config-list.component-CJhSPkCZ.mjs +508 -0
- package/fesm2022/flusys-ng-email-email-config-list.component-CJhSPkCZ.mjs.map +1 -0
- package/fesm2022/flusys-ng-email-template-form.component-DjzG_lG1.mjs +529 -0
- package/fesm2022/flusys-ng-email-template-form.component-DjzG_lG1.mjs.map +1 -0
- package/fesm2022/flusys-ng-email-template-list.component-CGJxfZMT.mjs +595 -0
- package/fesm2022/flusys-ng-email-template-list.component-CGJxfZMT.mjs.map +1 -0
- package/fesm2022/flusys-ng-email.mjs +571 -0
- package/fesm2022/flusys-ng-email.mjs.map +1 -0
- package/package.json +32 -0
- package/types/flusys-ng-email.d.ts +456 -0
package/README.md
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
# Email Package Guide
|
|
2
|
+
|
|
3
|
+
> **Package:** `@flusys/ng-email`
|
|
4
|
+
> **Type:** Email management with configurations, templates, and sending
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
`@flusys/ng-email` provides complete email management:
|
|
11
|
+
|
|
12
|
+
- **Email Configurations** - CRUD for SMTP, SendGrid, Mailgun providers
|
|
13
|
+
- **Email Templates** - Visual template management with variable interpolation
|
|
14
|
+
- **HTML/Plain Text** - Toggle between content types via `isHtml` field
|
|
15
|
+
- **Test Sending** - Test configurations and templates with dynamic variables
|
|
16
|
+
- **Company Scoping** - Automatic company-scoped queries (when enabled)
|
|
17
|
+
- **Lazy Loading** - All page components are lazy-loaded via routes
|
|
18
|
+
|
|
19
|
+
### Package Hierarchy
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
@flusys/ng-core <- Foundation (BaseApiService, APP_CONFIG)
|
|
23
|
+
|
|
|
24
|
+
@flusys/ng-shared <- Shared utilities (ApiResourceService, IBaseEntity)
|
|
25
|
+
|
|
|
26
|
+
@flusys/ng-layout <- Layout (LAYOUT_AUTH_STATE for company context)
|
|
27
|
+
|
|
|
28
|
+
@flusys/ng-email <- Email management (THIS PACKAGE)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Company Context
|
|
32
|
+
|
|
33
|
+
Email components use `LAYOUT_AUTH_STATE` from `@flusys/ng-layout` for company context. This follows the Provider Interface Pattern - ng-email depends on ng-layout's abstraction, not ng-auth directly.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
37
|
+
|
|
38
|
+
private readonly companyContext = inject(LAYOUT_AUTH_STATE);
|
|
39
|
+
|
|
40
|
+
readonly currentCompanyName = computed(
|
|
41
|
+
() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME,
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Package Architecture
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
ng-email/
|
|
51
|
+
├── interfaces/
|
|
52
|
+
│ ├── email-config.interface.ts # IEmailConfig, ICreateEmailConfigDto
|
|
53
|
+
│ ├── email-template.interface.ts # IEmailTemplate, ICreateEmailTemplateDto
|
|
54
|
+
│ ├── email-send.interface.ts # ISendEmailDto, ISendTemplateEmailDto
|
|
55
|
+
│ └── public-api.ts
|
|
56
|
+
├── services/
|
|
57
|
+
│ ├── email-config-api.service.ts # Config CRUD
|
|
58
|
+
│ ├── email-template-api.service.ts # Template CRUD
|
|
59
|
+
│ ├── email-send.service.ts # Email sending operations
|
|
60
|
+
│ └── public-api.ts
|
|
61
|
+
├── pages/
|
|
62
|
+
│ ├── config/
|
|
63
|
+
│ │ ├── email-config-list.component.ts
|
|
64
|
+
│ │ └── email-config-form.component.ts
|
|
65
|
+
│ └── template/
|
|
66
|
+
│ ├── template-list.component.ts
|
|
67
|
+
│ └── template-form.component.ts
|
|
68
|
+
├── routes/
|
|
69
|
+
│ └── email.routes.ts
|
|
70
|
+
└── public-api.ts
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Route Integration
|
|
76
|
+
|
|
77
|
+
### Adding Email Routes
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// app.routes.ts
|
|
81
|
+
import { EMAIL_ROUTES } from '@flusys/ng-email';
|
|
82
|
+
|
|
83
|
+
export const routes: Routes = [
|
|
84
|
+
{
|
|
85
|
+
path: 'email',
|
|
86
|
+
children: EMAIL_ROUTES,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Route Structure
|
|
92
|
+
|
|
93
|
+
| Path | Component | Description |
|
|
94
|
+
|------|-----------|-------------|
|
|
95
|
+
| `/email/configs` | EmailConfigListComponent | List email configurations |
|
|
96
|
+
| `/email/configs/new` | EmailConfigFormComponent | Create new config |
|
|
97
|
+
| `/email/configs/:id` | EmailConfigFormComponent | Edit config |
|
|
98
|
+
| `/email/templates` | TemplateListComponent | List email templates |
|
|
99
|
+
| `/email/templates/new` | TemplateFormComponent | Create new template |
|
|
100
|
+
| `/email/templates/:id` | TemplateFormComponent | Edit template |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Interfaces
|
|
105
|
+
|
|
106
|
+
### Email Configuration
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
interface IEmailConfig extends IBaseEntity {
|
|
110
|
+
name: string;
|
|
111
|
+
provider: 'smtp' | 'sendgrid' | 'mailgun';
|
|
112
|
+
config: Record<string, any>; // Provider-specific config
|
|
113
|
+
fromEmail: string;
|
|
114
|
+
fromName: string | null;
|
|
115
|
+
replyTo: string | null;
|
|
116
|
+
isActive: boolean;
|
|
117
|
+
isDefault: boolean; // Set as default configuration
|
|
118
|
+
companyId?: string | null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
interface ICreateEmailConfigDto {
|
|
122
|
+
name: string;
|
|
123
|
+
provider: 'smtp' | 'sendgrid' | 'mailgun';
|
|
124
|
+
config: Record<string, any>;
|
|
125
|
+
fromEmail: string;
|
|
126
|
+
fromName?: string;
|
|
127
|
+
replyTo?: string;
|
|
128
|
+
isActive?: boolean;
|
|
129
|
+
isDefault?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface IUpdateEmailConfigDto {
|
|
133
|
+
id: string;
|
|
134
|
+
name?: string;
|
|
135
|
+
provider?: 'smtp' | 'sendgrid' | 'mailgun';
|
|
136
|
+
config?: Record<string, any>;
|
|
137
|
+
fromEmail?: string;
|
|
138
|
+
fromName?: string;
|
|
139
|
+
replyTo?: string;
|
|
140
|
+
isActive?: boolean;
|
|
141
|
+
isDefault?: boolean;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Provider-Specific Configs
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// SMTP
|
|
149
|
+
interface ISmtpConfig {
|
|
150
|
+
host: string;
|
|
151
|
+
port: number;
|
|
152
|
+
secure: boolean;
|
|
153
|
+
auth: {
|
|
154
|
+
user: string;
|
|
155
|
+
pass: string;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// SendGrid
|
|
160
|
+
interface ISendGridConfig {
|
|
161
|
+
apiKey: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Mailgun
|
|
165
|
+
interface IMailgunConfig {
|
|
166
|
+
apiKey: string;
|
|
167
|
+
domain: string;
|
|
168
|
+
region?: 'us' | 'eu';
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Email Template
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface IEmailTemplate extends IBaseEntity {
|
|
176
|
+
name: string;
|
|
177
|
+
slug: string;
|
|
178
|
+
description: string | null;
|
|
179
|
+
subject: string;
|
|
180
|
+
schema: Record<string, unknown>;
|
|
181
|
+
htmlContent: string;
|
|
182
|
+
textContent: string | null;
|
|
183
|
+
schemaVersion: number;
|
|
184
|
+
isActive: boolean;
|
|
185
|
+
isHtml: boolean; // true=HTML mode, false=plain text
|
|
186
|
+
metadata: Record<string, unknown> | null;
|
|
187
|
+
companyId?: string | null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface ICreateEmailTemplateDto {
|
|
191
|
+
name: string;
|
|
192
|
+
slug: string;
|
|
193
|
+
description?: string;
|
|
194
|
+
subject: string;
|
|
195
|
+
schema?: Record<string, unknown>;
|
|
196
|
+
htmlContent: string;
|
|
197
|
+
textContent?: string;
|
|
198
|
+
isActive?: boolean;
|
|
199
|
+
isHtml?: boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
interface IUpdateEmailTemplateDto {
|
|
203
|
+
id: string;
|
|
204
|
+
name?: string;
|
|
205
|
+
slug?: string;
|
|
206
|
+
description?: string;
|
|
207
|
+
subject?: string;
|
|
208
|
+
schema?: Record<string, unknown>;
|
|
209
|
+
htmlContent?: string;
|
|
210
|
+
textContent?: string;
|
|
211
|
+
isActive?: boolean;
|
|
212
|
+
isHtml?: boolean;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Email Sending
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Direct email
|
|
220
|
+
interface ISendEmailDto {
|
|
221
|
+
to: string | string[];
|
|
222
|
+
cc?: string | string[];
|
|
223
|
+
bcc?: string | string[];
|
|
224
|
+
subject: string;
|
|
225
|
+
html: string;
|
|
226
|
+
text?: string;
|
|
227
|
+
from?: string;
|
|
228
|
+
fromName?: string;
|
|
229
|
+
replyTo?: string;
|
|
230
|
+
emailConfigId?: string;
|
|
231
|
+
attachments?: IEmailAttachmentDto[];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Template email
|
|
235
|
+
interface ISendTemplateEmailDto {
|
|
236
|
+
templateId?: string;
|
|
237
|
+
templateSlug?: string;
|
|
238
|
+
to: string | string[];
|
|
239
|
+
cc?: string | string[];
|
|
240
|
+
bcc?: string | string[];
|
|
241
|
+
variables?: Record<string, any>;
|
|
242
|
+
from?: string;
|
|
243
|
+
fromName?: string;
|
|
244
|
+
replyTo?: string;
|
|
245
|
+
emailConfigId?: string;
|
|
246
|
+
attachments?: IEmailAttachmentDto[];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Test email
|
|
250
|
+
interface ITestEmailDto {
|
|
251
|
+
emailConfigId: string;
|
|
252
|
+
recipient: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Attachment
|
|
256
|
+
interface IEmailAttachmentDto {
|
|
257
|
+
filename: string;
|
|
258
|
+
content: string; // Base64 encoded
|
|
259
|
+
contentType?: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Result
|
|
263
|
+
interface IEmailSendResult {
|
|
264
|
+
success: boolean;
|
|
265
|
+
messageId?: string;
|
|
266
|
+
error?: string;
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Services
|
|
273
|
+
|
|
274
|
+
All services are `providedIn: 'root'`. Company scoping is automatic when the company feature is enabled.
|
|
275
|
+
|
|
276
|
+
### EmailConfigApiService
|
|
277
|
+
|
|
278
|
+
Email configuration CRUD. Extends `ApiResourceService<IUpdateEmailConfigDto, IEmailConfig>`.
|
|
279
|
+
|
|
280
|
+
**Inherited CRUD methods** (from ApiResourceService): `insert`, `update`, `findById`, `getAll`, `delete` and their `*Async` variants.
|
|
281
|
+
|
|
282
|
+
### EmailTemplateApiService
|
|
283
|
+
|
|
284
|
+
Email template CRUD. Extends `ApiResourceService<IUpdateEmailTemplateDto, IEmailTemplate>`.
|
|
285
|
+
|
|
286
|
+
**Inherited CRUD methods** (from ApiResourceService): `insert`, `update`, `findById`, `getAll`, `delete` and their `*Async` variants.
|
|
287
|
+
|
|
288
|
+
### EmailSendService
|
|
289
|
+
|
|
290
|
+
Handles email sending operations. Extends `BaseApiService`.
|
|
291
|
+
|
|
292
|
+
| Method | Endpoint | Description |
|
|
293
|
+
|--------|----------|-------------|
|
|
294
|
+
| `sendDirect(dto)` | `POST /email/send/direct` | Send email directly |
|
|
295
|
+
| `sendTemplate(dto)` | `POST /email/send/template` | Send using template |
|
|
296
|
+
| `testConfiguration(dto)` | `POST /email/send/test` | Test email configuration |
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Components
|
|
301
|
+
|
|
302
|
+
All components are standalone, use Angular 21 signals, and lazy-load via routes.
|
|
303
|
+
|
|
304
|
+
### EmailConfigListComponent
|
|
305
|
+
|
|
306
|
+
Lists email configurations with actions.
|
|
307
|
+
|
|
308
|
+
**Features:**
|
|
309
|
+
- Lazy pagination via `p-datatable`
|
|
310
|
+
- Provider type tags (SMTP, SendGrid, Mailgun)
|
|
311
|
+
- Active/Inactive status badges
|
|
312
|
+
- Test button with dialog for recipient input
|
|
313
|
+
- Edit and delete actions
|
|
314
|
+
- Company info display when enabled
|
|
315
|
+
|
|
316
|
+
**Test Dialog:**
|
|
317
|
+
Opens a PrimeNG dialog to enter test recipient email instead of browser `prompt()`.
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
readonly showTestDialog = signal(false);
|
|
321
|
+
readonly selectedConfig = signal<IEmailConfig | null>(null);
|
|
322
|
+
testRecipient = '';
|
|
323
|
+
|
|
324
|
+
onTest(config: IEmailConfig): void {
|
|
325
|
+
this.selectedConfig.set(config);
|
|
326
|
+
this.testRecipient = '';
|
|
327
|
+
this.showTestDialog.set(true);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async sendTestEmail(): Promise<void> {
|
|
331
|
+
const config = this.selectedConfig();
|
|
332
|
+
if (!config || !this.testRecipient) return;
|
|
333
|
+
|
|
334
|
+
const response = await firstValueFrom(
|
|
335
|
+
this.emailSendService.testConfiguration({
|
|
336
|
+
emailConfigId: config.id,
|
|
337
|
+
recipient: this.testRecipient,
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
if (response.data?.success) {
|
|
342
|
+
this.messageService.add({
|
|
343
|
+
severity: 'success',
|
|
344
|
+
summary: 'Success',
|
|
345
|
+
detail: `Test email sent! Message ID: ${response.data.messageId}`,
|
|
346
|
+
});
|
|
347
|
+
this.showTestDialog.set(false);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### EmailConfigFormComponent
|
|
353
|
+
|
|
354
|
+
Create/edit email configuration with dynamic provider fields.
|
|
355
|
+
|
|
356
|
+
**Features:**
|
|
357
|
+
- Dynamic form fields based on selected provider
|
|
358
|
+
- SMTP: host, port, secure, username, password
|
|
359
|
+
- SendGrid: apiKey
|
|
360
|
+
- Mailgun: apiKey, domain, region
|
|
361
|
+
- Active toggle
|
|
362
|
+
- **Set as Default toggle** - marks configuration as the default for sending emails
|
|
363
|
+
- Create/Edit mode auto-detection
|
|
364
|
+
|
|
365
|
+
**Default Configuration:**
|
|
366
|
+
When `isDefault: true`, this configuration will be used automatically when no `emailConfigId` is provided in send requests.
|
|
367
|
+
|
|
368
|
+
### TemplateListComponent
|
|
369
|
+
|
|
370
|
+
Lists email templates with test send functionality.
|
|
371
|
+
|
|
372
|
+
**Features:**
|
|
373
|
+
- Lazy pagination via `p-datatable`
|
|
374
|
+
- HTML/Text type badges (based on `isHtml`)
|
|
375
|
+
- Active/Inactive status badges
|
|
376
|
+
- Test send button with dialog
|
|
377
|
+
- Edit and delete actions
|
|
378
|
+
|
|
379
|
+
**Test Send Dialog:**
|
|
380
|
+
- Select email configuration
|
|
381
|
+
- Enter recipient email
|
|
382
|
+
- **Dynamic variable inputs** extracted from template content
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
/** Extract variables from template content */
|
|
386
|
+
readonly templateVariables = computed(() => {
|
|
387
|
+
const template = this.selectedTemplate();
|
|
388
|
+
if (!template) return [];
|
|
389
|
+
|
|
390
|
+
// Combine all content sources to extract variables
|
|
391
|
+
const content = [
|
|
392
|
+
template.subject,
|
|
393
|
+
template.htmlContent,
|
|
394
|
+
template.textContent || '',
|
|
395
|
+
].join(' ');
|
|
396
|
+
|
|
397
|
+
// Find all {{variableName}} patterns
|
|
398
|
+
const matches = content.matchAll(/\{\{(\w+)\}\}/g);
|
|
399
|
+
const variables = new Set<string>();
|
|
400
|
+
|
|
401
|
+
for (const match of matches) {
|
|
402
|
+
variables.add(match[1]);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return Array.from(variables).sort();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Variable values bound to form inputs
|
|
409
|
+
variableValues: Record<string, string> = {};
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### TemplateFormComponent
|
|
413
|
+
|
|
414
|
+
Create/edit email template with HTML/Plain Text toggle.
|
|
415
|
+
|
|
416
|
+
**Features:**
|
|
417
|
+
- Name, slug, description fields
|
|
418
|
+
- Subject line with variable support
|
|
419
|
+
- **Editor Mode Toggle** (HTML/Plain Text)
|
|
420
|
+
- HTML content editor (when `isHtml: true`)
|
|
421
|
+
- Plain text content editor (editable, not read-only)
|
|
422
|
+
- Content sync between modes when switching
|
|
423
|
+
- Preview with variable highlighting
|
|
424
|
+
- Active toggle
|
|
425
|
+
|
|
426
|
+
**Editor Mode Switching:**
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
setEditorMode(mode: 'html' | 'text'): void {
|
|
430
|
+
const currentMode = this.editorMode();
|
|
431
|
+
|
|
432
|
+
// Sync content when switching modes
|
|
433
|
+
if (currentMode === 'text' && mode === 'html') {
|
|
434
|
+
if (!this.formModel.htmlContent && this.formModel.textContent) {
|
|
435
|
+
this.formModel.htmlContent = this.textToHtml(this.formModel.textContent);
|
|
436
|
+
}
|
|
437
|
+
} else if (currentMode === 'html' && mode === 'text') {
|
|
438
|
+
if (!this.formModel.textContent && this.formModel.htmlContent) {
|
|
439
|
+
this.formModel.textContent = this.htmlToPlainText(this.formModel.htmlContent);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
this.editorMode.set(mode);
|
|
444
|
+
this.formModel.isHtml = mode === 'html';
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Usage Examples
|
|
451
|
+
|
|
452
|
+
### Send Direct Email
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
import { EmailSendService } from '@flusys/ng-email';
|
|
456
|
+
|
|
457
|
+
private readonly emailSendService = inject(EmailSendService);
|
|
458
|
+
|
|
459
|
+
async sendEmail(): Promise<void> {
|
|
460
|
+
const response = await firstValueFrom(
|
|
461
|
+
this.emailSendService.sendDirect({
|
|
462
|
+
to: 'user@example.com',
|
|
463
|
+
subject: 'Hello!',
|
|
464
|
+
html: '<h1>Hello World</h1>',
|
|
465
|
+
text: 'Hello World',
|
|
466
|
+
emailConfigId: 'config-id',
|
|
467
|
+
})
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
if (response.data?.success) {
|
|
471
|
+
console.log('Sent! Message ID:', response.data.messageId);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Send Template Email
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
async sendTemplateEmail(): Promise<void> {
|
|
480
|
+
const response = await firstValueFrom(
|
|
481
|
+
this.emailSendService.sendTemplate({
|
|
482
|
+
templateId: 'template-id',
|
|
483
|
+
to: 'user@example.com',
|
|
484
|
+
emailConfigId: 'config-id',
|
|
485
|
+
variables: {
|
|
486
|
+
userName: 'John',
|
|
487
|
+
appName: 'My App',
|
|
488
|
+
verificationLink: 'https://example.com/verify/abc123',
|
|
489
|
+
},
|
|
490
|
+
})
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
if (response.data?.success) {
|
|
494
|
+
console.log('Sent! Message ID:', response.data.messageId);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Test Configuration
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
async testConfig(configId: string, recipient: string): Promise<boolean> {
|
|
503
|
+
const response = await firstValueFrom(
|
|
504
|
+
this.emailSendService.testConfiguration({
|
|
505
|
+
emailConfigId: configId,
|
|
506
|
+
recipient: recipient,
|
|
507
|
+
})
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
return response.data?.success ?? false;
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Create Email Template
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { EmailTemplateApiService } from '@flusys/ng-email';
|
|
518
|
+
|
|
519
|
+
private readonly templateService = inject(EmailTemplateApiService);
|
|
520
|
+
|
|
521
|
+
async createTemplate(): Promise<void> {
|
|
522
|
+
const response = await firstValueFrom(
|
|
523
|
+
this.templateService.insert({
|
|
524
|
+
name: 'Welcome Email',
|
|
525
|
+
slug: 'welcome-email',
|
|
526
|
+
description: 'Sent to new users',
|
|
527
|
+
subject: 'Welcome {{userName}}!',
|
|
528
|
+
isHtml: true,
|
|
529
|
+
htmlContent: '<h1>Welcome, {{userName}}!</h1><p>Thank you for joining.</p>',
|
|
530
|
+
textContent: 'Welcome, {{userName}}! Thank you for joining.',
|
|
531
|
+
isActive: true,
|
|
532
|
+
})
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
if (response.success) {
|
|
536
|
+
console.log('Template created:', response.data?.id);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
---
|
|
542
|
+
|
|
543
|
+
## Backend Integration
|
|
544
|
+
|
|
545
|
+
All endpoints use **POST-only RPC** style (not REST).
|
|
546
|
+
|
|
547
|
+
| Service | Endpoint | Description |
|
|
548
|
+
|---------|----------|-------------|
|
|
549
|
+
| Config | `POST /email/config/insert` | Create config |
|
|
550
|
+
| Config | `POST /email/config/get/:id` | Get config by ID |
|
|
551
|
+
| Config | `POST /email/config/get-all` | List configs (paginated) |
|
|
552
|
+
| Config | `POST /email/config/update` | Update config |
|
|
553
|
+
| Config | `POST /email/config/delete` | Delete config |
|
|
554
|
+
| Template | `POST /email/template/insert` | Create template |
|
|
555
|
+
| Template | `POST /email/template/get/:id` | Get template by ID |
|
|
556
|
+
| Template | `POST /email/template/get-all` | List templates (paginated) |
|
|
557
|
+
| Template | `POST /email/template/update` | Update template |
|
|
558
|
+
| Template | `POST /email/template/delete` | Delete template |
|
|
559
|
+
| Send | `POST /email/send/direct` | Send email directly |
|
|
560
|
+
| Send | `POST /email/send/template` | Send using template |
|
|
561
|
+
| Send | `POST /email/send/test` | Test configuration |
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Best Practices
|
|
566
|
+
|
|
567
|
+
### 1. Use Template Slugs for Code References
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
// ✅ Use slug for programmatic access
|
|
571
|
+
await this.emailSendService.sendTemplate({
|
|
572
|
+
templateSlug: 'welcome-email', // Stable identifier
|
|
573
|
+
...
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// ❌ Don't hardcode template IDs
|
|
577
|
+
await this.emailSendService.sendTemplate({
|
|
578
|
+
templateId: 'f47ac10b-58cc-4372-a567-0e02b2c3d479', // May change
|
|
579
|
+
...
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### 2. Provide Both HTML and Text Content
|
|
584
|
+
|
|
585
|
+
```typescript
|
|
586
|
+
// ✅ Provide both for maximum compatibility
|
|
587
|
+
{
|
|
588
|
+
isHtml: true,
|
|
589
|
+
htmlContent: '<h1>Hello!</h1>',
|
|
590
|
+
textContent: 'Hello!', // Fallback for text-only clients
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### 3. Test Configurations Before Production
|
|
595
|
+
|
|
596
|
+
Always use the test send feature to verify configurations work before using them in production.
|
|
597
|
+
|
|
598
|
+
### 4. Use Meaningful Variable Names
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
// ✅ Clear, descriptive variable names
|
|
602
|
+
"Hello {{userName}}, your order #{{orderNumber}} shipped on {{shipDate}}."
|
|
603
|
+
|
|
604
|
+
// ❌ Vague or abbreviated names
|
|
605
|
+
"Hello {{u}}, order {{o}} shipped {{d}}."
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### 5. Handle Send Failures Gracefully
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
async sendEmail(): Promise<void> {
|
|
612
|
+
const response = await firstValueFrom(
|
|
613
|
+
this.emailSendService.sendTemplate({ ... })
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
if (response.data?.success) {
|
|
617
|
+
this.messageService.add({
|
|
618
|
+
severity: 'success',
|
|
619
|
+
summary: 'Email Sent',
|
|
620
|
+
detail: `Message ID: ${response.data.messageId}`,
|
|
621
|
+
});
|
|
622
|
+
} else {
|
|
623
|
+
this.messageService.add({
|
|
624
|
+
severity: 'error',
|
|
625
|
+
summary: 'Send Failed',
|
|
626
|
+
detail: response.data?.error || 'Unknown error occurred',
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Public API Exports
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
// Interfaces
|
|
638
|
+
export {
|
|
639
|
+
IEmailConfig, ICreateEmailConfigDto, IUpdateEmailConfigDto,
|
|
640
|
+
ISmtpConfig, ISendGridConfig, IMailgunConfig,
|
|
641
|
+
IEmailTemplate, ICreateEmailTemplateDto, IUpdateEmailTemplateDto,
|
|
642
|
+
ISendEmailDto, ISendTemplateEmailDto, ITestEmailDto,
|
|
643
|
+
IEmailAttachmentDto, IEmailSendResult,
|
|
644
|
+
} from '@flusys/ng-email';
|
|
645
|
+
|
|
646
|
+
// Services
|
|
647
|
+
export {
|
|
648
|
+
EmailConfigApiService,
|
|
649
|
+
EmailTemplateApiService,
|
|
650
|
+
EmailSendService,
|
|
651
|
+
} from '@flusys/ng-email';
|
|
652
|
+
|
|
653
|
+
// Routes
|
|
654
|
+
export { EMAIL_ROUTES } from '@flusys/ng-email';
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
**Last Updated:** 2026-02-17
|
|
660
|
+
**Angular Version:** 21
|