@flusys/ng-email 3.0.1 → 4.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 +2 -2
- package/fesm2022/{flusys-ng-email-email-config-form.component-tg4pKP7G.mjs → flusys-ng-email-email-config-form.component-Cj2U6Tgf.mjs} +98 -92
- package/fesm2022/flusys-ng-email-email-config-form.component-Cj2U6Tgf.mjs.map +1 -0
- package/fesm2022/{flusys-ng-email-email-config-list.component-Dt9PY9xD.mjs → flusys-ng-email-email-config-list.component-oznfQvxo.mjs} +81 -70
- package/fesm2022/flusys-ng-email-email-config-list.component-oznfQvxo.mjs.map +1 -0
- package/fesm2022/{flusys-ng-email-template-form.component-vlfHz6VE.mjs → flusys-ng-email-template-form.component-CgJZiMCa.mjs} +67 -61
- package/fesm2022/flusys-ng-email-template-form.component-CgJZiMCa.mjs.map +1 -0
- package/fesm2022/{flusys-ng-email-template-list.component-krrpsjDv.mjs → flusys-ng-email-template-list.component-DHnuMTpB.mjs} +80 -70
- package/fesm2022/flusys-ng-email-template-list.component-DHnuMTpB.mjs.map +1 -0
- package/fesm2022/flusys-ng-email.mjs +256 -31
- package/fesm2022/flusys-ng-email.mjs.map +1 -1
- package/package.json +4 -4
- package/types/flusys-ng-email.d.ts +15 -6
- package/fesm2022/flusys-ng-email-email-config-form.component-tg4pKP7G.mjs.map +0 -1
- package/fesm2022/flusys-ng-email-email-config-list.component-Dt9PY9xD.mjs.map +0 -1
- package/fesm2022/flusys-ng-email-template-form.component-vlfHz6VE.mjs.map +0 -1
- package/fesm2022/flusys-ng-email-template-list.component-krrpsjDv.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-email-email-config-list.component-oznfQvxo.mjs","sources":["../../../projects/ng-email/pages/config/email-config-list.component.ts"],"sourcesContent":["import {\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, TRANSLATE_ADAPTER } from '@flusys/ng-core';\nimport { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';\nimport { AngularModule, EMAIL_CONFIG_PERMISSIONS, HasPermissionDirective, PrimeModule, TranslatePipe } 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 imports: [AngularModule, PrimeModule, HasPermissionDirective, TranslatePipe],\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.config.title' | translate }}</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">\n {{ 'common.company' | translate }}: {{ currentCompanyName() }}\n </p>\n }\n </div>\n <p-button\n *hasPermission=\"EMAIL_CONFIG_PERMISSIONS.CREATE\"\n [label]=\"'email.config.new' | translate\"\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>{{ 'common.name' | translate }}</th>\n <th>{{ 'email.config.provider' | translate }}</th>\n <th class=\"hidden md:table-cell\">{{ 'email.config.from.email' | translate }}</th>\n <th>{{ 'common.status' | translate }}</th>\n <th class=\"hidden lg:table-cell\">{{ 'common.created' | translate }}</th>\n <th class=\"w-[120px]\">{{ 'common.actions' | translate }}</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]=\"'common.default' | translate\" 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 || ('shared.na' | translate) }}</td>\n <td>\n <p-tag\n [value]=\"(config.isActive ? 'common.active' : 'common.inactive') | translate\"\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]=\"'common.test' | translate\"\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]=\"'common.edit' | translate\"\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]=\"'common.delete' | translate\"\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 {{ 'email.config.empty' | translate }}\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n\n <!-- Test Email Dialog -->\n <p-dialog\n [header]=\"'email.config.test.dialog.title' | translate\"\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\">{{ 'email.config.configuration' | translate }}</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\">{{ 'email.config.provider' | translate }}</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\">{{ 'email.config.recipient.email' | translate }} *</label>\n <input\n pInputText\n [ngModel]=\"testRecipient()\"\n (ngModelChange)=\"testRecipient.set($event)\"\n class=\"w-full\"\n [placeholder]=\"'email.recipient.example' | translate\"\n type=\"email\"\n />\n <small class=\"text-muted-color mt-1 block\">\n {{ 'email.config.test.dialog.hint' | translate }}\n </small>\n </div>\n </div>\n\n <ng-template #footer>\n <p-button\n [label]=\"'common.cancel' | translate\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"showTestDialog.set(false)\"\n />\n <p-button\n [label]=\"'email.config.send.test' | translate\"\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 private readonly translateAdapter = inject(TRANSLATE_ADAPTER, { 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 } catch {\n // Error toast handled by global interceptor\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 private readonly providerMeta: Record<\n string,\n { icon: string; labelKey: string; severity: 'success' | 'secondary' | 'info' | 'warn' | 'danger' | 'contrast' }\n > = {\n [EmailProviderEnum.SMTP]: { icon: 'pi pi-server', labelKey: 'email.providers.smtp', severity: 'info' },\n [EmailProviderEnum.SENDGRID]: { icon: 'pi pi-cloud', labelKey: 'email.providers.sendgrid', severity: 'success' },\n [EmailProviderEnum.MAILGUN]: { icon: 'pi pi-cloud', labelKey: 'email.providers.mailgun', severity: 'warn' },\n };\n\n private readonly defaultProviderMeta = { icon: 'pi pi-envelope', labelKey: '', severity: 'secondary' as const };\n\n getProviderIcon(provider: EmailProviderEnum): string {\n return this.providerMeta[provider]?.icon ?? this.defaultProviderMeta.icon;\n }\n\n getProviderLabel(provider: EmailProviderEnum | string): string {\n const labelKey = this.providerMeta[provider]?.labelKey;\n if (labelKey) {\n return this.translate(labelKey);\n }\n // Fallback to dynamic key for unknown providers\n return provider ? this.translate(`email.providers.${provider}`) : '';\n }\n\n getProviderSeverity(provider: EmailProviderEnum | string) {\n return this.providerMeta[provider]?.severity ?? this.defaultProviderMeta.severity;\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: this.translate('common.validation'),\n detail: this.translate('email.config.enter.recipient'),\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: this.translate('common.success'),\n detail: this.translate('email.config.test.sent.success', { messageId: response.data.messageId ?? '' }),\n });\n this.showTestDialog.set(false);\n } else {\n this.messageService.add({\n severity: 'error',\n summary: this.translate('common.error'),\n detail: response.data?.error || this.translate('email.config.test.sent.failed'),\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: this.translate('email.config.delete.confirm', { name: config.name }),\n header: this.translate('email.config.delete.title'),\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: this.translate('common.success'),\n detail: this.translate('email.config.deleted'),\n });\n this.loadConfigs();\n } catch {\n // Error toast handled by global interceptor\n }\n },\n });\n }\n\n private translate(key: string, variables?: Record<string, string | number>): string {\n return this.translateAdapter?.translate(key, variables) ?? key;\n }\n}\n"],"names":["i6","i7","i8","i9"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, signal, computed, effect,
|
|
2
|
+
import { inject, signal, computed, effect, Component } from '@angular/core';
|
|
3
3
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { form, required, FormField } from '@angular/forms/signals';
|
|
5
5
|
import * as i2 from '@angular/router';
|
|
6
6
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
7
|
-
import { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';
|
|
7
|
+
import { APP_CONFIG, TRANSLATE_ADAPTER, DEFAULT_APP_NAME } from '@flusys/ng-core';
|
|
8
8
|
import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
|
|
9
|
-
import { AngularModule, PrimeModule } from '@flusys/ng-shared';
|
|
9
|
+
import { AngularModule, PrimeModule, TranslatePipe } from '@flusys/ng-shared';
|
|
10
10
|
import { MessageService } from 'primeng/api';
|
|
11
11
|
import { EmailTemplateApiService, EmailBuilderStateService } from './flusys-ng-email.mjs';
|
|
12
12
|
import * as i1 from '@angular/forms';
|
|
@@ -28,6 +28,7 @@ class TemplateFormComponent {
|
|
|
28
28
|
messageService = inject(MessageService);
|
|
29
29
|
appConfig = inject(APP_CONFIG);
|
|
30
30
|
companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });
|
|
31
|
+
translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
|
|
31
32
|
state = inject(EmailBuilderStateService);
|
|
32
33
|
// Route params as signal
|
|
33
34
|
routeParams = toSignal(this.route.paramMap);
|
|
@@ -51,9 +52,9 @@ class TemplateFormComponent {
|
|
|
51
52
|
}, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
52
53
|
/** Form with validation using Angular Signal Forms */
|
|
53
54
|
templateForm = form(this.formModel, (f) => {
|
|
54
|
-
required(f.name
|
|
55
|
-
required(f.slug
|
|
56
|
-
required(f.subject
|
|
55
|
+
required(f.name);
|
|
56
|
+
required(f.slug);
|
|
57
|
+
required(f.subject);
|
|
57
58
|
});
|
|
58
59
|
/** Check if form is valid */
|
|
59
60
|
isFormValid = computed(() => {
|
|
@@ -101,6 +102,9 @@ class TemplateFormComponent {
|
|
|
101
102
|
this.state.loadSchema(template.schema);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
105
|
+
catch {
|
|
106
|
+
// Error toast handled by global interceptor
|
|
107
|
+
}
|
|
104
108
|
finally {
|
|
105
109
|
this.isLoading.set(false);
|
|
106
110
|
}
|
|
@@ -163,8 +167,8 @@ class TemplateFormComponent {
|
|
|
163
167
|
if (!this.isFormValid()) {
|
|
164
168
|
this.messageService.add({
|
|
165
169
|
severity: 'warn',
|
|
166
|
-
summary: '
|
|
167
|
-
detail: '
|
|
170
|
+
summary: this.translate('common.validation'),
|
|
171
|
+
detail: this.translate('common.fill.required.fields'),
|
|
168
172
|
});
|
|
169
173
|
return;
|
|
170
174
|
}
|
|
@@ -184,7 +188,7 @@ class TemplateFormComponent {
|
|
|
184
188
|
name: model.name,
|
|
185
189
|
subject: model.subject,
|
|
186
190
|
},
|
|
187
|
-
htmlContent: model.htmlContent || '
|
|
191
|
+
htmlContent: model.htmlContent || `<p>${this.translate('email.template.default.content')}</p>`,
|
|
188
192
|
textContent: textContent || undefined,
|
|
189
193
|
isActive: model.isActive,
|
|
190
194
|
isHtml: model.isHtml,
|
|
@@ -197,8 +201,8 @@ class TemplateFormComponent {
|
|
|
197
201
|
}
|
|
198
202
|
this.messageService.add({
|
|
199
203
|
severity: 'success',
|
|
200
|
-
summary: '
|
|
201
|
-
detail:
|
|
204
|
+
summary: this.translate('common.success'),
|
|
205
|
+
detail: this.translate(this.isEditMode() ? 'email.template.updated' : 'email.template.created'),
|
|
202
206
|
});
|
|
203
207
|
this.router.navigate(['../'], { relativeTo: this.route });
|
|
204
208
|
}
|
|
@@ -209,6 +213,9 @@ class TemplateFormComponent {
|
|
|
209
213
|
this.isLoading.set(false);
|
|
210
214
|
}
|
|
211
215
|
}
|
|
216
|
+
translate(key, variables) {
|
|
217
|
+
return this.translateAdapter?.translate(key, variables) ?? key;
|
|
218
|
+
}
|
|
212
219
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TemplateFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
213
220
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: TemplateFormComponent, isStandalone: true, selector: "lib-template-form", providers: [MessageService, EmailBuilderStateService], ngImport: i0, template: `
|
|
214
221
|
<div class="card mb-4">
|
|
@@ -216,11 +223,11 @@ class TemplateFormComponent {
|
|
|
216
223
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
|
|
217
224
|
<div>
|
|
218
225
|
<h3 class="text-lg sm:text-xl font-semibold m-0">
|
|
219
|
-
{{ isEditMode() ? '
|
|
226
|
+
{{ (isEditMode() ? 'email.template.edit.title' : 'email.template.new.title') | translate }}
|
|
220
227
|
</h3>
|
|
221
228
|
@if (showCompanyInfo()) {
|
|
222
229
|
<p class="text-sm text-muted-color mt-1">
|
|
223
|
-
|
|
230
|
+
{{ 'common.company' | translate }}: {{ currentCompanyName() }}
|
|
224
231
|
</p>
|
|
225
232
|
}
|
|
226
233
|
</div>
|
|
@@ -229,53 +236,53 @@ class TemplateFormComponent {
|
|
|
229
236
|
<!-- Form Fields -->
|
|
230
237
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
231
238
|
<div class="field">
|
|
232
|
-
<label for="name" class="block font-medium mb-2">
|
|
239
|
+
<label for="name" class="block font-medium mb-2">{{ 'email.template.name' | translate }} *</label>
|
|
233
240
|
<input
|
|
234
241
|
pInputText
|
|
235
242
|
id="name"
|
|
236
243
|
[formField]="templateForm.name"
|
|
237
244
|
class="w-full"
|
|
238
|
-
placeholder="
|
|
245
|
+
[placeholder]="'email.template.name.example' | translate"
|
|
239
246
|
/>
|
|
240
247
|
</div>
|
|
241
248
|
|
|
242
249
|
<div class="field">
|
|
243
|
-
<label for="slug" class="block font-medium mb-2">
|
|
250
|
+
<label for="slug" class="block font-medium mb-2">{{ 'email.template.slug' | translate }} *</label>
|
|
244
251
|
<input
|
|
245
252
|
pInputText
|
|
246
253
|
id="slug"
|
|
247
254
|
[formField]="templateForm.slug"
|
|
248
255
|
class="w-full"
|
|
249
|
-
placeholder="
|
|
256
|
+
[placeholder]="'email.template.slug.example' | translate"
|
|
250
257
|
/>
|
|
251
258
|
</div>
|
|
252
259
|
|
|
253
260
|
<div class="field md:col-span-2">
|
|
254
|
-
<label for="subject" class="block font-medium mb-2">
|
|
261
|
+
<label for="subject" class="block font-medium mb-2">{{ 'email.template.subject' | translate }} *</label>
|
|
255
262
|
<input
|
|
256
263
|
pInputText
|
|
257
264
|
id="subject"
|
|
258
265
|
[formField]="templateForm.subject"
|
|
259
266
|
class="w-full"
|
|
260
|
-
[placeholder]="'
|
|
267
|
+
[placeholder]="'email.template.subject.example' | translate"
|
|
261
268
|
/>
|
|
262
|
-
<small class="text-muted-color">
|
|
269
|
+
<small class="text-muted-color">{{ 'email.template.variable.hint' | translate }}</small>
|
|
263
270
|
</div>
|
|
264
271
|
|
|
265
272
|
<div class="field">
|
|
266
|
-
<label for="description" class="block font-medium mb-2">
|
|
273
|
+
<label for="description" class="block font-medium mb-2">{{ 'common.description' | translate }}</label>
|
|
267
274
|
<input
|
|
268
275
|
pInputText
|
|
269
276
|
id="description"
|
|
270
277
|
[formField]="templateForm.description"
|
|
271
278
|
class="w-full"
|
|
272
|
-
placeholder="
|
|
279
|
+
[placeholder]="'email.template.desc.placeholder' | translate"
|
|
273
280
|
/>
|
|
274
281
|
</div>
|
|
275
282
|
|
|
276
283
|
<div class="field flex items-center gap-2">
|
|
277
284
|
<p-toggleswitch [formField]="templateForm.isActive" />
|
|
278
|
-
<label>
|
|
285
|
+
<label>{{ 'common.active' | translate }}</label>
|
|
279
286
|
</div>
|
|
280
287
|
</div>
|
|
281
288
|
|
|
@@ -284,17 +291,17 @@ class TemplateFormComponent {
|
|
|
284
291
|
<!-- Editor Panel -->
|
|
285
292
|
<div class="card">
|
|
286
293
|
<div class="flex justify-between items-center mb-3">
|
|
287
|
-
<h3 class="font-semibold">
|
|
294
|
+
<h3 class="font-semibold">{{ 'email.template.content' | translate }}</h3>
|
|
288
295
|
<div class="flex gap-1">
|
|
289
296
|
<p-button
|
|
290
|
-
label="
|
|
297
|
+
[label]="'email.template.html' | translate"
|
|
291
298
|
[outlined]="editorMode() !== 'html'"
|
|
292
299
|
[severity]="editorMode() === 'html' ? 'primary' : 'secondary'"
|
|
293
300
|
size="small"
|
|
294
301
|
(onClick)="setEditorMode('html')"
|
|
295
302
|
/>
|
|
296
303
|
<p-button
|
|
297
|
-
label="
|
|
304
|
+
[label]="'email.template.plain.text' | translate"
|
|
298
305
|
[outlined]="editorMode() !== 'text'"
|
|
299
306
|
[severity]="editorMode() === 'text' ? 'primary' : 'secondary'"
|
|
300
307
|
size="small"
|
|
@@ -310,7 +317,7 @@ class TemplateFormComponent {
|
|
|
310
317
|
[ngModel]="formModel().htmlContent"
|
|
311
318
|
(ngModelChange)="updateFormField('htmlContent', $event)"
|
|
312
319
|
class="w-full h-full font-mono text-sm resize-none overflow-auto border border-surface rounded"
|
|
313
|
-
placeholder="
|
|
320
|
+
[placeholder]="'email.template.html.placeholder' | translate"
|
|
314
321
|
wrap="off"
|
|
315
322
|
></textarea>
|
|
316
323
|
} @else {
|
|
@@ -319,7 +326,7 @@ class TemplateFormComponent {
|
|
|
319
326
|
[ngModel]="formModel().textContent"
|
|
320
327
|
(ngModelChange)="updateFormField('textContent', $event)"
|
|
321
328
|
class="w-full h-full font-mono text-sm resize-none overflow-auto border border-surface rounded"
|
|
322
|
-
placeholder="
|
|
329
|
+
[placeholder]="'email.template.text.placeholder' | translate"
|
|
323
330
|
wrap="off"
|
|
324
331
|
></textarea>
|
|
325
332
|
}
|
|
@@ -327,7 +334,7 @@ class TemplateFormComponent {
|
|
|
327
334
|
@if (editorMode() === 'text') {
|
|
328
335
|
<small class="text-muted-color mt-2 block">
|
|
329
336
|
<i class="pi pi-info-circle mr-1"></i>
|
|
330
|
-
|
|
337
|
+
{{ 'email.template.plain.text.hint' | translate }}
|
|
331
338
|
</small>
|
|
332
339
|
}
|
|
333
340
|
</div>
|
|
@@ -336,8 +343,8 @@ class TemplateFormComponent {
|
|
|
336
343
|
@if (editorMode() === 'html') {
|
|
337
344
|
<div class="card">
|
|
338
345
|
<div class="flex flex-wrap justify-between items-center gap-2 mb-3">
|
|
339
|
-
<h3 class="font-semibold m-0">
|
|
340
|
-
<p-tag value="
|
|
346
|
+
<h3 class="font-semibold m-0">{{ 'email.template.preview' | translate }}</h3>
|
|
347
|
+
<p-tag [value]="'email.template.live.preview' | translate" severity="info" />
|
|
341
348
|
</div>
|
|
342
349
|
<div class="border border-surface rounded bg-surface-0 dark:bg-surface-900 h-[400px] overflow-auto">
|
|
343
350
|
@if (formModel().htmlContent) {
|
|
@@ -350,7 +357,7 @@ class TemplateFormComponent {
|
|
|
350
357
|
<div class="flex items-center justify-center h-full text-muted-color">
|
|
351
358
|
<div class="text-center p-4">
|
|
352
359
|
<i class="pi pi-eye text-4xl mb-2"></i>
|
|
353
|
-
<p class="m-0">
|
|
360
|
+
<p class="m-0">{{ 'email.template.enter.html.preview' | translate }}</p>
|
|
354
361
|
</div>
|
|
355
362
|
</div>
|
|
356
363
|
}
|
|
@@ -362,13 +369,13 @@ class TemplateFormComponent {
|
|
|
362
369
|
<!-- Form Actions -->
|
|
363
370
|
<div class="flex justify-end gap-2 mt-6 pt-4 border-t border-surface">
|
|
364
371
|
<p-button
|
|
365
|
-
label="
|
|
372
|
+
[label]="'common.cancel' | translate"
|
|
366
373
|
severity="secondary"
|
|
367
374
|
[outlined]="true"
|
|
368
375
|
routerLink="../"
|
|
369
376
|
/>
|
|
370
377
|
<p-button
|
|
371
|
-
[label]="isEditMode() ? '
|
|
378
|
+
[label]="(isEditMode() ? 'common.update' : 'common.create') | translate"
|
|
372
379
|
icon="pi pi-save"
|
|
373
380
|
[loading]="isLoading()"
|
|
374
381
|
[disabled]="!isFormValid()"
|
|
@@ -378,14 +385,13 @@ class TemplateFormComponent {
|
|
|
378
385
|
</div>
|
|
379
386
|
|
|
380
387
|
<p-toast />
|
|
381
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2$1.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: i6.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: i8$2.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"] }
|
|
388
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2$1.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: i6.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: i8$2.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"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
382
389
|
}
|
|
383
390
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TemplateFormComponent, decorators: [{
|
|
384
391
|
type: Component,
|
|
385
392
|
args: [{
|
|
386
393
|
selector: 'lib-template-form',
|
|
387
|
-
|
|
388
|
-
imports: [AngularModule, PrimeModule, FormField],
|
|
394
|
+
imports: [AngularModule, PrimeModule, FormField, TranslatePipe],
|
|
389
395
|
providers: [MessageService, EmailBuilderStateService],
|
|
390
396
|
template: `
|
|
391
397
|
<div class="card mb-4">
|
|
@@ -393,11 +399,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
393
399
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
|
|
394
400
|
<div>
|
|
395
401
|
<h3 class="text-lg sm:text-xl font-semibold m-0">
|
|
396
|
-
{{ isEditMode() ? '
|
|
402
|
+
{{ (isEditMode() ? 'email.template.edit.title' : 'email.template.new.title') | translate }}
|
|
397
403
|
</h3>
|
|
398
404
|
@if (showCompanyInfo()) {
|
|
399
405
|
<p class="text-sm text-muted-color mt-1">
|
|
400
|
-
|
|
406
|
+
{{ 'common.company' | translate }}: {{ currentCompanyName() }}
|
|
401
407
|
</p>
|
|
402
408
|
}
|
|
403
409
|
</div>
|
|
@@ -406,53 +412,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
406
412
|
<!-- Form Fields -->
|
|
407
413
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
408
414
|
<div class="field">
|
|
409
|
-
<label for="name" class="block font-medium mb-2">
|
|
415
|
+
<label for="name" class="block font-medium mb-2">{{ 'email.template.name' | translate }} *</label>
|
|
410
416
|
<input
|
|
411
417
|
pInputText
|
|
412
418
|
id="name"
|
|
413
419
|
[formField]="templateForm.name"
|
|
414
420
|
class="w-full"
|
|
415
|
-
placeholder="
|
|
421
|
+
[placeholder]="'email.template.name.example' | translate"
|
|
416
422
|
/>
|
|
417
423
|
</div>
|
|
418
424
|
|
|
419
425
|
<div class="field">
|
|
420
|
-
<label for="slug" class="block font-medium mb-2">
|
|
426
|
+
<label for="slug" class="block font-medium mb-2">{{ 'email.template.slug' | translate }} *</label>
|
|
421
427
|
<input
|
|
422
428
|
pInputText
|
|
423
429
|
id="slug"
|
|
424
430
|
[formField]="templateForm.slug"
|
|
425
431
|
class="w-full"
|
|
426
|
-
placeholder="
|
|
432
|
+
[placeholder]="'email.template.slug.example' | translate"
|
|
427
433
|
/>
|
|
428
434
|
</div>
|
|
429
435
|
|
|
430
436
|
<div class="field md:col-span-2">
|
|
431
|
-
<label for="subject" class="block font-medium mb-2">
|
|
437
|
+
<label for="subject" class="block font-medium mb-2">{{ 'email.template.subject' | translate }} *</label>
|
|
432
438
|
<input
|
|
433
439
|
pInputText
|
|
434
440
|
id="subject"
|
|
435
441
|
[formField]="templateForm.subject"
|
|
436
442
|
class="w-full"
|
|
437
|
-
[placeholder]="'
|
|
443
|
+
[placeholder]="'email.template.subject.example' | translate"
|
|
438
444
|
/>
|
|
439
|
-
<small class="text-muted-color">
|
|
445
|
+
<small class="text-muted-color">{{ 'email.template.variable.hint' | translate }}</small>
|
|
440
446
|
</div>
|
|
441
447
|
|
|
442
448
|
<div class="field">
|
|
443
|
-
<label for="description" class="block font-medium mb-2">
|
|
449
|
+
<label for="description" class="block font-medium mb-2">{{ 'common.description' | translate }}</label>
|
|
444
450
|
<input
|
|
445
451
|
pInputText
|
|
446
452
|
id="description"
|
|
447
453
|
[formField]="templateForm.description"
|
|
448
454
|
class="w-full"
|
|
449
|
-
placeholder="
|
|
455
|
+
[placeholder]="'email.template.desc.placeholder' | translate"
|
|
450
456
|
/>
|
|
451
457
|
</div>
|
|
452
458
|
|
|
453
459
|
<div class="field flex items-center gap-2">
|
|
454
460
|
<p-toggleswitch [formField]="templateForm.isActive" />
|
|
455
|
-
<label>
|
|
461
|
+
<label>{{ 'common.active' | translate }}</label>
|
|
456
462
|
</div>
|
|
457
463
|
</div>
|
|
458
464
|
|
|
@@ -461,17 +467,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
461
467
|
<!-- Editor Panel -->
|
|
462
468
|
<div class="card">
|
|
463
469
|
<div class="flex justify-between items-center mb-3">
|
|
464
|
-
<h3 class="font-semibold">
|
|
470
|
+
<h3 class="font-semibold">{{ 'email.template.content' | translate }}</h3>
|
|
465
471
|
<div class="flex gap-1">
|
|
466
472
|
<p-button
|
|
467
|
-
label="
|
|
473
|
+
[label]="'email.template.html' | translate"
|
|
468
474
|
[outlined]="editorMode() !== 'html'"
|
|
469
475
|
[severity]="editorMode() === 'html' ? 'primary' : 'secondary'"
|
|
470
476
|
size="small"
|
|
471
477
|
(onClick)="setEditorMode('html')"
|
|
472
478
|
/>
|
|
473
479
|
<p-button
|
|
474
|
-
label="
|
|
480
|
+
[label]="'email.template.plain.text' | translate"
|
|
475
481
|
[outlined]="editorMode() !== 'text'"
|
|
476
482
|
[severity]="editorMode() === 'text' ? 'primary' : 'secondary'"
|
|
477
483
|
size="small"
|
|
@@ -487,7 +493,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
487
493
|
[ngModel]="formModel().htmlContent"
|
|
488
494
|
(ngModelChange)="updateFormField('htmlContent', $event)"
|
|
489
495
|
class="w-full h-full font-mono text-sm resize-none overflow-auto border border-surface rounded"
|
|
490
|
-
placeholder="
|
|
496
|
+
[placeholder]="'email.template.html.placeholder' | translate"
|
|
491
497
|
wrap="off"
|
|
492
498
|
></textarea>
|
|
493
499
|
} @else {
|
|
@@ -496,7 +502,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
496
502
|
[ngModel]="formModel().textContent"
|
|
497
503
|
(ngModelChange)="updateFormField('textContent', $event)"
|
|
498
504
|
class="w-full h-full font-mono text-sm resize-none overflow-auto border border-surface rounded"
|
|
499
|
-
placeholder="
|
|
505
|
+
[placeholder]="'email.template.text.placeholder' | translate"
|
|
500
506
|
wrap="off"
|
|
501
507
|
></textarea>
|
|
502
508
|
}
|
|
@@ -504,7 +510,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
504
510
|
@if (editorMode() === 'text') {
|
|
505
511
|
<small class="text-muted-color mt-2 block">
|
|
506
512
|
<i class="pi pi-info-circle mr-1"></i>
|
|
507
|
-
|
|
513
|
+
{{ 'email.template.plain.text.hint' | translate }}
|
|
508
514
|
</small>
|
|
509
515
|
}
|
|
510
516
|
</div>
|
|
@@ -513,8 +519,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
513
519
|
@if (editorMode() === 'html') {
|
|
514
520
|
<div class="card">
|
|
515
521
|
<div class="flex flex-wrap justify-between items-center gap-2 mb-3">
|
|
516
|
-
<h3 class="font-semibold m-0">
|
|
517
|
-
<p-tag value="
|
|
522
|
+
<h3 class="font-semibold m-0">{{ 'email.template.preview' | translate }}</h3>
|
|
523
|
+
<p-tag [value]="'email.template.live.preview' | translate" severity="info" />
|
|
518
524
|
</div>
|
|
519
525
|
<div class="border border-surface rounded bg-surface-0 dark:bg-surface-900 h-[400px] overflow-auto">
|
|
520
526
|
@if (formModel().htmlContent) {
|
|
@@ -527,7 +533,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
527
533
|
<div class="flex items-center justify-center h-full text-muted-color">
|
|
528
534
|
<div class="text-center p-4">
|
|
529
535
|
<i class="pi pi-eye text-4xl mb-2"></i>
|
|
530
|
-
<p class="m-0">
|
|
536
|
+
<p class="m-0">{{ 'email.template.enter.html.preview' | translate }}</p>
|
|
531
537
|
</div>
|
|
532
538
|
</div>
|
|
533
539
|
}
|
|
@@ -539,13 +545,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
539
545
|
<!-- Form Actions -->
|
|
540
546
|
<div class="flex justify-end gap-2 mt-6 pt-4 border-t border-surface">
|
|
541
547
|
<p-button
|
|
542
|
-
label="
|
|
548
|
+
[label]="'common.cancel' | translate"
|
|
543
549
|
severity="secondary"
|
|
544
550
|
[outlined]="true"
|
|
545
551
|
routerLink="../"
|
|
546
552
|
/>
|
|
547
553
|
<p-button
|
|
548
|
-
[label]="isEditMode() ? '
|
|
554
|
+
[label]="(isEditMode() ? 'common.update' : 'common.create') | translate"
|
|
549
555
|
icon="pi pi-save"
|
|
550
556
|
[loading]="isLoading()"
|
|
551
557
|
[disabled]="!isFormValid()"
|
|
@@ -560,4 +566,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
|
|
|
560
566
|
}], ctorParameters: () => [] });
|
|
561
567
|
|
|
562
568
|
export { TemplateFormComponent };
|
|
563
|
-
//# sourceMappingURL=flusys-ng-email-template-form.component-
|
|
569
|
+
//# sourceMappingURL=flusys-ng-email-template-form.component-CgJZiMCa.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-email-template-form.component-CgJZiMCa.mjs","sources":["../../../projects/ng-email/pages/template/template-form.component.ts"],"sourcesContent":["import {\n Component,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { form, FormField, required } from '@angular/forms/signals';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { APP_CONFIG, DEFAULT_APP_NAME, TRANSLATE_ADAPTER } from '@flusys/ng-core';\nimport { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';\nimport { AngularModule, PrimeModule, TranslatePipe } from '@flusys/ng-shared';\nimport { MessageService } from 'primeng/api';\nimport { EmailTemplateApiService } from '../../services/email-template-api.service';\nimport { EmailBuilderStateService } from '../../services/email-builder-state.service';\nimport { IEmailTemplate } from '../../interfaces/email-template.interface';\n\n/** Form model interface for email template */\ninterface IEmailTemplateFormModel {\n id: string;\n name: string;\n slug: string;\n description: string;\n subject: string;\n htmlContent: string;\n textContent: string;\n isActive: boolean;\n isHtml: boolean;\n}\n\n/**\n * Email template form component (create/edit)\n * Uses Angular Signal Forms for reactive form handling\n */\n@Component({\n selector: 'lib-template-form',\n imports: [AngularModule, PrimeModule, FormField, TranslatePipe],\n providers: [MessageService, EmailBuilderStateService],\n template: `\n <div class=\"card mb-4\">\n <!-- Header -->\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\">\n {{ (isEditMode() ? 'email.template.edit.title' : 'email.template.new.title') | translate }}\n </h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">\n {{ 'common.company' | translate }}: {{ currentCompanyName() }}\n </p>\n }\n </div>\n </div>\n\n <!-- Form Fields -->\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div class=\"field\">\n <label for=\"name\" class=\"block font-medium mb-2\">{{ 'email.template.name' | translate }} *</label>\n <input\n pInputText\n id=\"name\"\n [formField]=\"templateForm.name\"\n class=\"w-full\"\n [placeholder]=\"'email.template.name.example' | translate\"\n />\n </div>\n\n <div class=\"field\">\n <label for=\"slug\" class=\"block font-medium mb-2\">{{ 'email.template.slug' | translate }} *</label>\n <input\n pInputText\n id=\"slug\"\n [formField]=\"templateForm.slug\"\n class=\"w-full\"\n [placeholder]=\"'email.template.slug.example' | translate\"\n />\n </div>\n\n <div class=\"field md:col-span-2\">\n <label for=\"subject\" class=\"block font-medium mb-2\">{{ 'email.template.subject' | translate }} *</label>\n <input\n pInputText\n id=\"subject\"\n [formField]=\"templateForm.subject\"\n class=\"w-full\"\n [placeholder]=\"'email.template.subject.example' | translate\"\n />\n <small class=\"text-muted-color\">{{ 'email.template.variable.hint' | translate }}</small>\n </div>\n\n <div class=\"field\">\n <label for=\"description\" class=\"block font-medium mb-2\">{{ 'common.description' | translate }}</label>\n <input\n pInputText\n id=\"description\"\n [formField]=\"templateForm.description\"\n class=\"w-full\"\n [placeholder]=\"'email.template.desc.placeholder' | translate\"\n />\n </div>\n\n <div class=\"field flex items-center gap-2\">\n <p-toggleswitch [formField]=\"templateForm.isActive\" />\n <label>{{ 'common.active' | translate }}</label>\n </div>\n </div>\n\n <!-- Content Editor & Preview -->\n <div class=\"grid grid-cols-1 gap-4\" [class.lg:grid-cols-2]=\"editorMode() === 'html'\">\n <!-- Editor Panel -->\n <div class=\"card\">\n <div class=\"flex justify-between items-center mb-3\">\n <h3 class=\"font-semibold\">{{ 'email.template.content' | translate }}</h3>\n <div class=\"flex gap-1\">\n <p-button\n [label]=\"'email.template.html' | translate\"\n [outlined]=\"editorMode() !== 'html'\"\n [severity]=\"editorMode() === 'html' ? 'primary' : 'secondary'\"\n size=\"small\"\n (onClick)=\"setEditorMode('html')\"\n />\n <p-button\n [label]=\"'email.template.plain.text' | translate\"\n [outlined]=\"editorMode() !== 'text'\"\n [severity]=\"editorMode() === 'text' ? 'primary' : 'secondary'\"\n size=\"small\"\n (onClick)=\"setEditorMode('text')\"\n />\n </div>\n </div>\n\n <div class=\"h-[400px]\">\n @if (editorMode() === 'html') {\n <textarea\n pTextarea\n [ngModel]=\"formModel().htmlContent\"\n (ngModelChange)=\"updateFormField('htmlContent', $event)\"\n class=\"w-full h-full font-mono text-sm resize-none overflow-auto border border-surface rounded\"\n [placeholder]=\"'email.template.html.placeholder' | translate\"\n wrap=\"off\"\n ></textarea>\n } @else {\n <textarea\n pTextarea\n [ngModel]=\"formModel().textContent\"\n (ngModelChange)=\"updateFormField('textContent', $event)\"\n class=\"w-full h-full font-mono text-sm resize-none overflow-auto border border-surface rounded\"\n [placeholder]=\"'email.template.text.placeholder' | translate\"\n wrap=\"off\"\n ></textarea>\n }\n </div>\n @if (editorMode() === 'text') {\n <small class=\"text-muted-color mt-2 block\">\n <i class=\"pi pi-info-circle mr-1\"></i>\n {{ 'email.template.plain.text.hint' | translate }}\n </small>\n }\n </div>\n\n <!-- Preview Panel (HTML mode only) -->\n @if (editorMode() === 'html') {\n <div class=\"card\">\n <div class=\"flex flex-wrap justify-between items-center gap-2 mb-3\">\n <h3 class=\"font-semibold m-0\">{{ 'email.template.preview' | translate }}</h3>\n <p-tag [value]=\"'email.template.live.preview' | translate\" severity=\"info\" />\n </div>\n <div class=\"border border-surface rounded bg-surface-0 dark:bg-surface-900 h-[400px] overflow-auto\">\n @if (formModel().htmlContent) {\n <iframe\n [srcdoc]=\"getPreviewHtml()\"\n class=\"w-full h-full border-0\"\n sandbox=\"allow-same-origin\"\n ></iframe>\n } @else {\n <div class=\"flex items-center justify-center h-full text-muted-color\">\n <div class=\"text-center p-4\">\n <i class=\"pi pi-eye text-4xl mb-2\"></i>\n <p class=\"m-0\">{{ 'email.template.enter.html.preview' | translate }}</p>\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Form Actions -->\n <div class=\"flex justify-end gap-2 mt-6 pt-4 border-t border-surface\">\n <p-button\n [label]=\"'common.cancel' | translate\"\n severity=\"secondary\"\n [outlined]=\"true\"\n routerLink=\"../\"\n />\n <p-button\n [label]=\"(isEditMode() ? 'common.update' : 'common.create') | translate\"\n icon=\"pi pi-save\"\n [loading]=\"isLoading()\"\n [disabled]=\"!isFormValid()\"\n (onClick)=\"onSave()\"\n />\n </div>\n </div>\n\n <p-toast />\n `,\n})\nexport class TemplateFormComponent {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly templateService = inject(EmailTemplateApiService);\n private readonly messageService = inject(MessageService);\n private readonly appConfig = inject(APP_CONFIG);\n private readonly companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });\n private readonly translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });\n readonly state = inject(EmailBuilderStateService);\n\n // Route params as signal\n private readonly routeParams = toSignal(this.route.paramMap);\n\n readonly isLoading = signal(false);\n readonly existingTemplate = signal<IEmailTemplate | null>(null);\n readonly isEditMode = computed(() => !!this.existingTemplate());\n readonly editorMode = signal<'html' | 'text'>('html');\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\n /** Form model using signal */\n readonly formModel = signal<IEmailTemplateFormModel>({\n id: '',\n name: '',\n slug: '',\n description: '',\n subject: '',\n htmlContent: '',\n textContent: '',\n isActive: true,\n isHtml: true,\n });\n\n /** Form with validation using Angular Signal Forms */\n readonly templateForm = form(this.formModel, (f) => {\n required(f.name);\n required(f.slug);\n required(f.subject);\n });\n\n /** Check if form is valid */\n readonly isFormValid = computed(() => {\n const model = this.formModel();\n return (\n model.name.trim().length > 0 &&\n model.slug.trim().length > 0 &&\n model.subject.trim().length > 0\n );\n });\n\n /** Update a single form field */\n updateFormField<K extends keyof IEmailTemplateFormModel>(\n field: K,\n value: IEmailTemplateFormModel[K],\n ): void {\n this.formModel.update((m) => ({ ...m, [field]: value }));\n }\n\n constructor() {\n // Effect to handle route-based initialization\n effect(() => {\n const params = this.routeParams();\n if (!params) return;\n\n const id = params.get('id');\n if (id) {\n this.loadTemplate(id);\n }\n });\n }\n\n async loadTemplate(id: string): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await this.templateService.findByIdAsync(id);\n if (response.success && response.data) {\n const template = response.data;\n this.existingTemplate.set(template);\n this.formModel.set({\n id: template.id,\n name: template.name,\n slug: template.slug,\n description: template.description || '',\n subject: template.subject,\n htmlContent: template.htmlContent,\n textContent: template.textContent || '',\n isActive: template.isActive,\n isHtml: template.isHtml ?? true,\n });\n // Set editor mode based on saved template type\n this.editorMode.set(template.isHtml ? 'html' : 'text');\n this.state.loadSchema(template.schema);\n }\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this.isLoading.set(false);\n }\n }\n\n setEditorMode(mode: 'html' | 'text'): void {\n const currentMode = this.editorMode();\n const model = this.formModel();\n\n // Sync content when switching modes\n if (currentMode === 'text' && mode === 'html') {\n // Switching from text to html - convert text to basic HTML if htmlContent is empty\n if (!model.htmlContent && model.textContent) {\n this.formModel.update((m) => ({ ...m, htmlContent: this.textToHtml(m.textContent) }));\n }\n } else if (currentMode === 'html' && mode === 'text') {\n // Switching from html to text - generate text from HTML if textContent is empty\n if (!model.textContent && model.htmlContent) {\n this.formModel.update((m) => ({ ...m, textContent: this.htmlToPlainText(m.htmlContent) }));\n }\n }\n\n this.editorMode.set(mode);\n this.formModel.update((m) => ({ ...m, isHtml: mode === 'html' }));\n }\n\n /** Convert plain text to basic HTML */\n private textToHtml(text: string): string {\n if (!text) return '';\n // Convert line breaks to paragraphs\n return text\n .split(/\\n\\n+/)\n .map((p) => `<p>${p.replace(/\\n/g, '<br>')}</p>`)\n .join('\\n');\n }\n\n /** Convert HTML to plain text (using DOMParser for safe parsing) */\n private htmlToPlainText(html: string): string {\n if (!html) return '';\n // Use DOMParser for safe HTML parsing (prevents XSS)\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, 'text/html');\n // Get text content and clean up whitespace\n return (doc.body.textContent || '').replace(/\\s+/g, ' ').trim();\n }\n\n /** Get HTML for preview iframe */\n getPreviewHtml(): string {\n const baseStyles = `\n <style>\n body {\n font-family: Arial, sans-serif;\n margin: 16px;\n background-color: #ffffff;\n color: #333333;\n }\n img { max-width: 100%; height: auto; }\n </style>\n `;\n return baseStyles + (this.formModel().htmlContent || '');\n }\n\n async onSave(): Promise<void> {\n if (!this.isFormValid()) {\n this.messageService.add({\n severity: 'warn',\n summary: this.translate('common.validation'),\n detail: this.translate('common.fill.required.fields'),\n });\n return;\n }\n\n this.isLoading.set(true);\n try {\n const model = this.formModel();\n const schema = this.state.schema();\n // Use user-provided plain text, or auto-generate from HTML if empty\n const textContent = model.textContent || this.htmlToPlainText(model.htmlContent);\n\n const data = {\n name: model.name,\n slug: model.slug,\n description: model.description || undefined,\n subject: model.subject,\n schema: {\n ...schema,\n name: model.name,\n subject: model.subject,\n },\n htmlContent: model.htmlContent || `<p>${this.translate('email.template.default.content')}</p>`,\n textContent: textContent || undefined,\n isActive: model.isActive,\n isHtml: model.isHtml,\n };\n\n if (this.isEditMode()) {\n await this.templateService.updateAsync({ id: model.id, ...data });\n } else {\n await this.templateService.insertAsync(data);\n }\n\n this.messageService.add({\n severity: 'success',\n summary: this.translate('common.success'),\n detail: this.translate(this.isEditMode() ? 'email.template.updated' : 'email.template.created'),\n });\n\n this.router.navigate(['../'], { relativeTo: this.route });\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this.isLoading.set(false);\n }\n }\n\n private translate(key: string, variables?: Record<string, string | number>): string {\n return this.translateAdapter?.translate(key, variables) ?? key;\n }\n}\n"],"names":["i3","i4","i5","i7","i8"],"mappings":"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"}
|