@flusys/ng-email 1.0.0-rc
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 +872 -0
- package/fesm2022/flusys-ng-email-email-config-form.component-BK195yNS.mjs +713 -0
- package/fesm2022/flusys-ng-email-email-config-form.component-BK195yNS.mjs.map +1 -0
- package/fesm2022/flusys-ng-email-email-config-list.component-DnIJdHdf.mjs +541 -0
- package/fesm2022/flusys-ng-email-email-config-list.component-DnIJdHdf.mjs.map +1 -0
- package/fesm2022/flusys-ng-email-template-form.component-u40UX6yY.mjs +555 -0
- package/fesm2022/flusys-ng-email-template-form.component-u40UX6yY.mjs.map +1 -0
- package/fesm2022/flusys-ng-email-template-list.component-91djzx28.mjs +636 -0
- package/fesm2022/flusys-ng-email-template-list.component-91djzx28.mjs.map +1 -0
- package/fesm2022/flusys-ng-email.mjs +579 -0
- package/fesm2022/flusys-ng-email.mjs.map +1 -0
- package/package.json +33 -0
- package/types/flusys-ng-email.d.ts +455 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-email-email-config-list.component-DnIJdHdf.mjs","sources":["../../../projects/ng-email/pages/config/email-config-list.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport { Router } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\nimport { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';\nimport { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';\nimport { AngularModule, EMAIL_CONFIG_PERMISSIONS, HasPermissionDirective, PrimeModule } from '@flusys/ng-shared';\nimport { ConfirmationService, MessageService } from 'primeng/api';\nimport { EmailProviderEnum } from '../../enums/email-provider.enum';\nimport { IEmailConfig } from '../../interfaces/email-config.interface';\nimport { EmailConfigApiService } from '../../services/email-config-api.service';\n\n/**\n * Email configuration list component\n */\n@Component({\n selector: 'lib-email-config-list',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AngularModule, PrimeModule, HasPermissionDirective],\n providers: [ConfirmationService, MessageService],\n template: `\n <div class=\"card\">\n <div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4\">\n <div>\n <h3 class=\"text-lg sm:text-xl font-semibold m-0\">Email Configurations</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">\n Company: {{ currentCompanyName() }}\n </p>\n }\n </div>\n <p-button\n *hasPermission=\"EMAIL_CONFIG_PERMISSIONS.CREATE\"\n label=\"New Configuration\"\n icon=\"pi pi-plus\"\n (onClick)=\"onCreate()\"\n styleClass=\"w-full sm:w-auto\"\n />\n </div>\n\n <div class=\"overflow-x-auto -mx-4 sm:mx-0\">\n <p-table\n [value]=\"configs()\"\n [loading]=\"isLoading()\"\n [paginator]=\"totalRecords() > 0\"\n [rows]=\"pageSize()\"\n [first]=\"first()\"\n [totalRecords]=\"totalRecords()\"\n [lazy]=\"true\"\n (onLazyLoad)=\"onLazyLoad($event)\"\n [rowsPerPageOptions]=\"[10, 25, 50]\"\n styleClass=\"p-datatable-sm\"\n [tableStyle]=\"{ 'min-width': '50rem' }\"\n >\n <ng-template #header>\n <tr>\n <th>Name</th>\n <th>Provider</th>\n <th class=\"hidden md:table-cell\">From Email</th>\n <th>Status</th>\n <th class=\"hidden lg:table-cell\">Created</th>\n <th class=\"w-[120px]\">Actions</th>\n </tr>\n </ng-template>\n\n <ng-template #body let-config>\n <tr>\n <td>\n <i [class]=\"getProviderIcon(config.provider)\" class=\"mr-2 text-muted-color\"></i>\n {{ config.name }}\n @if (config.isDefault) {\n <p-tag value=\"Default\" severity=\"contrast\" class=\"ml-2\" />\n }\n </td>\n <td>\n <p-tag\n [value]=\"getProviderLabel(config.provider)\"\n [severity]=\"getProviderSeverity(config.provider)\"\n />\n </td>\n <td class=\"hidden md:table-cell\">{{ config.fromEmail || '-' }}</td>\n <td>\n <p-tag\n [value]=\"config.isActive ? 'Active' : 'Inactive'\"\n [severity]=\"config.isActive ? 'success' : 'secondary'\"\n />\n </td>\n <td class=\"hidden lg:table-cell\">{{ config.createdAt | date: 'short' }}</td>\n <td>\n <div class=\"flex gap-1\">\n <p-button\n *hasPermission=\"EMAIL_CONFIG_PERMISSIONS.UPDATE\"\n icon=\"pi pi-send\"\n [text]=\"true\"\n size=\"small\"\n pTooltip=\"Test\"\n (onClick)=\"onTest(config)\"\n />\n <p-button\n *hasPermission=\"EMAIL_CONFIG_PERMISSIONS.UPDATE\"\n icon=\"pi pi-pencil\"\n [text]=\"true\"\n severity=\"secondary\"\n size=\"small\"\n pTooltip=\"Edit\"\n (onClick)=\"onEdit(config.id)\"\n />\n <p-button\n *hasPermission=\"EMAIL_CONFIG_PERMISSIONS.DELETE\"\n icon=\"pi pi-trash\"\n [text]=\"true\"\n severity=\"danger\"\n size=\"small\"\n pTooltip=\"Delete\"\n (onClick)=\"onDelete(config)\"\n />\n </div>\n </td>\n </tr>\n </ng-template>\n\n <ng-template #emptymessage>\n <tr>\n <td colspan=\"6\" class=\"text-center py-4 text-muted-color\">\n No email configurations found. Add a provider to start sending emails.\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n\n <!-- Test Email Dialog -->\n <p-dialog\n header=\"Test Email Configuration\"\n [visible]=\"showTestDialog()\"\n (visibleChange)=\"showTestDialog.set($event)\"\n [modal]=\"true\"\n [style]=\"{ width: '95vw', maxWidth: '400px' }\"\n [breakpoints]=\"{ '575px': '95vw' }\"\n >\n <div class=\"grid gap-4\">\n <div class=\"field\">\n <label class=\"block font-medium mb-2\">Configuration</label>\n <input\n pInputText\n [value]=\"selectedConfig()?.name || ''\"\n class=\"w-full\"\n [disabled]=\"true\"\n />\n </div>\n\n <div class=\"field\">\n <label class=\"block font-medium mb-2\">Provider</label>\n <p-tag\n [value]=\"getProviderLabel(selectedConfig()?.provider || '')\"\n [severity]=\"getProviderSeverity(selectedConfig()?.provider || '')\"\n />\n </div>\n\n <div class=\"field\">\n <label class=\"block font-medium mb-2\">Recipient Email *</label>\n <input\n pInputText\n [ngModel]=\"testRecipient()\"\n (ngModelChange)=\"testRecipient.set($event)\"\n class=\"w-full\"\n placeholder=\"recipient@example.com\"\n type=\"email\"\n />\n <small class=\"text-muted-color mt-1 block\">\n A test email will be sent to this address\n </small>\n </div>\n </div>\n\n <ng-template #footer>\n <p-button\n label=\"Cancel\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"showTestDialog.set(false)\"\n />\n <p-button\n label=\"Send Test\"\n icon=\"pi pi-send\"\n [loading]=\"isSendingTest()\"\n (onClick)=\"sendTestEmail()\"\n />\n </ng-template>\n </p-dialog>\n\n <p-confirmDialog />\n <p-toast />\n `,\n})\nexport class EmailConfigListComponent {\n // Permission constants for template\n readonly EMAIL_CONFIG_PERMISSIONS = EMAIL_CONFIG_PERMISSIONS;\n\n private readonly router = inject(Router);\n private readonly configService = inject(EmailConfigApiService);\n private readonly confirmationService = inject(ConfirmationService);\n private readonly messageService = inject(MessageService);\n private readonly appConfig = inject(APP_CONFIG);\n private readonly companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });\n\n readonly isLoading = signal(false);\n readonly configs = signal<IEmailConfig[]>([]);\n readonly totalRecords = signal(0);\n readonly pageSize = signal(10);\n readonly first = signal(0);\n\n readonly showCompanyInfo = computed(\n () => this.appConfig.enableCompanyFeature && !!this.companyContext,\n );\n readonly currentCompanyName = computed(\n () => this.companyContext?.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME,\n );\n // Test dialog state (signals for zoneless change detection)\n readonly showTestDialog = signal(false);\n readonly selectedConfig = signal<IEmailConfig | null>(null);\n readonly isSendingTest = signal(false);\n readonly testRecipient = signal('');\n\n onCreate(): void {\n this.router.navigate(['/email/configs/new']);\n }\n\n onEdit(configId: string): void {\n this.router.navigate(['/email/configs', configId]);\n }\n\n async loadConfigs(): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await firstValueFrom(\n this.configService.getAll('', {\n pagination: {\n currentPage: Math.floor(this.first() / this.pageSize()),\n pageSize: this.pageSize(),\n },\n filter: {},\n select: [],\n sort: {},\n }),\n );\n\n if (response.success) {\n this.configs.set(response.data ?? []);\n this.totalRecords.set(response.meta?.total ?? 0);\n }\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onLazyLoad(event: { first?: number | null; rows?: number | null }): void {\n this.first.set(event.first ?? 0);\n this.pageSize.set(event.rows ?? 10);\n this.loadConfigs();\n }\n\n getProviderIcon(provider: EmailProviderEnum): string {\n switch (provider) {\n case EmailProviderEnum.SMTP:\n return 'pi pi-server';\n case EmailProviderEnum.SENDGRID:\n return 'pi pi-cloud';\n case EmailProviderEnum.MAILGUN:\n return 'pi pi-cloud';\n default:\n return 'pi pi-envelope';\n }\n }\n\n getProviderLabel(provider: EmailProviderEnum | string): string {\n switch (provider) {\n case EmailProviderEnum.SMTP:\n return 'SMTP';\n case EmailProviderEnum.SENDGRID:\n return 'SendGrid';\n case EmailProviderEnum.MAILGUN:\n return 'Mailgun';\n default:\n return provider || '';\n }\n }\n\n getProviderSeverity(\n provider: EmailProviderEnum | string,\n ): 'success' | 'secondary' | 'info' | 'warn' | 'danger' | 'contrast' {\n switch (provider) {\n case EmailProviderEnum.SMTP:\n return 'info';\n case EmailProviderEnum.SENDGRID:\n return 'success';\n case EmailProviderEnum.MAILGUN:\n return 'warn';\n default:\n return 'secondary';\n }\n }\n\n onTest(config: IEmailConfig): void {\n this.selectedConfig.set(config);\n this.testRecipient.set('');\n this.showTestDialog.set(true);\n }\n\n async sendTestEmail(): Promise<void> {\n const config = this.selectedConfig();\n const recipient = this.testRecipient();\n if (!config || !recipient) {\n this.messageService.add({\n severity: 'warn',\n summary: 'Validation',\n detail: 'Please enter a recipient email address.',\n });\n return;\n }\n\n this.isSendingTest.set(true);\n try {\n const response = await firstValueFrom(\n this.configService.sendTest(config.id, recipient),\n );\n\n if (response.data?.success) {\n this.messageService.add({\n severity: 'success',\n summary: 'Success',\n detail: `Test email sent successfully! Message ID: ${response.data.messageId}`,\n });\n this.showTestDialog.set(false);\n } else {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: response.data?.error || 'Failed to send test email.',\n });\n }\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this.isSendingTest.set(false);\n }\n }\n\n onDelete(config: IEmailConfig): void {\n this.confirmationService.confirm({\n message: `Are you sure you want to delete \"${config.name}\"?`,\n header: 'Delete Configuration',\n icon: 'pi pi-exclamation-triangle',\n acceptButtonStyleClass: 'p-button-danger',\n accept: async () => {\n try {\n await this.configService.deleteAsync({ id: config.id, type: 'delete' });\n this.messageService.add({\n severity: 'success',\n summary: 'Success',\n detail: 'Configuration deleted successfully.',\n });\n this.loadConfigs();\n } catch {\n // Error toast handled by global interceptor\n }\n },\n });\n }\n}\n"],"names":["i6","i7","i8","i9","i10"],"mappings":"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"}
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
+
import { form, required, FormField } from '@angular/forms/signals';
|
|
5
|
+
import * as i1 from '@angular/router';
|
|
6
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
7
|
+
import { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';
|
|
8
|
+
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
9
|
+
import { AngularModule, PrimeModule } from '@flusys/ng-shared';
|
|
10
|
+
import { MessageService } from 'primeng/api';
|
|
11
|
+
import { EmailTemplateApiService, EmailBuilderStateService } from './flusys-ng-email.mjs';
|
|
12
|
+
import * as i2 from 'primeng/button';
|
|
13
|
+
import * as i5 from 'primeng/inputtext';
|
|
14
|
+
import * as i8 from 'primeng/tag';
|
|
15
|
+
import * as i5$1 from 'primeng/textarea';
|
|
16
|
+
import * as i8$1 from 'primeng/toast';
|
|
17
|
+
import * as i7 from 'primeng/toggleswitch';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Email template form component (create/edit)
|
|
21
|
+
* Uses Angular Signal Forms for reactive form handling
|
|
22
|
+
*/
|
|
23
|
+
class TemplateFormComponent {
|
|
24
|
+
route = inject(ActivatedRoute);
|
|
25
|
+
router = inject(Router);
|
|
26
|
+
templateService = inject(EmailTemplateApiService);
|
|
27
|
+
messageService = inject(MessageService);
|
|
28
|
+
appConfig = inject(APP_CONFIG);
|
|
29
|
+
companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });
|
|
30
|
+
state = inject(EmailBuilderStateService);
|
|
31
|
+
// Route params as signal
|
|
32
|
+
routeParams = toSignal(this.route.paramMap);
|
|
33
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
34
|
+
existingTemplate = signal(null, ...(ngDevMode ? [{ debugName: "existingTemplate" }] : []));
|
|
35
|
+
isEditMode = computed(() => !!this.existingTemplate(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
|
|
36
|
+
editorMode = signal('html', ...(ngDevMode ? [{ debugName: "editorMode" }] : []));
|
|
37
|
+
showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature && !!this.companyContext, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
|
|
38
|
+
currentCompanyName = computed(() => this.companyContext?.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
|
|
39
|
+
/** Form model using signal */
|
|
40
|
+
formModel = signal({
|
|
41
|
+
id: '',
|
|
42
|
+
name: '',
|
|
43
|
+
slug: '',
|
|
44
|
+
description: '',
|
|
45
|
+
subject: '',
|
|
46
|
+
htmlContent: '',
|
|
47
|
+
textContent: '',
|
|
48
|
+
isActive: true,
|
|
49
|
+
isHtml: true,
|
|
50
|
+
}, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
51
|
+
/** Form with validation using Angular Signal Forms */
|
|
52
|
+
templateForm = form(this.formModel, (f) => {
|
|
53
|
+
required(f.name, { message: 'Template name is required' });
|
|
54
|
+
required(f.slug, { message: 'Slug is required' });
|
|
55
|
+
required(f.subject, { message: 'Subject is required' });
|
|
56
|
+
});
|
|
57
|
+
/** Check if form is valid */
|
|
58
|
+
isFormValid = computed(() => {
|
|
59
|
+
const model = this.formModel();
|
|
60
|
+
return (model.name.trim().length > 0 &&
|
|
61
|
+
model.slug.trim().length > 0 &&
|
|
62
|
+
model.subject.trim().length > 0);
|
|
63
|
+
}, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
64
|
+
constructor() {
|
|
65
|
+
// Effect to handle route-based initialization
|
|
66
|
+
effect(() => {
|
|
67
|
+
const params = this.routeParams();
|
|
68
|
+
if (!params)
|
|
69
|
+
return;
|
|
70
|
+
const id = params.get('id');
|
|
71
|
+
if (id) {
|
|
72
|
+
this.loadTemplate(id);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async loadTemplate(id) {
|
|
77
|
+
this.isLoading.set(true);
|
|
78
|
+
try {
|
|
79
|
+
const response = await this.templateService.findByIdAsync(id);
|
|
80
|
+
if (response.success && response.data) {
|
|
81
|
+
const template = response.data;
|
|
82
|
+
this.existingTemplate.set(template);
|
|
83
|
+
this.formModel.set({
|
|
84
|
+
id: template.id,
|
|
85
|
+
name: template.name,
|
|
86
|
+
slug: template.slug,
|
|
87
|
+
description: template.description || '',
|
|
88
|
+
subject: template.subject,
|
|
89
|
+
htmlContent: template.htmlContent,
|
|
90
|
+
textContent: template.textContent || '',
|
|
91
|
+
isActive: template.isActive,
|
|
92
|
+
isHtml: template.isHtml ?? true,
|
|
93
|
+
});
|
|
94
|
+
// Set editor mode based on saved template type
|
|
95
|
+
this.editorMode.set(template.isHtml ? 'html' : 'text');
|
|
96
|
+
this.state.loadSchema(template.schema);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
this.isLoading.set(false);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
setEditorMode(mode) {
|
|
104
|
+
const currentMode = this.editorMode();
|
|
105
|
+
const model = this.formModel();
|
|
106
|
+
// Sync content when switching modes
|
|
107
|
+
if (currentMode === 'text' && mode === 'html') {
|
|
108
|
+
// Switching from text to html - convert text to basic HTML if htmlContent is empty
|
|
109
|
+
if (!model.htmlContent && model.textContent) {
|
|
110
|
+
this.formModel.update((m) => ({ ...m, htmlContent: this.textToHtml(m.textContent) }));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (currentMode === 'html' && mode === 'text') {
|
|
114
|
+
// Switching from html to text - generate text from HTML if textContent is empty
|
|
115
|
+
if (!model.textContent && model.htmlContent) {
|
|
116
|
+
this.formModel.update((m) => ({ ...m, textContent: this.htmlToPlainText(m.htmlContent) }));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
this.editorMode.set(mode);
|
|
120
|
+
this.formModel.update((m) => ({ ...m, isHtml: mode === 'html' }));
|
|
121
|
+
}
|
|
122
|
+
/** Convert plain text to basic HTML */
|
|
123
|
+
textToHtml(text) {
|
|
124
|
+
if (!text)
|
|
125
|
+
return '';
|
|
126
|
+
// Convert line breaks to paragraphs
|
|
127
|
+
return text
|
|
128
|
+
.split(/\n\n+/)
|
|
129
|
+
.map((p) => `<p>${p.replace(/\n/g, '<br>')}</p>`)
|
|
130
|
+
.join('\n');
|
|
131
|
+
}
|
|
132
|
+
/** Convert HTML to plain text (using DOMParser for safe parsing) */
|
|
133
|
+
htmlToPlainText(html) {
|
|
134
|
+
if (!html)
|
|
135
|
+
return '';
|
|
136
|
+
// Use DOMParser for safe HTML parsing (prevents XSS)
|
|
137
|
+
const parser = new DOMParser();
|
|
138
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
139
|
+
// Get text content and clean up whitespace
|
|
140
|
+
return (doc.body.textContent || '').replace(/\s+/g, ' ').trim();
|
|
141
|
+
}
|
|
142
|
+
/** Get HTML for preview iframe */
|
|
143
|
+
getPreviewHtml() {
|
|
144
|
+
const baseStyles = `
|
|
145
|
+
<style>
|
|
146
|
+
body {
|
|
147
|
+
font-family: Arial, sans-serif;
|
|
148
|
+
margin: 16px;
|
|
149
|
+
background-color: #ffffff;
|
|
150
|
+
color: #333333;
|
|
151
|
+
}
|
|
152
|
+
img { max-width: 100%; height: auto; }
|
|
153
|
+
</style>
|
|
154
|
+
`;
|
|
155
|
+
return baseStyles + (this.formModel().htmlContent || '');
|
|
156
|
+
}
|
|
157
|
+
async onSave() {
|
|
158
|
+
if (!this.isFormValid()) {
|
|
159
|
+
this.messageService.add({
|
|
160
|
+
severity: 'warn',
|
|
161
|
+
summary: 'Validation',
|
|
162
|
+
detail: 'Please fill in all required fields.',
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.isLoading.set(true);
|
|
167
|
+
try {
|
|
168
|
+
const model = this.formModel();
|
|
169
|
+
const schema = this.state.schema();
|
|
170
|
+
// Use user-provided plain text, or auto-generate from HTML if empty
|
|
171
|
+
const textContent = model.textContent || this.htmlToPlainText(model.htmlContent);
|
|
172
|
+
const data = {
|
|
173
|
+
name: model.name,
|
|
174
|
+
slug: model.slug,
|
|
175
|
+
description: model.description || undefined,
|
|
176
|
+
subject: model.subject,
|
|
177
|
+
schema: {
|
|
178
|
+
...schema,
|
|
179
|
+
name: model.name,
|
|
180
|
+
subject: model.subject,
|
|
181
|
+
},
|
|
182
|
+
htmlContent: model.htmlContent || '<p>Email content</p>',
|
|
183
|
+
textContent: textContent || undefined,
|
|
184
|
+
isActive: model.isActive,
|
|
185
|
+
isHtml: model.isHtml,
|
|
186
|
+
};
|
|
187
|
+
if (this.isEditMode()) {
|
|
188
|
+
await this.templateService.updateAsync({ id: model.id, ...data });
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
await this.templateService.createTemplateAsync(data);
|
|
192
|
+
}
|
|
193
|
+
this.messageService.add({
|
|
194
|
+
severity: 'success',
|
|
195
|
+
summary: 'Success',
|
|
196
|
+
detail: `Template ${this.isEditMode() ? 'updated' : 'created'} successfully.`,
|
|
197
|
+
});
|
|
198
|
+
this.router.navigate(['../'], { relativeTo: this.route });
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// Error toast handled by global interceptor
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
this.isLoading.set(false);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TemplateFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
208
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: TemplateFormComponent, isStandalone: true, selector: "lib-template-form", providers: [MessageService, EmailBuilderStateService], ngImport: i0, template: `
|
|
209
|
+
<div class="card mb-4">
|
|
210
|
+
<!-- Header -->
|
|
211
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
|
|
212
|
+
<div>
|
|
213
|
+
<h3 class="text-lg sm:text-xl font-semibold m-0">
|
|
214
|
+
{{ isEditMode() ? 'Edit Template' : 'New Template' }}
|
|
215
|
+
</h3>
|
|
216
|
+
@if (showCompanyInfo()) {
|
|
217
|
+
<p class="text-sm text-muted-color mt-1">
|
|
218
|
+
Company: {{ currentCompanyName() }}
|
|
219
|
+
</p>
|
|
220
|
+
}
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<!-- Form Fields -->
|
|
225
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
226
|
+
<div class="field">
|
|
227
|
+
<label for="name" class="block font-medium mb-2">Template Name *</label>
|
|
228
|
+
<input
|
|
229
|
+
pInputText
|
|
230
|
+
id="name"
|
|
231
|
+
[formField]="templateForm.name"
|
|
232
|
+
class="w-full"
|
|
233
|
+
placeholder="e.g., Welcome Email"
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div class="field">
|
|
238
|
+
<label for="slug" class="block font-medium mb-2">Slug *</label>
|
|
239
|
+
<input
|
|
240
|
+
pInputText
|
|
241
|
+
id="slug"
|
|
242
|
+
[formField]="templateForm.slug"
|
|
243
|
+
class="w-full"
|
|
244
|
+
placeholder="e.g., welcome-email"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div class="field md:col-span-2">
|
|
249
|
+
<label for="subject" class="block font-medium mb-2">Subject *</label>
|
|
250
|
+
<input
|
|
251
|
+
pInputText
|
|
252
|
+
id="subject"
|
|
253
|
+
[formField]="templateForm.subject"
|
|
254
|
+
class="w-full"
|
|
255
|
+
[placeholder]="'e.g., Welcome to ' + '{{' + 'appName' + '}}' + '!'"
|
|
256
|
+
/>
|
|
257
|
+
<small class="text-muted-color">Use {{ '{{variableName}}' }} for dynamic content</small>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<div class="field">
|
|
261
|
+
<label for="description" class="block font-medium mb-2">Description</label>
|
|
262
|
+
<input
|
|
263
|
+
pInputText
|
|
264
|
+
id="description"
|
|
265
|
+
[formField]="templateForm.description"
|
|
266
|
+
class="w-full"
|
|
267
|
+
placeholder="Brief description of the template"
|
|
268
|
+
/>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div class="field flex items-center gap-2">
|
|
272
|
+
<p-toggleswitch [formField]="templateForm.isActive" />
|
|
273
|
+
<label>Active</label>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<!-- Content Editor & Preview -->
|
|
278
|
+
<div class="grid grid-cols-1 gap-4" [class.lg:grid-cols-2]="editorMode() === 'html'">
|
|
279
|
+
<!-- Editor Panel -->
|
|
280
|
+
<div class="card">
|
|
281
|
+
<div class="flex justify-between items-center mb-3">
|
|
282
|
+
<h3 class="font-semibold">Content</h3>
|
|
283
|
+
<div class="flex gap-1">
|
|
284
|
+
<p-button
|
|
285
|
+
label="HTML"
|
|
286
|
+
[outlined]="editorMode() !== 'html'"
|
|
287
|
+
[severity]="editorMode() === 'html' ? 'primary' : 'secondary'"
|
|
288
|
+
size="small"
|
|
289
|
+
(onClick)="setEditorMode('html')"
|
|
290
|
+
/>
|
|
291
|
+
<p-button
|
|
292
|
+
label="Plain Text"
|
|
293
|
+
[outlined]="editorMode() !== 'text'"
|
|
294
|
+
[severity]="editorMode() === 'text' ? 'primary' : 'secondary'"
|
|
295
|
+
size="small"
|
|
296
|
+
(onClick)="setEditorMode('text')"
|
|
297
|
+
/>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<div class="h-[400px] overflow-auto">
|
|
302
|
+
@if (editorMode() === 'html') {
|
|
303
|
+
<textarea
|
|
304
|
+
pTextarea
|
|
305
|
+
[formField]="templateForm.htmlContent"
|
|
306
|
+
class="w-full h-full font-mono text-sm resize-none !overflow-x-auto"
|
|
307
|
+
placeholder="<html>...</html>"
|
|
308
|
+
wrap="off"
|
|
309
|
+
></textarea>
|
|
310
|
+
} @else {
|
|
311
|
+
<textarea
|
|
312
|
+
pTextarea
|
|
313
|
+
[formField]="templateForm.textContent"
|
|
314
|
+
class="w-full h-full font-mono text-sm resize-none !overflow-x-auto"
|
|
315
|
+
placeholder="Enter plain text content for email clients that don't support HTML"
|
|
316
|
+
wrap="off"
|
|
317
|
+
></textarea>
|
|
318
|
+
}
|
|
319
|
+
</div>
|
|
320
|
+
@if (editorMode() === 'text') {
|
|
321
|
+
<small class="text-muted-color mt-2 block">
|
|
322
|
+
<i class="pi pi-info-circle mr-1"></i>
|
|
323
|
+
Plain text version sent to email clients without HTML support
|
|
324
|
+
</small>
|
|
325
|
+
}
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<!-- Preview Panel (HTML mode only) -->
|
|
329
|
+
@if (editorMode() === 'html') {
|
|
330
|
+
<div class="card">
|
|
331
|
+
<div class="flex flex-wrap justify-between items-center gap-2 mb-3">
|
|
332
|
+
<h3 class="font-semibold m-0">Preview</h3>
|
|
333
|
+
<p-tag value="Live Preview" severity="info" />
|
|
334
|
+
</div>
|
|
335
|
+
<div class="border border-surface rounded bg-surface-0 dark:bg-surface-900 h-[400px] overflow-auto">
|
|
336
|
+
@if (formModel().htmlContent) {
|
|
337
|
+
<iframe
|
|
338
|
+
[srcdoc]="getPreviewHtml()"
|
|
339
|
+
class="w-full h-full border-0"
|
|
340
|
+
sandbox="allow-same-origin"
|
|
341
|
+
></iframe>
|
|
342
|
+
} @else {
|
|
343
|
+
<div class="flex items-center justify-center h-full text-muted-color">
|
|
344
|
+
<div class="text-center p-4">
|
|
345
|
+
<i class="pi pi-eye text-4xl mb-2"></i>
|
|
346
|
+
<p class="m-0">Enter HTML content to see preview</p>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
}
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
}
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<!-- Form Actions -->
|
|
356
|
+
<div class="flex justify-end gap-2 mt-6 pt-4 border-t border-surface">
|
|
357
|
+
<p-button
|
|
358
|
+
label="Cancel"
|
|
359
|
+
severity="secondary"
|
|
360
|
+
[outlined]="true"
|
|
361
|
+
routerLink="../"
|
|
362
|
+
/>
|
|
363
|
+
<p-button
|
|
364
|
+
[label]="isEditMode() ? 'Update' : 'Create'"
|
|
365
|
+
icon="pi pi-save"
|
|
366
|
+
[loading]="isLoading()"
|
|
367
|
+
[disabled]="!isFormValid()"
|
|
368
|
+
(onClick)="onSave()"
|
|
369
|
+
/>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
<p-toast />
|
|
374
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i8.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i5$1.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: i8$1.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i7.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
375
|
+
}
|
|
376
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TemplateFormComponent, decorators: [{
|
|
377
|
+
type: Component,
|
|
378
|
+
args: [{
|
|
379
|
+
selector: 'lib-template-form',
|
|
380
|
+
standalone: true,
|
|
381
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
382
|
+
imports: [AngularModule, PrimeModule, FormField],
|
|
383
|
+
providers: [MessageService, EmailBuilderStateService],
|
|
384
|
+
template: `
|
|
385
|
+
<div class="card mb-4">
|
|
386
|
+
<!-- Header -->
|
|
387
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
|
|
388
|
+
<div>
|
|
389
|
+
<h3 class="text-lg sm:text-xl font-semibold m-0">
|
|
390
|
+
{{ isEditMode() ? 'Edit Template' : 'New Template' }}
|
|
391
|
+
</h3>
|
|
392
|
+
@if (showCompanyInfo()) {
|
|
393
|
+
<p class="text-sm text-muted-color mt-1">
|
|
394
|
+
Company: {{ currentCompanyName() }}
|
|
395
|
+
</p>
|
|
396
|
+
}
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<!-- Form Fields -->
|
|
401
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
402
|
+
<div class="field">
|
|
403
|
+
<label for="name" class="block font-medium mb-2">Template Name *</label>
|
|
404
|
+
<input
|
|
405
|
+
pInputText
|
|
406
|
+
id="name"
|
|
407
|
+
[formField]="templateForm.name"
|
|
408
|
+
class="w-full"
|
|
409
|
+
placeholder="e.g., Welcome Email"
|
|
410
|
+
/>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<div class="field">
|
|
414
|
+
<label for="slug" class="block font-medium mb-2">Slug *</label>
|
|
415
|
+
<input
|
|
416
|
+
pInputText
|
|
417
|
+
id="slug"
|
|
418
|
+
[formField]="templateForm.slug"
|
|
419
|
+
class="w-full"
|
|
420
|
+
placeholder="e.g., welcome-email"
|
|
421
|
+
/>
|
|
422
|
+
</div>
|
|
423
|
+
|
|
424
|
+
<div class="field md:col-span-2">
|
|
425
|
+
<label for="subject" class="block font-medium mb-2">Subject *</label>
|
|
426
|
+
<input
|
|
427
|
+
pInputText
|
|
428
|
+
id="subject"
|
|
429
|
+
[formField]="templateForm.subject"
|
|
430
|
+
class="w-full"
|
|
431
|
+
[placeholder]="'e.g., Welcome to ' + '{{' + 'appName' + '}}' + '!'"
|
|
432
|
+
/>
|
|
433
|
+
<small class="text-muted-color">Use {{ '{{variableName}}' }} for dynamic content</small>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
<div class="field">
|
|
437
|
+
<label for="description" class="block font-medium mb-2">Description</label>
|
|
438
|
+
<input
|
|
439
|
+
pInputText
|
|
440
|
+
id="description"
|
|
441
|
+
[formField]="templateForm.description"
|
|
442
|
+
class="w-full"
|
|
443
|
+
placeholder="Brief description of the template"
|
|
444
|
+
/>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<div class="field flex items-center gap-2">
|
|
448
|
+
<p-toggleswitch [formField]="templateForm.isActive" />
|
|
449
|
+
<label>Active</label>
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<!-- Content Editor & Preview -->
|
|
454
|
+
<div class="grid grid-cols-1 gap-4" [class.lg:grid-cols-2]="editorMode() === 'html'">
|
|
455
|
+
<!-- Editor Panel -->
|
|
456
|
+
<div class="card">
|
|
457
|
+
<div class="flex justify-between items-center mb-3">
|
|
458
|
+
<h3 class="font-semibold">Content</h3>
|
|
459
|
+
<div class="flex gap-1">
|
|
460
|
+
<p-button
|
|
461
|
+
label="HTML"
|
|
462
|
+
[outlined]="editorMode() !== 'html'"
|
|
463
|
+
[severity]="editorMode() === 'html' ? 'primary' : 'secondary'"
|
|
464
|
+
size="small"
|
|
465
|
+
(onClick)="setEditorMode('html')"
|
|
466
|
+
/>
|
|
467
|
+
<p-button
|
|
468
|
+
label="Plain Text"
|
|
469
|
+
[outlined]="editorMode() !== 'text'"
|
|
470
|
+
[severity]="editorMode() === 'text' ? 'primary' : 'secondary'"
|
|
471
|
+
size="small"
|
|
472
|
+
(onClick)="setEditorMode('text')"
|
|
473
|
+
/>
|
|
474
|
+
</div>
|
|
475
|
+
</div>
|
|
476
|
+
|
|
477
|
+
<div class="h-[400px] overflow-auto">
|
|
478
|
+
@if (editorMode() === 'html') {
|
|
479
|
+
<textarea
|
|
480
|
+
pTextarea
|
|
481
|
+
[formField]="templateForm.htmlContent"
|
|
482
|
+
class="w-full h-full font-mono text-sm resize-none !overflow-x-auto"
|
|
483
|
+
placeholder="<html>...</html>"
|
|
484
|
+
wrap="off"
|
|
485
|
+
></textarea>
|
|
486
|
+
} @else {
|
|
487
|
+
<textarea
|
|
488
|
+
pTextarea
|
|
489
|
+
[formField]="templateForm.textContent"
|
|
490
|
+
class="w-full h-full font-mono text-sm resize-none !overflow-x-auto"
|
|
491
|
+
placeholder="Enter plain text content for email clients that don't support HTML"
|
|
492
|
+
wrap="off"
|
|
493
|
+
></textarea>
|
|
494
|
+
}
|
|
495
|
+
</div>
|
|
496
|
+
@if (editorMode() === 'text') {
|
|
497
|
+
<small class="text-muted-color mt-2 block">
|
|
498
|
+
<i class="pi pi-info-circle mr-1"></i>
|
|
499
|
+
Plain text version sent to email clients without HTML support
|
|
500
|
+
</small>
|
|
501
|
+
}
|
|
502
|
+
</div>
|
|
503
|
+
|
|
504
|
+
<!-- Preview Panel (HTML mode only) -->
|
|
505
|
+
@if (editorMode() === 'html') {
|
|
506
|
+
<div class="card">
|
|
507
|
+
<div class="flex flex-wrap justify-between items-center gap-2 mb-3">
|
|
508
|
+
<h3 class="font-semibold m-0">Preview</h3>
|
|
509
|
+
<p-tag value="Live Preview" severity="info" />
|
|
510
|
+
</div>
|
|
511
|
+
<div class="border border-surface rounded bg-surface-0 dark:bg-surface-900 h-[400px] overflow-auto">
|
|
512
|
+
@if (formModel().htmlContent) {
|
|
513
|
+
<iframe
|
|
514
|
+
[srcdoc]="getPreviewHtml()"
|
|
515
|
+
class="w-full h-full border-0"
|
|
516
|
+
sandbox="allow-same-origin"
|
|
517
|
+
></iframe>
|
|
518
|
+
} @else {
|
|
519
|
+
<div class="flex items-center justify-center h-full text-muted-color">
|
|
520
|
+
<div class="text-center p-4">
|
|
521
|
+
<i class="pi pi-eye text-4xl mb-2"></i>
|
|
522
|
+
<p class="m-0">Enter HTML content to see preview</p>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
}
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
}
|
|
529
|
+
</div>
|
|
530
|
+
|
|
531
|
+
<!-- Form Actions -->
|
|
532
|
+
<div class="flex justify-end gap-2 mt-6 pt-4 border-t border-surface">
|
|
533
|
+
<p-button
|
|
534
|
+
label="Cancel"
|
|
535
|
+
severity="secondary"
|
|
536
|
+
[outlined]="true"
|
|
537
|
+
routerLink="../"
|
|
538
|
+
/>
|
|
539
|
+
<p-button
|
|
540
|
+
[label]="isEditMode() ? 'Update' : 'Create'"
|
|
541
|
+
icon="pi pi-save"
|
|
542
|
+
[loading]="isLoading()"
|
|
543
|
+
[disabled]="!isFormValid()"
|
|
544
|
+
(onClick)="onSave()"
|
|
545
|
+
/>
|
|
546
|
+
</div>
|
|
547
|
+
</div>
|
|
548
|
+
|
|
549
|
+
<p-toast />
|
|
550
|
+
`,
|
|
551
|
+
}]
|
|
552
|
+
}], ctorParameters: () => [] });
|
|
553
|
+
|
|
554
|
+
export { TemplateFormComponent };
|
|
555
|
+
//# sourceMappingURL=flusys-ng-email-template-form.component-u40UX6yY.mjs.map
|