@flusys/ng-email 4.0.2 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -521
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,98 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @flusys/ng-email
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
> Email management UI library for the FLUSYS Angular platform — provider configuration, visual template builder, and programmatic email sending.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/ng-email)
|
|
6
|
+
[](https://angular.io)
|
|
7
|
+
[](https://www.typescriptlang.org)
|
|
8
|
+
[](LICENSE)
|
|
6
9
|
|
|
7
10
|
---
|
|
8
11
|
|
|
9
|
-
##
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [Overview](#overview)
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Compatibility](#compatibility)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Quick Start](#quick-start)
|
|
19
|
+
- [Email Schema](#email-schema)
|
|
20
|
+
- [IEmailSchema](#iemailschema)
|
|
21
|
+
- [Block Types](#block-types)
|
|
22
|
+
- [Services](#services)
|
|
23
|
+
- [EmailProviderApiService](#emailproviderapiservice)
|
|
24
|
+
- [EmailTemplateApiService](#emailtemplateapiservice)
|
|
25
|
+
- [EmailSendService](#emailsendservice)
|
|
26
|
+
- [EmailBuilderStateService](#emailbuilderstateservice)
|
|
27
|
+
- [Components](#components)
|
|
28
|
+
- [EmailBuilderComponent](#emailbuildercomponent)
|
|
29
|
+
- [EmailPreviewComponent](#emailpreviewcomponent)
|
|
30
|
+
- [Email Provider Enum](#email-provider-enum)
|
|
31
|
+
- [API Endpoints](#api-endpoints)
|
|
32
|
+
- [Configuration Reference](#configuration-reference)
|
|
33
|
+
- [Troubleshooting](#troubleshooting)
|
|
34
|
+
- [License](#license)
|
|
10
35
|
|
|
11
|
-
|
|
36
|
+
---
|
|
12
37
|
|
|
13
|
-
|
|
14
|
-
- **Email Templates** - Visual template management with variable interpolation
|
|
15
|
-
- **HTML/Plain Text** - Toggle between content types with live preview
|
|
16
|
-
- **Test Sending** - Test configurations and templates with dynamic variables
|
|
17
|
-
- **Company Scoping** - Automatic company-scoped queries (when enabled)
|
|
18
|
-
- **Lazy Loading** - All page components are lazy-loaded via routes
|
|
19
|
-
- **Permission Guards** - Route-level access control using `permissionGuard`
|
|
20
|
-
- **Zoneless Ready** - All components use signals for zoneless change detection
|
|
21
|
-
- **Responsive Design** - Mobile-first with horizontal scroll tables
|
|
22
|
-
- **Dark Mode** - Full dark/light mode support
|
|
38
|
+
## Overview
|
|
23
39
|
|
|
24
|
-
|
|
40
|
+
`@flusys/ng-email` provides a complete email management UI for FLUSYS applications. Administrators can configure SMTP/SendGrid/Mailgun providers, design reusable email templates via a block-based visual builder, and developers can send emails programmatically using `EmailSendService`.
|
|
25
41
|
|
|
26
|
-
|
|
27
|
-
@flusys/ng-core <- Foundation (APP_CONFIG, DEFAULT_APP_NAME, getServiceUrl)
|
|
28
|
-
|
|
|
29
|
-
@flusys/ng-shared <- Shared utilities (ApiResourceService, HasPermissionDirective, PermissionValidatorService)
|
|
30
|
-
|
|
|
31
|
-
@flusys/ng-layout <- Layout (LAYOUT_AUTH_STATE for company context)
|
|
32
|
-
|
|
|
33
|
-
@flusys/ng-email <- Email management (THIS PACKAGE)
|
|
34
|
-
```
|
|
42
|
+
Templates are stored as `IEmailSchema` JSON in the backend (`nestjs-email`) and rendered server-side with `{{variable}}` interpolation.
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
---
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
## Features
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
- ✅ Email provider configuration UI (SMTP, SendGrid, Mailgun)
|
|
49
|
+
- ✅ Visual block-based email template builder
|
|
50
|
+
- ✅ `{{variable}}` interpolation with XSS protection
|
|
51
|
+
- ✅ Email template preview (desktop + mobile viewport)
|
|
52
|
+
- ✅ `EmailSendService` for programmatic sending from any feature
|
|
53
|
+
- ✅ Template versioning (draft / published)
|
|
54
|
+
- ✅ File/image insertion via `ng-storage` integration
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
private readonly companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });
|
|
56
|
+
---
|
|
46
57
|
|
|
47
|
-
|
|
48
|
-
() => this.appConfig.enableCompanyFeature && !!this.companyContext,
|
|
49
|
-
);
|
|
58
|
+
## Compatibility
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
| Package | Version |
|
|
61
|
+
|---------|---------|
|
|
62
|
+
| Angular | 21+ |
|
|
63
|
+
| @flusys/ng-core | 4.x |
|
|
64
|
+
| @flusys/ng-shared | 4.x |
|
|
55
65
|
|
|
56
66
|
---
|
|
57
67
|
|
|
58
|
-
##
|
|
68
|
+
## Installation
|
|
59
69
|
|
|
60
|
-
```
|
|
61
|
-
ng-email/
|
|
62
|
-
├── enums/
|
|
63
|
-
│ ├── email-provider.enum.ts # EmailProviderEnum (smtp, sendgrid, mailgun)
|
|
64
|
-
│ ├── email-block-type.enum.ts # EmailBlockType (text, image, button, divider, html)
|
|
65
|
-
│ └── public-api.ts
|
|
66
|
-
├── interfaces/
|
|
67
|
-
│ ├── email-config.interface.ts # IEmailConfig, ICreateEmailConfigDto, ISmtpAuth, ISmtpConfig, ISendGridConfig, IMailgunConfig
|
|
68
|
-
│ ├── email-template.interface.ts # IEmailTemplate, ICreateEmailTemplateDto, IUpdateEmailTemplateDto
|
|
69
|
-
│ ├── email-schema.interface.ts # IEmailSchema, IEmailSection, IEmailBlock, block content types
|
|
70
|
-
│ ├── email-send.interface.ts # ISendEmailDto, ISendTemplateEmailDto, ITestSendResult
|
|
71
|
-
│ └── public-api.ts
|
|
72
|
-
├── services/
|
|
73
|
-
│ ├── email-config-api.service.ts # Config CRUD + test sending
|
|
74
|
-
│ ├── email-template-api.service.ts # Template CRUD + getBySlug
|
|
75
|
-
│ ├── email-send.service.ts # Email sending operations (direct + template)
|
|
76
|
-
│ └── email-builder-state.service.ts # Template builder state management
|
|
77
|
-
├── pages/
|
|
78
|
-
│ ├── email-container/
|
|
79
|
-
│ │ └── email-container.component.ts # Main container with permission-filtered tabs
|
|
80
|
-
│ ├── config/
|
|
81
|
-
│ │ ├── email-config-list.component.ts # Config list with test dialog
|
|
82
|
-
│ │ └── email-config-form.component.ts # Config form with dynamic provider fields
|
|
83
|
-
│ └── template/
|
|
84
|
-
│ ├── template-list.component.ts # Template list with test send dialog
|
|
85
|
-
│ └── template-form.component.ts # Template form with Angular Signal Forms
|
|
86
|
-
├── routes/
|
|
87
|
-
│ └── email.routes.ts # Routes with permission guards
|
|
88
|
-
└── public-api.ts
|
|
70
|
+
```bash
|
|
71
|
+
npm install @flusys/ng-email @flusys/ng-core @flusys/ng-shared
|
|
89
72
|
```
|
|
90
73
|
|
|
91
74
|
---
|
|
92
75
|
|
|
93
|
-
##
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
### 1. Enable Email in Config
|
|
94
79
|
|
|
95
|
-
|
|
80
|
+
```typescript
|
|
81
|
+
// environments/environment.ts
|
|
82
|
+
export const environment = {
|
|
83
|
+
services: {
|
|
84
|
+
email: { enabled: true },
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. Add Email Routes
|
|
96
90
|
|
|
97
91
|
```typescript
|
|
98
92
|
// app.routes.ts
|
|
@@ -101,566 +95,328 @@ import { EMAIL_ROUTES } from '@flusys/ng-email';
|
|
|
101
95
|
export const routes: Routes = [
|
|
102
96
|
{
|
|
103
97
|
path: 'email',
|
|
104
|
-
|
|
98
|
+
loadChildren: () => EMAIL_ROUTES,
|
|
105
99
|
},
|
|
106
100
|
];
|
|
107
101
|
```
|
|
108
102
|
|
|
109
|
-
###
|
|
110
|
-
|
|
111
|
-
| Path | Component | Permission Guard |
|
|
112
|
-
|------|-----------|------------------|
|
|
113
|
-
| `/email` | EmailContainerComponent | - |
|
|
114
|
-
| `/email/templates` | TemplateListComponent | `EMAIL_TEMPLATE_PERMISSIONS.READ` |
|
|
115
|
-
| `/email/templates/new` | TemplateFormComponent | `EMAIL_TEMPLATE_PERMISSIONS.CREATE` |
|
|
116
|
-
| `/email/templates/:id` | TemplateFormComponent | `EMAIL_TEMPLATE_PERMISSIONS.UPDATE` |
|
|
117
|
-
| `/email/configs` | EmailConfigListComponent | `EMAIL_CONFIG_PERMISSIONS.READ` |
|
|
118
|
-
| `/email/configs/new` | EmailConfigFormComponent | `EMAIL_CONFIG_PERMISSIONS.CREATE` |
|
|
119
|
-
| `/email/configs/:id` | EmailConfigFormComponent | `EMAIL_CONFIG_PERMISSIONS.UPDATE` |
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Enums
|
|
124
|
-
|
|
125
|
-
### EmailProviderEnum
|
|
103
|
+
### 3. Send Email Programmatically
|
|
126
104
|
|
|
127
105
|
```typescript
|
|
128
|
-
|
|
129
|
-
SMTP = 'smtp',
|
|
130
|
-
SENDGRID = 'sendgrid',
|
|
131
|
-
MAILGUN = 'mailgun',
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### EmailBlockType
|
|
106
|
+
import { EmailSendService } from '@flusys/ng-email';
|
|
136
107
|
|
|
137
|
-
|
|
138
|
-
export
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
108
|
+
@Injectable({ providedIn: 'root' })
|
|
109
|
+
export class OrderService {
|
|
110
|
+
private emailSend = inject(EmailSendService);
|
|
111
|
+
|
|
112
|
+
sendOrderConfirmation(order: Order): Observable<IMessageResponse> {
|
|
113
|
+
return this.emailSend.send({
|
|
114
|
+
templateKey: 'order-confirmation',
|
|
115
|
+
to: order.customerEmail,
|
|
116
|
+
variables: {
|
|
117
|
+
customerName: order.customerName,
|
|
118
|
+
orderNumber: order.id,
|
|
119
|
+
totalAmount: order.total.toFixed(2),
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
144
123
|
}
|
|
145
124
|
```
|
|
146
125
|
|
|
147
126
|
---
|
|
148
127
|
|
|
149
|
-
##
|
|
150
|
-
|
|
151
|
-
### Email Configuration
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
/** SMTP auth credentials (nodemailer style) */
|
|
155
|
-
interface ISmtpAuth {
|
|
156
|
-
user: string;
|
|
157
|
-
pass: string;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** SMTP provider configuration (nodemailer style) */
|
|
161
|
-
interface ISmtpConfig {
|
|
162
|
-
host: string;
|
|
163
|
-
port: number;
|
|
164
|
-
secure?: boolean;
|
|
165
|
-
auth: ISmtpAuth;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** SendGrid provider configuration */
|
|
169
|
-
interface ISendGridConfig {
|
|
170
|
-
apiKey: string;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** Mailgun provider configuration */
|
|
174
|
-
interface IMailgunConfig {
|
|
175
|
-
apiKey: string;
|
|
176
|
-
domain: string;
|
|
177
|
-
region?: 'us' | 'eu';
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Union type for all provider configurations */
|
|
181
|
-
type EmailProviderConfig = ISmtpConfig | ISendGridConfig | IMailgunConfig;
|
|
128
|
+
## Email Schema
|
|
182
129
|
|
|
183
|
-
|
|
184
|
-
interface IEmailConfig extends IBaseEntity {
|
|
185
|
-
name: string;
|
|
186
|
-
provider: EmailProviderEnum;
|
|
187
|
-
config: EmailProviderConfig;
|
|
188
|
-
fromEmail: string | null;
|
|
189
|
-
fromName: string | null;
|
|
190
|
-
isActive: boolean;
|
|
191
|
-
isDefault: boolean;
|
|
192
|
-
companyId?: string | null;
|
|
193
|
-
}
|
|
130
|
+
### IEmailSchema
|
|
194
131
|
|
|
195
|
-
|
|
196
|
-
interface ICreateEmailConfigDto {
|
|
197
|
-
name: string;
|
|
198
|
-
provider: EmailProviderEnum;
|
|
199
|
-
config: EmailProviderConfig;
|
|
200
|
-
fromEmail?: string;
|
|
201
|
-
fromName?: string;
|
|
202
|
-
isActive?: boolean;
|
|
203
|
-
isDefault?: boolean;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** Update email config DTO */
|
|
207
|
-
interface IUpdateEmailConfigDto extends Partial<ICreateEmailConfigDto> {
|
|
208
|
-
id: string;
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Email Template
|
|
132
|
+
Complete email template definition stored as JSON:
|
|
213
133
|
|
|
214
134
|
```typescript
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
name: string;
|
|
218
|
-
slug: string;
|
|
219
|
-
description: string | null;
|
|
220
|
-
subject: string;
|
|
221
|
-
schema: IEmailSchema;
|
|
222
|
-
htmlContent: string;
|
|
223
|
-
textContent: string | null;
|
|
224
|
-
schemaVersion: number;
|
|
225
|
-
isActive: boolean;
|
|
226
|
-
isHtml: boolean;
|
|
227
|
-
metadata: Record<string, unknown> | null;
|
|
228
|
-
companyId?: string | null;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/** Create email template DTO */
|
|
232
|
-
interface ICreateEmailTemplateDto {
|
|
135
|
+
interface IEmailSchema {
|
|
136
|
+
id: string;
|
|
233
137
|
name: string;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
metadata?: Record<string, unknown>;
|
|
138
|
+
key: string; // Unique identifier for programmatic sending
|
|
139
|
+
subject: string; // Supports {{variable}} interpolation
|
|
140
|
+
previewText?: string;
|
|
141
|
+
blocks: IEmailBlock[];
|
|
142
|
+
settings: IEmailSettings;
|
|
143
|
+
isDraft: boolean;
|
|
144
|
+
createdAt: string;
|
|
145
|
+
updatedAt: string;
|
|
243
146
|
}
|
|
244
147
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
148
|
+
interface IEmailSettings {
|
|
149
|
+
backgroundColor: string;
|
|
150
|
+
contentWidth: number; // pixels (default: 600)
|
|
151
|
+
fontFamily: string;
|
|
152
|
+
padding: number; // px
|
|
248
153
|
}
|
|
249
154
|
```
|
|
250
155
|
|
|
251
|
-
###
|
|
252
|
-
|
|
253
|
-
**Type Aliases:**
|
|
254
|
-
- `EmailVariableType` = `'string' | 'number' | 'boolean' | 'date'`
|
|
255
|
-
- `EmailSectionType` = `'header' | 'body' | 'footer'`
|
|
256
|
-
- `EmailBlockContent` = `ITextBlockContent | IImageBlockContent | IButtonBlockContent | IDividerBlockContent | IHtmlBlockContent`
|
|
257
|
-
|
|
258
|
-
**Block Content Types:**
|
|
156
|
+
### Block Types
|
|
259
157
|
|
|
260
|
-
|
|
261
|
-
|-----------|--------|
|
|
262
|
-
| `ITextBlockContent` | `text`, `html?` |
|
|
263
|
-
| `IImageBlockContent` | `src`, `alt?`, `link?`, `width?`, `height?` |
|
|
264
|
-
| `IButtonBlockContent` | `text`, `link`, `backgroundColor?`, `textColor?`, `borderRadius?` |
|
|
265
|
-
| `IDividerBlockContent` | `height?`, `color?`, `style?` (solid\|dashed\|dotted) |
|
|
266
|
-
| `IHtmlBlockContent` | `html` |
|
|
158
|
+
Email templates are composed of blocks:
|
|
267
159
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
|
271
|
-
|
|
272
|
-
| `
|
|
273
|
-
| `
|
|
274
|
-
| `
|
|
275
|
-
| `
|
|
276
|
-
| `
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
279
|
-
|
|
280
|
-
### Schema Helper Functions
|
|
160
|
+
| Block Type | Description | Key Properties |
|
|
161
|
+
|------------|-------------|----------------|
|
|
162
|
+
| `header` | Logo + company name header | `logoFileId`, `companyName`, `backgroundColor` |
|
|
163
|
+
| `text` | Rich text paragraph | `content` (HTML), `fontSize`, `color`, `align` |
|
|
164
|
+
| `heading` | H1-H4 heading | `content`, `level`, `color`, `align` |
|
|
165
|
+
| `button` | Call-to-action button | `label`, `url`, `backgroundColor`, `textColor` |
|
|
166
|
+
| `image` | Image block | `fileId`, `altText`, `width`, `link` |
|
|
167
|
+
| `divider` | Horizontal rule | `color`, `thickness` |
|
|
168
|
+
| `spacer` | Vertical whitespace | `height` |
|
|
169
|
+
| `columns` | Two-column layout | `leftBlock`, `rightBlock` |
|
|
170
|
+
| `footer` | Unsubscribe/legal footer | `content`, `unsubscribeUrl` |
|
|
281
171
|
|
|
282
172
|
```typescript
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
fontFamily: 'Arial, sans-serif',
|
|
288
|
-
baseTextColor: '#333333',
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
/** Create a new email schema with default sections */
|
|
292
|
-
function createEmailSchema(partial: Partial<IEmailSchema> = {}): IEmailSchema {
|
|
293
|
-
return {
|
|
294
|
-
id: partial.id ?? crypto.randomUUID(),
|
|
295
|
-
version: partial.version ?? '1.0.0',
|
|
296
|
-
name: partial.name ?? 'Untitled Template',
|
|
297
|
-
subject: partial.subject ?? '',
|
|
298
|
-
preheader: partial.preheader,
|
|
299
|
-
sections: partial.sections ?? createDefaultSections(),
|
|
300
|
-
variables: partial.variables ?? [],
|
|
301
|
-
settings: { ...DEFAULT_EMAIL_SETTINGS, ...partial.settings },
|
|
302
|
-
};
|
|
173
|
+
interface IEmailBlock {
|
|
174
|
+
id: string;
|
|
175
|
+
type: EmailBlockType;
|
|
176
|
+
[key: string]: unknown; // Block-specific properties
|
|
303
177
|
}
|
|
304
178
|
```
|
|
305
179
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
/** Test send result */
|
|
310
|
-
interface ITestSendResult {
|
|
311
|
-
success: boolean;
|
|
312
|
-
messageId?: string;
|
|
313
|
-
error?: string;
|
|
314
|
-
}
|
|
180
|
+
**Variable Interpolation:**
|
|
315
181
|
|
|
316
|
-
|
|
317
|
-
interface ISendEmailDto {
|
|
318
|
-
to: string | string[];
|
|
319
|
-
cc?: string | string[];
|
|
320
|
-
bcc?: string | string[];
|
|
321
|
-
subject: string;
|
|
322
|
-
html: string;
|
|
323
|
-
text?: string;
|
|
324
|
-
from?: string;
|
|
325
|
-
fromName?: string;
|
|
326
|
-
replyTo?: string;
|
|
327
|
-
emailConfigId?: string;
|
|
328
|
-
}
|
|
182
|
+
Use `{{variableName}}` in any text content:
|
|
329
183
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
templateSlug?: string;
|
|
334
|
-
to: string | string[];
|
|
335
|
-
cc?: string | string[];
|
|
336
|
-
bcc?: string | string[];
|
|
337
|
-
variables?: Record<string, any>;
|
|
338
|
-
from?: string;
|
|
339
|
-
fromName?: string;
|
|
340
|
-
replyTo?: string;
|
|
341
|
-
emailConfigId?: string;
|
|
342
|
-
}
|
|
184
|
+
```
|
|
185
|
+
Subject: "Order {{orderNumber}} Confirmed"
|
|
186
|
+
Content: "Hi {{customerName}}, your order totaling ${{totalAmount}} is confirmed."
|
|
343
187
|
```
|
|
344
188
|
|
|
189
|
+
Variables are XSS-sanitized server-side before rendering.
|
|
190
|
+
|
|
345
191
|
---
|
|
346
192
|
|
|
347
193
|
## Services
|
|
348
194
|
|
|
349
|
-
###
|
|
195
|
+
### EmailProviderApiService
|
|
350
196
|
|
|
351
|
-
|
|
197
|
+
Manages email provider configuration (SMTP, SendGrid, Mailgun):
|
|
352
198
|
|
|
353
199
|
```typescript
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
// Resource name, HttpClient, service prefix
|
|
363
|
-
super('email-config', inject(HttpClient), 'email');
|
|
200
|
+
import { EmailProviderApiService } from '@flusys/ng-email';
|
|
201
|
+
|
|
202
|
+
@Injectable({ ... })
|
|
203
|
+
export class MyService {
|
|
204
|
+
private providerApi = inject(EmailProviderApiService);
|
|
205
|
+
|
|
206
|
+
getProviders(): Observable<IListResponse<IEmailProvider>> {
|
|
207
|
+
return this.providerApi.getAll({});
|
|
364
208
|
}
|
|
365
209
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
210
|
+
configureSmtp(config: ISmtpConfig): Observable<ISingleResponse<IEmailProvider>> {
|
|
211
|
+
return this.providerApi.insert({
|
|
212
|
+
type: EmailProviderEnum.SMTP,
|
|
213
|
+
...config,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
testProvider(id: string): Observable<IMessageResponse> {
|
|
218
|
+
return this.providerApi.test(id);
|
|
373
219
|
}
|
|
374
220
|
}
|
|
375
221
|
```
|
|
376
222
|
|
|
377
|
-
**Inherited Methods from ApiResourceService:**
|
|
378
|
-
- `getAll(search, queryParams)` - List with pagination
|
|
379
|
-
- `findByIdAsync(id)` - Get single by ID
|
|
380
|
-
- `insertAsync(dto)` - Create new
|
|
381
|
-
- `updateAsync(dto)` - Update existing
|
|
382
|
-
- `deleteAsync({ id, type })` - Delete
|
|
383
|
-
|
|
384
223
|
### EmailTemplateApiService
|
|
385
224
|
|
|
386
|
-
|
|
225
|
+
Manages email template CRUD and publishing:
|
|
387
226
|
|
|
388
227
|
```typescript
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
228
|
+
import { EmailTemplateApiService } from '@flusys/ng-email';
|
|
229
|
+
|
|
230
|
+
@Injectable({ ... })
|
|
231
|
+
export class MyService {
|
|
232
|
+
private templateApi = inject(EmailTemplateApiService);
|
|
233
|
+
|
|
234
|
+
getTemplates(): Observable<IListResponse<IEmailTemplate>> {
|
|
235
|
+
return this.templateApi.getAll({ pageSize: 50 });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
getByKey(key: string): Observable<ISingleResponse<IEmailTemplate>> {
|
|
239
|
+
return this.templateApi.getByKey(key);
|
|
396
240
|
}
|
|
397
241
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return this.http.post<ISingleResponse<IEmailTemplate | null>>(
|
|
401
|
-
`${this.baseUrl}/get-by-slug`,
|
|
402
|
-
{ slug },
|
|
403
|
-
);
|
|
242
|
+
publish(id: string): Observable<IMessageResponse> {
|
|
243
|
+
return this.templateApi.publish(id);
|
|
404
244
|
}
|
|
405
245
|
}
|
|
406
246
|
```
|
|
407
247
|
|
|
408
248
|
### EmailSendService
|
|
409
249
|
|
|
410
|
-
|
|
250
|
+
Programmatic email sending from any feature module:
|
|
411
251
|
|
|
412
252
|
```typescript
|
|
253
|
+
import { EmailSendService } from '@flusys/ng-email';
|
|
254
|
+
|
|
413
255
|
@Injectable({ providedIn: 'root' })
|
|
414
|
-
export class
|
|
415
|
-
private
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
256
|
+
export class NotificationBridgeService {
|
|
257
|
+
private emailSend = inject(EmailSendService);
|
|
258
|
+
|
|
259
|
+
sendWelcomeEmail(user: IUser): Observable<IMessageResponse> {
|
|
260
|
+
return this.emailSend.send({
|
|
261
|
+
templateKey: 'welcome',
|
|
262
|
+
to: user.email,
|
|
263
|
+
variables: {
|
|
264
|
+
firstName: user.firstName,
|
|
265
|
+
loginUrl: 'https://app.example.com/auth/login',
|
|
266
|
+
},
|
|
267
|
+
});
|
|
425
268
|
}
|
|
426
269
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
);
|
|
270
|
+
sendPasswordReset(email: string, resetToken: string): Observable<IMessageResponse> {
|
|
271
|
+
return this.emailSend.send({
|
|
272
|
+
templateKey: 'password-reset',
|
|
273
|
+
to: email,
|
|
274
|
+
variables: { resetToken, expiresIn: '24 hours' },
|
|
275
|
+
});
|
|
433
276
|
}
|
|
434
277
|
}
|
|
435
278
|
```
|
|
436
279
|
|
|
280
|
+
**EmailSendService.send() Options:**
|
|
281
|
+
|
|
282
|
+
| Field | Type | Required | Description |
|
|
283
|
+
|-------|------|----------|-------------|
|
|
284
|
+
| `templateKey` | `string` | ✅ | Unique template key |
|
|
285
|
+
| `to` | `string \| string[]` | ✅ | Recipient(s) |
|
|
286
|
+
| `variables` | `Record<string, string \| number>` | — | Template variables |
|
|
287
|
+
| `cc` | `string[]` | — | CC recipients |
|
|
288
|
+
| `bcc` | `string[]` | — | BCC recipients |
|
|
289
|
+
| `replyTo` | `string` | — | Reply-to address |
|
|
290
|
+
| `attachments` | `IEmailAttachment[]` | — | File attachments |
|
|
291
|
+
|
|
437
292
|
### EmailBuilderStateService
|
|
438
293
|
|
|
439
|
-
|
|
294
|
+
Component-scoped service managing the visual builder state:
|
|
440
295
|
|
|
441
296
|
```typescript
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
private
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
// =========================================
|
|
454
|
-
// Public Readonly Signals
|
|
455
|
-
// =========================================
|
|
456
|
-
readonly schema = this._schema.asReadonly();
|
|
457
|
-
readonly isDirty = this._isDirty.asReadonly();
|
|
458
|
-
readonly selectedSectionId = this._selectedSectionId.asReadonly();
|
|
459
|
-
readonly selectedBlockId = this._selectedBlockId.asReadonly();
|
|
460
|
-
readonly viewMode = this._viewMode.asReadonly();
|
|
461
|
-
|
|
462
|
-
// Computed Signals
|
|
463
|
-
readonly sections = computed(() => this._schema().sections);
|
|
464
|
-
readonly templateName = computed(() => this._schema().name);
|
|
465
|
-
readonly subject = computed(() => this._schema().subject);
|
|
466
|
-
readonly settings = computed(() => this._schema().settings ?? DEFAULT_EMAIL_SETTINGS);
|
|
467
|
-
readonly variables = computed(() => this._schema().variables ?? []);
|
|
468
|
-
readonly headerSection = computed(() => this.sections().find((s) => s.type === 'header') ?? null);
|
|
469
|
-
readonly bodySection = computed(() => this.sections().find((s) => s.type === 'body') ?? null);
|
|
470
|
-
readonly footerSection = computed(() => this.sections().find((s) => s.type === 'footer') ?? null);
|
|
471
|
-
readonly selectedSection = computed(() => /* finds section by _selectedSectionId */);
|
|
472
|
-
readonly selectedBlock = computed(() => /* finds block by _selectedBlockId across sections */);
|
|
473
|
-
readonly allBlocks = computed(() => this.sections().flatMap((s) => s.blocks));
|
|
474
|
-
|
|
475
|
-
// Methods documented in table below
|
|
297
|
+
// Component-scoped — not provided at root
|
|
298
|
+
@Component({
|
|
299
|
+
providers: [EmailBuilderStateService],
|
|
300
|
+
})
|
|
301
|
+
export class EmailBuilderComponent {
|
|
302
|
+
private state = inject(EmailBuilderStateService);
|
|
303
|
+
|
|
304
|
+
schema = this.state.schema; // Signal<IEmailSchema>
|
|
305
|
+
selectedBlock = this.state.selectedBlock; // Signal<IEmailBlock | null>
|
|
306
|
+
isDirty = this.state.isDirty; // Signal<boolean>
|
|
307
|
+
previewMode = this.state.previewMode; // Signal<'desktop' | 'mobile'>
|
|
476
308
|
}
|
|
477
309
|
```
|
|
478
310
|
|
|
479
|
-
**Methods:**
|
|
480
|
-
|
|
481
|
-
| Category | Method | Description |
|
|
482
|
-
|----------|--------|-------------|
|
|
483
|
-
| Schema | `loadSchema(schema)` | Load existing schema into builder |
|
|
484
|
-
| Schema | `createNewSchema(name?)` | Create new empty schema |
|
|
485
|
-
| Schema | `updateSchemaMeta(updates)` | Update name, subject, preheader, settings, variables |
|
|
486
|
-
| Schema | `markAsSaved()` | Clear dirty flag |
|
|
487
|
-
| Schema | `setViewMode(mode)` | Set 'visual' or 'html' mode |
|
|
488
|
-
| Section | `updateSection(sectionId, updates)` | Update section properties |
|
|
489
|
-
| Block | `addBlock(sectionId, blockType, block?)` | Add block, returns block ID |
|
|
490
|
-
| Block | `updateBlock(sectionId, blockId, updates)` | Update block properties |
|
|
491
|
-
| Block | `deleteBlock(sectionId, blockId)` | Delete block from section |
|
|
492
|
-
| Block | `reorderBlocks(sectionId, from, to)` | Reorder blocks within section |
|
|
493
|
-
| Block | `duplicateBlock(sectionId, blockId)` | Duplicate block, returns new ID |
|
|
494
|
-
| Selection | `selectSection(sectionId)` | Select section (clears block selection) |
|
|
495
|
-
| Selection | `selectBlock(blockId)` | Select block (auto-selects parent section) |
|
|
496
|
-
| Selection | `clearSelection()` | Clear all selection |
|
|
497
|
-
|
|
498
|
-
**Default Block Content:**
|
|
499
|
-
|
|
500
|
-
| Block Type | Default Content |
|
|
501
|
-
|------------|-----------------|
|
|
502
|
-
| TEXT | `{ text: 'Enter your text here...', html: '<p>Enter your text here...</p>' }` |
|
|
503
|
-
| IMAGE | `{ src: '', alt: 'Image' }` |
|
|
504
|
-
| BUTTON | `{ text: 'Click Here', link: 'https://', backgroundColor: '#007bff', textColor: '#ffffff', borderRadius: '4px' }` |
|
|
505
|
-
| DIVIDER | `{ height: '1px', color: '#cccccc', style: 'solid' }` |
|
|
506
|
-
| HTML | `{ html: '<!-- Custom HTML -->' }` |
|
|
507
|
-
|
|
508
311
|
---
|
|
509
312
|
|
|
510
313
|
## Components
|
|
511
314
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
### Component Overview
|
|
515
|
-
|
|
516
|
-
| Component | Purpose | Key Features |
|
|
517
|
-
|-----------|---------|--------------|
|
|
518
|
-
| `EmailContainerComponent` | Tab container | Permission-filtered tabs, horizontal scroll, router outlet |
|
|
519
|
-
| `EmailConfigListComponent` | Config list | Lazy pagination, provider metadata badges, test dialog |
|
|
520
|
-
| `EmailConfigFormComponent` | Config form | Dynamic provider fields, type guards, signal-based form |
|
|
521
|
-
| `TemplateListComponent` | Template list | Lazy pagination, variable extraction, test send dialog |
|
|
522
|
-
| `TemplateFormComponent` | Template form | Angular Signal Forms, HTML/Text toggle, live preview |
|
|
315
|
+
### EmailBuilderComponent
|
|
523
316
|
|
|
524
|
-
|
|
317
|
+
Visual block-based email template editor:
|
|
525
318
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
319
|
+
```html
|
|
320
|
+
<flusys-email-builder
|
|
321
|
+
[templateId]="templateId"
|
|
322
|
+
(saved)="onTemplateSaved($event)"
|
|
323
|
+
/>
|
|
324
|
+
```
|
|
531
325
|
|
|
532
|
-
|
|
326
|
+
Features:
|
|
327
|
+
- Left panel: Block type palette
|
|
328
|
+
- Center: Email canvas (drag to reorder blocks)
|
|
329
|
+
- Right panel: Selected block properties
|
|
330
|
+
- Top bar: Save draft / Publish / Preview toggle
|
|
533
331
|
|
|
534
|
-
###
|
|
332
|
+
### EmailPreviewComponent
|
|
535
333
|
|
|
536
|
-
|
|
334
|
+
Renders an email template with mock variable values:
|
|
537
335
|
|
|
538
|
-
|
|
336
|
+
```html
|
|
337
|
+
<flusys-email-preview
|
|
338
|
+
[schema]="emailSchema"
|
|
339
|
+
[variables]="mockVariables"
|
|
340
|
+
[viewport]="'desktop'"
|
|
341
|
+
/>
|
|
342
|
+
```
|
|
539
343
|
|
|
540
|
-
|
|
344
|
+
| Input | Type | Description |
|
|
345
|
+
|-------|------|-------------|
|
|
346
|
+
| `schema` | `IEmailSchema` | Template to preview |
|
|
347
|
+
| `variables` | `Record<string, string>` | Mock values for `{{vars}}` |
|
|
348
|
+
| `viewport` | `'desktop' \| 'mobile'` | Preview viewport width |
|
|
541
349
|
|
|
542
|
-
|
|
350
|
+
---
|
|
543
351
|
|
|
544
|
-
|
|
352
|
+
## Email Provider Enum
|
|
545
353
|
|
|
546
|
-
|
|
354
|
+
```typescript
|
|
355
|
+
import { EmailProviderEnum } from '@flusys/ng-email';
|
|
547
356
|
|
|
548
|
-
|
|
357
|
+
enum EmailProviderEnum {
|
|
358
|
+
SMTP = 'SMTP',
|
|
359
|
+
SENDGRID = 'SENDGRID',
|
|
360
|
+
MAILGUN = 'MAILGUN',
|
|
361
|
+
}
|
|
362
|
+
```
|
|
549
363
|
|
|
550
364
|
---
|
|
551
365
|
|
|
552
|
-
##
|
|
553
|
-
|
|
554
|
-
|
|
|
555
|
-
|
|
556
|
-
|
|
|
557
|
-
|
|
|
558
|
-
|
|
|
559
|
-
|
|
|
560
|
-
|
|
|
366
|
+
## API Endpoints
|
|
367
|
+
|
|
368
|
+
| Method | Endpoint | Description |
|
|
369
|
+
|--------|----------|-------------|
|
|
370
|
+
| POST | `/email/provider/get-all` | List email providers |
|
|
371
|
+
| POST | `/email/provider/get/:id` | Get provider config |
|
|
372
|
+
| POST | `/email/provider/insert` | Add provider |
|
|
373
|
+
| POST | `/email/provider/update` | Update provider |
|
|
374
|
+
| POST | `/email/provider/delete` | Delete provider |
|
|
375
|
+
| POST | `/email/provider/test/:id` | Send test email |
|
|
376
|
+
| POST | `/email/template/get-all` | List templates |
|
|
377
|
+
| POST | `/email/template/get/:id` | Get template |
|
|
378
|
+
| POST | `/email/template/insert` | Create template |
|
|
379
|
+
| POST | `/email/template/update` | Update template |
|
|
380
|
+
| POST | `/email/template/delete` | Delete template |
|
|
381
|
+
| POST | `/email/template/publish/:id` | Publish template |
|
|
382
|
+
| POST | `/email/send` | Send email |
|
|
561
383
|
|
|
562
384
|
---
|
|
563
385
|
|
|
564
|
-
##
|
|
386
|
+
## Configuration Reference
|
|
565
387
|
|
|
566
|
-
|
|
388
|
+
| Config Key | Type | Default | Description |
|
|
389
|
+
|------------|------|---------|-------------|
|
|
390
|
+
| `services.email.enabled` | `boolean` | `false` | Enable email module |
|
|
567
391
|
|
|
568
|
-
|
|
569
|
-
|---------|---------|
|
|
570
|
-
| Borders | `border-surface` |
|
|
571
|
-
| Text | `text-muted-color` |
|
|
572
|
-
| Backgrounds | `bg-surface-0 dark:bg-surface-900`, `bg-surface-100 dark:bg-surface-700` |
|
|
573
|
-
| Code blocks | `bg-surface-100 dark:bg-surface-700` |
|
|
574
|
-
| Hover states | `hover:bg-surface-hover` |
|
|
575
|
-
| Surface ground | `bg-surface-ground` |
|
|
392
|
+
Email provider credentials (API keys, SMTP host/port) are configured server-side in `nestjs-email`. The Angular client only calls the send API.
|
|
576
393
|
|
|
577
394
|
---
|
|
578
395
|
|
|
579
|
-
##
|
|
396
|
+
## Troubleshooting
|
|
580
397
|
|
|
581
|
-
|
|
582
|
-
import { EmailSendService, EmailConfigApiService, EmailTemplateApiService, EmailBuilderStateService, EmailBlockType } from '@flusys/ng-email';
|
|
398
|
+
**`EmailSendService.send()` returns 404**
|
|
583
399
|
|
|
584
|
-
|
|
585
|
-
private readonly emailSendService = inject(EmailSendService);
|
|
586
|
-
private readonly configService = inject(EmailConfigApiService);
|
|
587
|
-
private readonly templateService = inject(EmailTemplateApiService);
|
|
400
|
+
Template with the given `key` doesn't exist or is still in draft. Publish the template first via the builder UI or `templateApi.publish(id)`.
|
|
588
401
|
|
|
589
|
-
|
|
590
|
-
const res1 = await firstValueFrom(this.emailSendService.sendDirect({
|
|
591
|
-
to: 'user@example.com', subject: 'Hello!', html: '<h1>Hello</h1>', emailConfigId: 'config-id'
|
|
592
|
-
}));
|
|
402
|
+
**Visual builder blocks not reordering**
|
|
593
403
|
|
|
594
|
-
|
|
595
|
-
const res2 = await firstValueFrom(this.emailSendService.sendTemplate({
|
|
596
|
-
templateSlug: 'welcome-email', to: 'user@example.com', emailConfigId: 'config-id',
|
|
597
|
-
variables: { userName: 'John', appName: 'My App' }
|
|
598
|
-
}));
|
|
404
|
+
CDK drag-and-drop requires `@angular/cdk`. Install it and import `DragDropModule`.
|
|
599
405
|
|
|
600
|
-
|
|
601
|
-
const success = (await firstValueFrom(this.configService.sendTest(configId, recipient))).data?.success;
|
|
406
|
+
**Images in email not displaying for recipients**
|
|
602
407
|
|
|
603
|
-
|
|
604
|
-
const template = (await firstValueFrom(this.templateService.getBySlug('welcome-email'))).data;
|
|
605
|
-
```
|
|
408
|
+
Images are referenced by file ID. The backend resolves these to absolute URLs when rendering. Ensure `ng-storage` is configured and the storage backend is publicly accessible (or uses presigned URLs).
|
|
606
409
|
|
|
607
|
-
**
|
|
410
|
+
**Variables showing as `{{varName}}` in sent emails**
|
|
608
411
|
|
|
609
|
-
|
|
412
|
+
The `variables` object key doesn't match the template placeholder. Check for exact case match: `{{customerName}}` requires `{ customerName: '...' }`.
|
|
610
413
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
All endpoints use **POST-only RPC** style (not REST).
|
|
614
|
-
|
|
615
|
-
| Service | Endpoint | Description |
|
|
616
|
-
|---------|----------|-------------|
|
|
617
|
-
| Config | `POST /email/email-config/insert` | Create config |
|
|
618
|
-
| Config | `POST /email/email-config/get/:id` | Get config by ID |
|
|
619
|
-
| Config | `POST /email/email-config/get-all` | List configs (paginated) |
|
|
620
|
-
| Config | `POST /email/email-config/update` | Update config |
|
|
621
|
-
| Config | `POST /email/email-config/delete` | Delete config |
|
|
622
|
-
| Template | `POST /email/email-template/insert` | Create template |
|
|
623
|
-
| Template | `POST /email/email-template/get/:id` | Get template by ID |
|
|
624
|
-
| Template | `POST /email/email-template/get-all` | List templates (paginated) |
|
|
625
|
-
| Template | `POST /email/email-template/get-by-slug` | Get template by slug |
|
|
626
|
-
| Template | `POST /email/email-template/update` | Update template |
|
|
627
|
-
| Template | `POST /email/email-template/delete` | Delete template |
|
|
628
|
-
| Send | `POST /email/send/direct` | Send email directly |
|
|
629
|
-
| Send | `POST /email/send/template` | Send using template |
|
|
630
|
-
| Send | `POST /email/send/test` | Test configuration |
|
|
414
|
+
**Provider test fails with "Connection refused"**
|
|
631
415
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
## Best Practices
|
|
635
|
-
|
|
636
|
-
| Practice | Description |
|
|
637
|
-
|----------|-------------|
|
|
638
|
-
| **Use Template Slugs** | Use `templateSlug: 'welcome-email'` instead of hardcoded UUIDs |
|
|
639
|
-
| **Provide Both Content Types** | Include both `htmlContent` and `textContent` for email client compatibility |
|
|
640
|
-
| **Test Before Production** | Always use test send feature to verify configurations |
|
|
641
|
-
| **Meaningful Variable Names** | Use `{{userName}}` not `{{u}}` - clear, descriptive names |
|
|
642
|
-
| **Handle Failures** | Check `response.data?.success`, show toast on error (HTTP errors handled by global interceptor) |
|
|
643
|
-
| **Signal-Based Forms** | See [EmailConfigFormComponent](#emailconfigformcomponent) for zoneless pattern |
|
|
644
|
-
| **Component-Level StateService** | `providers: [EmailBuilderStateService]` - never `providedIn: 'root'` |
|
|
416
|
+
SMTP host/port is incorrect or the email provider's server is blocking the connection. Verify credentials in the Email Providers configuration page.
|
|
645
417
|
|
|
646
418
|
---
|
|
647
419
|
|
|
648
|
-
##
|
|
649
|
-
|
|
650
|
-
| Category | Exports |
|
|
651
|
-
|----------|---------|
|
|
652
|
-
| **Enums** | `EmailProviderEnum`, `EmailBlockType` |
|
|
653
|
-
| **Config Interfaces** | `IEmailConfig`, `ICreateEmailConfigDto`, `IUpdateEmailConfigDto`, `ISmtpAuth`, `ISmtpConfig`, `ISendGridConfig`, `IMailgunConfig`, `EmailProviderConfig` |
|
|
654
|
-
| **Template Interfaces** | `IEmailTemplate`, `ICreateEmailTemplateDto`, `IUpdateEmailTemplateDto` |
|
|
655
|
-
| **Schema Interfaces** | `IEmailSchema`, `IEmailSection`, `IEmailBlock`, `IEmailTemplateVariable`, `IEmailSettings`, `IEmailSectionStyle`, `IEmailBlockStyle`, `ITextBlockContent`, `IImageBlockContent`, `IButtonBlockContent`, `IDividerBlockContent`, `IHtmlBlockContent`, `EmailBlockContent`, `EmailVariableType`, `EmailSectionType` |
|
|
656
|
-
| **Send Interfaces** | `ISendEmailDto`, `ISendTemplateEmailDto`, `ITestSendResult` |
|
|
657
|
-
| **Constants** | `DEFAULT_EMAIL_SETTINGS`, `createEmailSchema` |
|
|
658
|
-
| **Services** | `EmailConfigApiService`, `EmailTemplateApiService`, `EmailSendService`, `EmailBuilderStateService` |
|
|
659
|
-
| **Components** | `EmailContainerComponent` |
|
|
660
|
-
| **Routes** | `EMAIL_ROUTES` |
|
|
661
|
-
|
|
662
|
-
---
|
|
420
|
+
## License
|
|
663
421
|
|
|
664
|
-
|
|
665
|
-
**Version:** 3.0.1
|
|
666
|
-
**Angular Version:** 21
|
|
422
|
+
MIT © FLUSYS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/ng-email",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Email management module for FLUSYS Angular applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"peerDependencies": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"@angular/core": ">=21.0.0",
|
|
9
9
|
"@angular/forms": ">=21.0.0",
|
|
10
10
|
"@angular/router": ">=21.0.0",
|
|
11
|
-
"@flusys/ng-core": ">=4.0
|
|
12
|
-
"@flusys/ng-layout": ">=4.0
|
|
13
|
-
"@flusys/ng-shared": ">=4.0
|
|
11
|
+
"@flusys/ng-core": ">=4.1.0",
|
|
12
|
+
"@flusys/ng-layout": ">=4.1.0",
|
|
13
|
+
"@flusys/ng-shared": ">=4.1.0",
|
|
14
14
|
"@primeuix/themes": ">=1.0.0",
|
|
15
15
|
"primeicons": ">=7.0.0",
|
|
16
16
|
"primeng": ">=21.0.0",
|