@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.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { inject, signal, computed, Component } from '@angular/core';
3
3
  import { Router } from '@angular/router';
4
4
  import { firstValueFrom } from 'rxjs';
5
- import { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';
5
+ import { APP_CONFIG, TRANSLATE_ADAPTER, DEFAULT_APP_NAME } from '@flusys/ng-core';
6
6
  import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
7
- import { EMAIL_TEMPLATE_PERMISSIONS, AngularModule, PrimeModule, HasPermissionDirective } from '@flusys/ng-shared';
7
+ import { EMAIL_TEMPLATE_PERMISSIONS, AngularModule, PrimeModule, HasPermissionDirective, TranslatePipe } from '@flusys/ng-shared';
8
8
  import { ConfirmationService, MessageService } from 'primeng/api';
9
9
  import { EmailTemplateApiService, EmailConfigApiService, EmailSendService } from './flusys-ng-email.mjs';
10
10
  import * as i1 from '@angular/forms';
@@ -33,6 +33,7 @@ class TemplateListComponent {
33
33
  messageService = inject(MessageService);
34
34
  appConfig = inject(APP_CONFIG);
35
35
  companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });
36
+ translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
36
37
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
37
38
  templates = signal([], ...(ngDevMode ? [{ debugName: "templates" }] : []));
38
39
  totalRecords = signal(0, ...(ngDevMode ? [{ debugName: "totalRecords" }] : []));
@@ -62,7 +63,7 @@ class TemplateListComponent {
62
63
  this._variableValues.update((v) => ({ ...v, [key]: value }));
63
64
  }
64
65
  configOptions = computed(() => this.configs().map((c) => ({
65
- label: `${c.name} (${c.provider})`,
66
+ label: `${c.name} (${this.translate(`email.providers.${c.provider}`)})`,
66
67
  value: c.id,
67
68
  })), ...(ngDevMode ? [{ debugName: "configOptions" }] : []));
68
69
  /** Extract variables from template content */
@@ -107,6 +108,9 @@ class TemplateListComponent {
107
108
  this.totalRecords.set(response.meta?.total ?? 0);
108
109
  }
109
110
  }
111
+ catch {
112
+ // Error toast handled by global interceptor
113
+ }
110
114
  finally {
111
115
  this.isLoading.set(false);
112
116
  }
@@ -136,6 +140,9 @@ class TemplateListComponent {
136
140
  this.configs.set(response.data ?? []);
137
141
  }
138
142
  }
143
+ catch {
144
+ // Error toast handled by global interceptor
145
+ }
139
146
  finally {
140
147
  this.isLoadingConfigs.set(false);
141
148
  }
@@ -146,8 +153,8 @@ class TemplateListComponent {
146
153
  if (!template || !model.configId || !model.recipient) {
147
154
  this.messageService.add({
148
155
  severity: 'warn',
149
- summary: 'Validation',
150
- detail: 'Please select a configuration and enter recipient email.',
156
+ summary: this.translate('common.validation'),
157
+ detail: this.translate('email.template.select.config.and.recipient'),
151
158
  });
152
159
  return;
153
160
  }
@@ -169,16 +176,16 @@ class TemplateListComponent {
169
176
  if (response.data?.success) {
170
177
  this.messageService.add({
171
178
  severity: 'success',
172
- summary: 'Success',
173
- detail: `Test email sent! Message ID: ${response.data.messageId}`,
179
+ summary: this.translate('common.success'),
180
+ detail: this.translate('email.template.test.sent.success', { messageId: response.data.messageId ?? '' }),
174
181
  });
175
182
  this.showTestDialog.set(false);
176
183
  }
177
184
  else {
178
185
  this.messageService.add({
179
186
  severity: 'error',
180
- summary: 'Error',
181
- detail: response.data?.error || 'Failed to send test email.',
187
+ summary: this.translate('common.error'),
188
+ detail: response.data?.error || this.translate('email.template.test.sent.failed'),
182
189
  });
183
190
  }
184
191
  }
@@ -191,8 +198,8 @@ class TemplateListComponent {
191
198
  }
192
199
  onDelete(template) {
193
200
  this.confirmationService.confirm({
194
- message: `Are you sure you want to delete "${template.name}"?`,
195
- header: 'Delete Template',
201
+ message: this.translate('email.template.delete.confirm', { name: template.name }),
202
+ header: this.translate('email.template.delete.title'),
196
203
  icon: 'pi pi-exclamation-triangle',
197
204
  acceptButtonStyleClass: 'p-button-danger',
198
205
  accept: async () => {
@@ -200,8 +207,8 @@ class TemplateListComponent {
200
207
  await this.templateService.deleteAsync({ id: template.id, type: 'delete' });
201
208
  this.messageService.add({
202
209
  severity: 'success',
203
- summary: 'Success',
204
- detail: 'Template deleted successfully.',
210
+ summary: this.translate('common.success'),
211
+ detail: this.translate('email.template.deleted'),
205
212
  });
206
213
  this.loadTemplates();
207
214
  }
@@ -211,21 +218,25 @@ class TemplateListComponent {
211
218
  },
212
219
  });
213
220
  }
221
+ /** Translate helper - public for template usage with dynamic variables */
222
+ translate(key, variables) {
223
+ return this.translateAdapter?.translate(key, variables) ?? key;
224
+ }
214
225
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TemplateListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
215
226
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: TemplateListComponent, isStandalone: true, selector: "lib-template-list", providers: [ConfirmationService, MessageService], ngImport: i0, template: `
216
227
  <div class="card">
217
228
  <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
218
229
  <div>
219
- <h3 class="text-lg sm:text-xl font-semibold m-0">Email Templates</h3>
230
+ <h3 class="text-lg sm:text-xl font-semibold m-0">{{ 'email.template.title' | translate }}</h3>
220
231
  @if (showCompanyInfo()) {
221
232
  <p class="text-sm text-muted-color mt-1">
222
- Company: {{ currentCompanyName() }}
233
+ {{ 'common.company' | translate }}: {{ currentCompanyName() }}
223
234
  </p>
224
235
  }
225
236
  </div>
226
237
  <p-button
227
238
  *hasPermission="EMAIL_TEMPLATE_PERMISSIONS.CREATE"
228
- label="New Template"
239
+ [label]="'email.template.new' | translate"
229
240
  icon="pi pi-plus"
230
241
  (onClick)="onCreate()"
231
242
  styleClass="w-full sm:w-auto"
@@ -248,13 +259,13 @@ class TemplateListComponent {
248
259
  >
249
260
  <ng-template #header>
250
261
  <tr>
251
- <th>Name</th>
252
- <th class="hidden md:table-cell">Slug</th>
253
- <th class="hidden lg:table-cell">Subject</th>
254
- <th>Type</th>
255
- <th>Status</th>
256
- <th class="hidden xl:table-cell">Created</th>
257
- <th class="w-[120px]">Actions</th>
262
+ <th>{{ 'common.name' | translate }}</th>
263
+ <th class="hidden md:table-cell">{{ 'email.template.slug' | translate }}</th>
264
+ <th class="hidden lg:table-cell">{{ 'email.template.subject' | translate }}</th>
265
+ <th>{{ 'common.type' | translate }}</th>
266
+ <th>{{ 'common.status' | translate }}</th>
267
+ <th class="hidden xl:table-cell">{{ 'common.created' | translate }}</th>
268
+ <th class="w-[120px]">{{ 'common.actions' | translate }}</th>
258
269
  </tr>
259
270
  </ng-template>
260
271
 
@@ -270,13 +281,13 @@ class TemplateListComponent {
270
281
  <td class="hidden lg:table-cell">{{ template.subject }}</td>
271
282
  <td>
272
283
  <p-tag
273
- [value]="template.isHtml ? 'HTML' : 'Text'"
284
+ [value]="(template.isHtml ? 'email.template.html' : 'email.template.text') | translate"
274
285
  [severity]="template.isHtml ? 'info' : 'secondary'"
275
286
  />
276
287
  </td>
277
288
  <td>
278
289
  <p-tag
279
- [value]="template.isActive ? 'Active' : 'Inactive'"
290
+ [value]="(template.isActive ? 'common.active' : 'common.inactive') | translate"
280
291
  [severity]="template.isActive ? 'success' : 'secondary'"
281
292
  />
282
293
  </td>
@@ -288,7 +299,7 @@ class TemplateListComponent {
288
299
  icon="pi pi-send"
289
300
  [text]="true"
290
301
  size="small"
291
- pTooltip="Test Send"
302
+ [pTooltip]="'email.template.test.send' | translate"
292
303
  (onClick)="onTestSend(template)"
293
304
  />
294
305
  <p-button
@@ -297,7 +308,7 @@ class TemplateListComponent {
297
308
  [text]="true"
298
309
  severity="secondary"
299
310
  size="small"
300
- pTooltip="Edit"
311
+ [pTooltip]="'common.edit' | translate"
301
312
  (onClick)="onEdit(template.id)"
302
313
  />
303
314
  <p-button
@@ -306,7 +317,7 @@ class TemplateListComponent {
306
317
  [text]="true"
307
318
  severity="danger"
308
319
  size="small"
309
- pTooltip="Delete"
320
+ [pTooltip]="'common.delete' | translate"
310
321
  (onClick)="onDelete(template)"
311
322
  />
312
323
  </div>
@@ -317,7 +328,7 @@ class TemplateListComponent {
317
328
  <ng-template #emptymessage>
318
329
  <tr>
319
330
  <td colspan="7" class="text-center py-4 text-muted-color">
320
- No email templates found. Create your first template to get started.
331
+ {{ 'email.template.empty' | translate }}
321
332
  </td>
322
333
  </tr>
323
334
  </ng-template>
@@ -327,7 +338,7 @@ class TemplateListComponent {
327
338
 
328
339
  <!-- Test Send Dialog -->
329
340
  <p-dialog
330
- header="Test Send Email"
341
+ [header]="'email.template.test.dialog.title' | translate"
331
342
  [visible]="showTestDialog()"
332
343
  (visibleChange)="showTestDialog.set($event)"
333
344
  [modal]="true"
@@ -336,7 +347,7 @@ class TemplateListComponent {
336
347
  >
337
348
  <div class="flex flex-col gap-4">
338
349
  <div class="field">
339
- <label class="block font-medium mb-2">Template</label>
350
+ <label class="block font-medium mb-2">{{ 'email.template.template' | translate }}</label>
340
351
  <input
341
352
  pInputText
342
353
  [value]="selectedTemplate()?.name || ''"
@@ -346,27 +357,27 @@ class TemplateListComponent {
346
357
  </div>
347
358
 
348
359
  <div class="field">
349
- <label class="block font-medium mb-2">Email Configuration *</label>
360
+ <label class="block font-medium mb-2">{{ 'email.template.email.config' | translate }} *</label>
350
361
  <p-select
351
362
  [ngModel]="testSendModel.configId"
352
363
  (ngModelChange)="updateTestSendModel('configId', $event)"
353
364
  [options]="configOptions()"
354
365
  optionLabel="label"
355
366
  optionValue="value"
356
- placeholder="Select configuration"
367
+ [placeholder]="'email.template.select.config' | translate"
357
368
  class="w-full"
358
369
  [loading]="isLoadingConfigs()"
359
370
  />
360
371
  </div>
361
372
 
362
373
  <div class="field">
363
- <label class="block font-medium mb-2">Recipient Email *</label>
374
+ <label class="block font-medium mb-2">{{ 'email.config.recipient.email' | translate }} *</label>
364
375
  <input
365
376
  pInputText
366
377
  [ngModel]="testSendModel.recipient"
367
378
  (ngModelChange)="updateTestSendModel('recipient', $event)"
368
379
  class="w-full"
369
- placeholder="recipient@example.com"
380
+ [placeholder]="'email.recipient.example' | translate"
370
381
  />
371
382
  </div>
372
383
 
@@ -375,7 +386,7 @@ class TemplateListComponent {
375
386
  <div class="border-t border-surface pt-4 mt-2">
376
387
  <h4 class="font-medium mb-3 flex items-center gap-2">
377
388
  <i class="pi pi-code text-muted-color"></i>
378
- Template Variables
389
+ {{ 'email.template.variables' | translate }}
379
390
  </h4>
380
391
  <div class="flex flex-col gap-3">
381
392
  @for (variable of templateVariables(); track variable) {
@@ -388,7 +399,7 @@ class TemplateListComponent {
388
399
  [ngModel]="variableValues[variable] || ''"
389
400
  (ngModelChange)="updateVariableValue(variable, $event)"
390
401
  class="w-full"
391
- [placeholder]="'Enter value for ' + variable"
402
+ [placeholder]="translate('email.template.enter.value.for', { variable: variable })"
392
403
  />
393
404
  </div>
394
405
  }
@@ -399,13 +410,13 @@ class TemplateListComponent {
399
410
 
400
411
  <ng-template #footer>
401
412
  <p-button
402
- label="Cancel"
413
+ [label]="'common.cancel' | translate"
403
414
  severity="secondary"
404
415
  [outlined]="true"
405
416
  (onClick)="showTestDialog.set(false)"
406
417
  />
407
418
  <p-button
408
- label="Send Test"
419
+ [label]="'email.template.send.test' | translate"
409
420
  icon="pi pi-send"
410
421
  [loading]="isSendingTest()"
411
422
  (onClick)="sendTestEmail()"
@@ -415,29 +426,28 @@ class TemplateListComponent {
415
426
 
416
427
  <p-confirmDialog />
417
428
  <p-toast />
418
- `, 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: "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: "component", type: i3.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: i4.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i6.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i7.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i8.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { 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: "directive", type: i10.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: i11.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
429
+ `, 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: "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: "component", type: i3.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: i4.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i5.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i6.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i7.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i8.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { 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: "directive", type: i10.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: i11.DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
419
430
  }
420
431
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TemplateListComponent, decorators: [{
421
432
  type: Component,
422
433
  args: [{
423
434
  selector: 'lib-template-list',
424
- changeDetection: ChangeDetectionStrategy.OnPush,
425
- imports: [AngularModule, PrimeModule, HasPermissionDirective],
435
+ imports: [AngularModule, PrimeModule, HasPermissionDirective, TranslatePipe],
426
436
  providers: [ConfirmationService, MessageService],
427
437
  template: `
428
438
  <div class="card">
429
439
  <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
430
440
  <div>
431
- <h3 class="text-lg sm:text-xl font-semibold m-0">Email Templates</h3>
441
+ <h3 class="text-lg sm:text-xl font-semibold m-0">{{ 'email.template.title' | translate }}</h3>
432
442
  @if (showCompanyInfo()) {
433
443
  <p class="text-sm text-muted-color mt-1">
434
- Company: {{ currentCompanyName() }}
444
+ {{ 'common.company' | translate }}: {{ currentCompanyName() }}
435
445
  </p>
436
446
  }
437
447
  </div>
438
448
  <p-button
439
449
  *hasPermission="EMAIL_TEMPLATE_PERMISSIONS.CREATE"
440
- label="New Template"
450
+ [label]="'email.template.new' | translate"
441
451
  icon="pi pi-plus"
442
452
  (onClick)="onCreate()"
443
453
  styleClass="w-full sm:w-auto"
@@ -460,13 +470,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
460
470
  >
461
471
  <ng-template #header>
462
472
  <tr>
463
- <th>Name</th>
464
- <th class="hidden md:table-cell">Slug</th>
465
- <th class="hidden lg:table-cell">Subject</th>
466
- <th>Type</th>
467
- <th>Status</th>
468
- <th class="hidden xl:table-cell">Created</th>
469
- <th class="w-[120px]">Actions</th>
473
+ <th>{{ 'common.name' | translate }}</th>
474
+ <th class="hidden md:table-cell">{{ 'email.template.slug' | translate }}</th>
475
+ <th class="hidden lg:table-cell">{{ 'email.template.subject' | translate }}</th>
476
+ <th>{{ 'common.type' | translate }}</th>
477
+ <th>{{ 'common.status' | translate }}</th>
478
+ <th class="hidden xl:table-cell">{{ 'common.created' | translate }}</th>
479
+ <th class="w-[120px]">{{ 'common.actions' | translate }}</th>
470
480
  </tr>
471
481
  </ng-template>
472
482
 
@@ -482,13 +492,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
482
492
  <td class="hidden lg:table-cell">{{ template.subject }}</td>
483
493
  <td>
484
494
  <p-tag
485
- [value]="template.isHtml ? 'HTML' : 'Text'"
495
+ [value]="(template.isHtml ? 'email.template.html' : 'email.template.text') | translate"
486
496
  [severity]="template.isHtml ? 'info' : 'secondary'"
487
497
  />
488
498
  </td>
489
499
  <td>
490
500
  <p-tag
491
- [value]="template.isActive ? 'Active' : 'Inactive'"
501
+ [value]="(template.isActive ? 'common.active' : 'common.inactive') | translate"
492
502
  [severity]="template.isActive ? 'success' : 'secondary'"
493
503
  />
494
504
  </td>
@@ -500,7 +510,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
500
510
  icon="pi pi-send"
501
511
  [text]="true"
502
512
  size="small"
503
- pTooltip="Test Send"
513
+ [pTooltip]="'email.template.test.send' | translate"
504
514
  (onClick)="onTestSend(template)"
505
515
  />
506
516
  <p-button
@@ -509,7 +519,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
509
519
  [text]="true"
510
520
  severity="secondary"
511
521
  size="small"
512
- pTooltip="Edit"
522
+ [pTooltip]="'common.edit' | translate"
513
523
  (onClick)="onEdit(template.id)"
514
524
  />
515
525
  <p-button
@@ -518,7 +528,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
518
528
  [text]="true"
519
529
  severity="danger"
520
530
  size="small"
521
- pTooltip="Delete"
531
+ [pTooltip]="'common.delete' | translate"
522
532
  (onClick)="onDelete(template)"
523
533
  />
524
534
  </div>
@@ -529,7 +539,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
529
539
  <ng-template #emptymessage>
530
540
  <tr>
531
541
  <td colspan="7" class="text-center py-4 text-muted-color">
532
- No email templates found. Create your first template to get started.
542
+ {{ 'email.template.empty' | translate }}
533
543
  </td>
534
544
  </tr>
535
545
  </ng-template>
@@ -539,7 +549,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
539
549
 
540
550
  <!-- Test Send Dialog -->
541
551
  <p-dialog
542
- header="Test Send Email"
552
+ [header]="'email.template.test.dialog.title' | translate"
543
553
  [visible]="showTestDialog()"
544
554
  (visibleChange)="showTestDialog.set($event)"
545
555
  [modal]="true"
@@ -548,7 +558,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
548
558
  >
549
559
  <div class="flex flex-col gap-4">
550
560
  <div class="field">
551
- <label class="block font-medium mb-2">Template</label>
561
+ <label class="block font-medium mb-2">{{ 'email.template.template' | translate }}</label>
552
562
  <input
553
563
  pInputText
554
564
  [value]="selectedTemplate()?.name || ''"
@@ -558,27 +568,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
558
568
  </div>
559
569
 
560
570
  <div class="field">
561
- <label class="block font-medium mb-2">Email Configuration *</label>
571
+ <label class="block font-medium mb-2">{{ 'email.template.email.config' | translate }} *</label>
562
572
  <p-select
563
573
  [ngModel]="testSendModel.configId"
564
574
  (ngModelChange)="updateTestSendModel('configId', $event)"
565
575
  [options]="configOptions()"
566
576
  optionLabel="label"
567
577
  optionValue="value"
568
- placeholder="Select configuration"
578
+ [placeholder]="'email.template.select.config' | translate"
569
579
  class="w-full"
570
580
  [loading]="isLoadingConfigs()"
571
581
  />
572
582
  </div>
573
583
 
574
584
  <div class="field">
575
- <label class="block font-medium mb-2">Recipient Email *</label>
585
+ <label class="block font-medium mb-2">{{ 'email.config.recipient.email' | translate }} *</label>
576
586
  <input
577
587
  pInputText
578
588
  [ngModel]="testSendModel.recipient"
579
589
  (ngModelChange)="updateTestSendModel('recipient', $event)"
580
590
  class="w-full"
581
- placeholder="recipient@example.com"
591
+ [placeholder]="'email.recipient.example' | translate"
582
592
  />
583
593
  </div>
584
594
 
@@ -587,7 +597,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
587
597
  <div class="border-t border-surface pt-4 mt-2">
588
598
  <h4 class="font-medium mb-3 flex items-center gap-2">
589
599
  <i class="pi pi-code text-muted-color"></i>
590
- Template Variables
600
+ {{ 'email.template.variables' | translate }}
591
601
  </h4>
592
602
  <div class="flex flex-col gap-3">
593
603
  @for (variable of templateVariables(); track variable) {
@@ -600,7 +610,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
600
610
  [ngModel]="variableValues[variable] || ''"
601
611
  (ngModelChange)="updateVariableValue(variable, $event)"
602
612
  class="w-full"
603
- [placeholder]="'Enter value for ' + variable"
613
+ [placeholder]="translate('email.template.enter.value.for', { variable: variable })"
604
614
  />
605
615
  </div>
606
616
  }
@@ -611,13 +621,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
611
621
 
612
622
  <ng-template #footer>
613
623
  <p-button
614
- label="Cancel"
624
+ [label]="'common.cancel' | translate"
615
625
  severity="secondary"
616
626
  [outlined]="true"
617
627
  (onClick)="showTestDialog.set(false)"
618
628
  />
619
629
  <p-button
620
- label="Send Test"
630
+ [label]="'email.template.send.test' | translate"
621
631
  icon="pi pi-send"
622
632
  [loading]="isSendingTest()"
623
633
  (onClick)="sendTestEmail()"
@@ -632,4 +642,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
632
642
  }] });
633
643
 
634
644
  export { TemplateListComponent };
635
- //# sourceMappingURL=flusys-ng-email-template-list.component-krrpsjDv.mjs.map
645
+ //# sourceMappingURL=flusys-ng-email-template-list.component-DHnuMTpB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-email-template-list.component-DHnuMTpB.mjs","sources":["../../../projects/ng-email/pages/template/template-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_TEMPLATE_PERMISSIONS, HasPermissionDirective, PrimeModule, TranslatePipe } from '@flusys/ng-shared';\nimport { ConfirmationService, MessageService } from 'primeng/api';\nimport { IEmailConfig } from '../../interfaces/email-config.interface';\nimport { IEmailTemplate } from '../../interfaces/email-template.interface';\nimport { EmailConfigApiService } from '../../services/email-config-api.service';\nimport { EmailSendService } from '../../services/email-send.service';\nimport { EmailTemplateApiService } from '../../services/email-template-api.service';\n\n/**\n * Email template list component\n */\n@Component({\n selector: 'lib-template-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.template.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_TEMPLATE_PERMISSIONS.CREATE\"\n [label]=\"'email.template.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]=\"templates()\"\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 class=\"hidden md:table-cell\">{{ 'email.template.slug' | translate }}</th>\n <th class=\"hidden lg:table-cell\">{{ 'email.template.subject' | translate }}</th>\n <th>{{ 'common.type' | translate }}</th>\n <th>{{ 'common.status' | translate }}</th>\n <th class=\"hidden xl: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-template>\n <tr>\n <td>\n <i class=\"pi pi-envelope mr-2 text-muted-color\"></i>\n {{ template.name }}\n </td>\n <td class=\"hidden md:table-cell\">\n <code class=\"text-sm bg-surface-100 dark:bg-surface-700 px-2 py-1 rounded\">{{ template.slug }}</code>\n </td>\n <td class=\"hidden lg:table-cell\">{{ template.subject }}</td>\n <td>\n <p-tag\n [value]=\"(template.isHtml ? 'email.template.html' : 'email.template.text') | translate\"\n [severity]=\"template.isHtml ? 'info' : 'secondary'\"\n />\n </td>\n <td>\n <p-tag\n [value]=\"(template.isActive ? 'common.active' : 'common.inactive') | translate\"\n [severity]=\"template.isActive ? 'success' : 'secondary'\"\n />\n </td>\n <td class=\"hidden xl:table-cell\">{{ template.createdAt | date: 'short' }}</td>\n <td>\n <div class=\"flex gap-1\">\n <p-button\n *hasPermission=\"EMAIL_TEMPLATE_PERMISSIONS.UPDATE\"\n icon=\"pi pi-send\"\n [text]=\"true\"\n size=\"small\"\n [pTooltip]=\"'email.template.test.send' | translate\"\n (onClick)=\"onTestSend(template)\"\n />\n <p-button\n *hasPermission=\"EMAIL_TEMPLATE_PERMISSIONS.UPDATE\"\n icon=\"pi pi-pencil\"\n [text]=\"true\"\n severity=\"secondary\"\n size=\"small\"\n [pTooltip]=\"'common.edit' | translate\"\n (onClick)=\"onEdit(template.id)\"\n />\n <p-button\n *hasPermission=\"EMAIL_TEMPLATE_PERMISSIONS.DELETE\"\n icon=\"pi pi-trash\"\n [text]=\"true\"\n severity=\"danger\"\n size=\"small\"\n [pTooltip]=\"'common.delete' | translate\"\n (onClick)=\"onDelete(template)\"\n />\n </div>\n </td>\n </tr>\n </ng-template>\n\n <ng-template #emptymessage>\n <tr>\n <td colspan=\"7\" class=\"text-center py-4 text-muted-color\">\n {{ 'email.template.empty' | translate }}\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n\n <!-- Test Send Dialog -->\n <p-dialog\n [header]=\"'email.template.test.dialog.title' | translate\"\n [visible]=\"showTestDialog()\"\n (visibleChange)=\"showTestDialog.set($event)\"\n [modal]=\"true\"\n [style]=\"{ width: '95vw', maxWidth: '500px', maxHeight: '80vh' }\"\n [breakpoints]=\"{ '575px': '95vw' }\"\n >\n <div class=\"flex flex-col gap-4\">\n <div class=\"field\">\n <label class=\"block font-medium mb-2\">{{ 'email.template.template' | translate }}</label>\n <input\n pInputText\n [value]=\"selectedTemplate()?.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.template.email.config' | translate }} *</label>\n <p-select\n [ngModel]=\"testSendModel.configId\"\n (ngModelChange)=\"updateTestSendModel('configId', $event)\"\n [options]=\"configOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'email.template.select.config' | translate\"\n class=\"w-full\"\n [loading]=\"isLoadingConfigs()\"\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]=\"testSendModel.recipient\"\n (ngModelChange)=\"updateTestSendModel('recipient', $event)\"\n class=\"w-full\"\n [placeholder]=\"'email.recipient.example' | translate\"\n />\n </div>\n\n <!-- Dynamic Variables -->\n @if (templateVariables().length > 0) {\n <div class=\"border-t border-surface pt-4 mt-2\">\n <h4 class=\"font-medium mb-3 flex items-center gap-2\">\n <i class=\"pi pi-code text-muted-color\"></i>\n {{ 'email.template.variables' | translate }}\n </h4>\n <div class=\"flex flex-col gap-3\">\n @for (variable of templateVariables(); track variable) {\n <div class=\"field\">\n <label class=\"block text-sm font-medium mb-1\">\n <code class=\"text-primary bg-surface-100 dark:bg-surface-700 px-1 rounded\">{{ '{{' + variable + '}}' }}</code>\n </label>\n <input\n pInputText\n [ngModel]=\"variableValues[variable] || ''\"\n (ngModelChange)=\"updateVariableValue(variable, $event)\"\n class=\"w-full\"\n [placeholder]=\"translate('email.template.enter.value.for', { variable: variable })\"\n />\n </div>\n }\n </div>\n </div>\n }\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.template.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 TemplateListComponent {\n // Permission constants for template\n readonly EMAIL_TEMPLATE_PERMISSIONS = EMAIL_TEMPLATE_PERMISSIONS;\n\n private readonly router = inject(Router);\n private readonly templateService = inject(EmailTemplateApiService);\n private readonly configService = inject(EmailConfigApiService);\n private readonly emailSendService = inject(EmailSendService);\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 templates = signal<IEmailTemplate[]>([]);\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 send dialog (signals for zoneless change detection)\n readonly showTestDialog = signal(false);\n readonly selectedTemplate = signal<IEmailTemplate | null>(null);\n readonly configs = signal<IEmailConfig[]>([]);\n readonly isLoadingConfigs = signal(false);\n readonly isSendingTest = signal(false);\n\n // Form-bound properties as signals for zoneless change detection\n private readonly _testSendModel = signal({ configId: '', recipient: '' });\n private readonly _variableValues = signal<Record<string, string>>({});\n\n get testSendModel() {\n return this._testSendModel();\n }\n\n get variableValues() {\n return this._variableValues();\n }\n\n updateTestSendModel<K extends 'configId' | 'recipient'>(field: K, value: string): void {\n this._testSendModel.update((m) => ({ ...m, [field]: value }));\n }\n\n updateVariableValue(key: string, value: string): void {\n this._variableValues.update((v) => ({ ...v, [key]: value }));\n }\n\n readonly configOptions = computed(() =>\n this.configs().map((c) => ({\n label: `${c.name} (${this.translate(`email.providers.${c.provider}`)})`,\n value: c.id,\n })),\n );\n\n /** Extract variables from template content */\n readonly templateVariables = computed(() => {\n const template = this.selectedTemplate();\n if (!template) return [];\n\n // Combine all content sources to extract variables\n const content = [\n template.subject,\n template.htmlContent,\n template.textContent || '',\n ].join(' ');\n\n // Find all {{variableName}} patterns\n const matches = content.matchAll(/\\{\\{(\\w+)\\}\\}/g);\n const variables = new Set<string>();\n\n for (const match of matches) {\n variables.add(match[1]);\n }\n\n return Array.from(variables).sort();\n });\n\n onCreate(): void {\n this.router.navigate(['/email/templates/new']);\n }\n\n onEdit(templateId: string): void {\n this.router.navigate(['/email/templates', templateId]);\n }\n\n async loadTemplates(): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await firstValueFrom(\n this.templateService.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.templates.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.loadTemplates();\n }\n\n async onTestSend(template: IEmailTemplate): Promise<void> {\n this.selectedTemplate.set(template);\n this._testSendModel.set({ configId: '', recipient: '' });\n this._variableValues.set({}); // Reset variable values\n this.showTestDialog.set(true);\n await this.loadConfigs();\n }\n\n async loadConfigs(): Promise<void> {\n this.isLoadingConfigs.set(true);\n try {\n const response = await firstValueFrom(\n this.configService.getAll('', {\n pagination: { currentPage: 0, pageSize: 100 },\n filter: { isActive: true },\n select: [],\n sort: {},\n }),\n );\n if (response.success) {\n this.configs.set(response.data ?? []);\n }\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this.isLoadingConfigs.set(false);\n }\n }\n\n async sendTestEmail(): Promise<void> {\n const template = this.selectedTemplate();\n const model = this.testSendModel;\n if (!template || !model.configId || !model.recipient) {\n this.messageService.add({\n severity: 'warn',\n summary: this.translate('common.validation'),\n detail: this.translate('email.template.select.config.and.recipient'),\n });\n return;\n }\n\n this.isSendingTest.set(true);\n try {\n // Build variables object (only include non-empty values)\n const variables: Record<string, string> = {};\n for (const [key, value] of Object.entries(this.variableValues)) {\n if (value) {\n variables[key] = value;\n }\n }\n\n const response = await firstValueFrom(\n this.emailSendService.sendTemplate({\n templateId: template.id,\n to: model.recipient,\n emailConfigId: model.configId,\n variables: Object.keys(variables).length > 0 ? variables : undefined,\n }),\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.template.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.template.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(template: IEmailTemplate): void {\n this.confirmationService.confirm({\n message: this.translate('email.template.delete.confirm', { name: template.name }),\n header: this.translate('email.template.delete.title'),\n icon: 'pi pi-exclamation-triangle',\n acceptButtonStyleClass: 'p-button-danger',\n accept: async () => {\n try {\n await this.templateService.deleteAsync({ id: template.id, type: 'delete' });\n this.messageService.add({\n severity: 'success',\n summary: this.translate('common.success'),\n detail: this.translate('email.template.deleted'),\n });\n this.loadTemplates();\n } catch {\n // Error toast handled by global interceptor\n }\n },\n });\n }\n\n /** Translate helper - public for template usage with dynamic variables */\n translate(key: string, variables?: Record<string, string | number>): string {\n return this.translateAdapter?.translate(key, variables) ?? key;\n }\n}\n"],"names":["i9"],"mappings":"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"}