@flusys/ng-email 1.1.0-beta → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,20 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { Router } from '@angular/router';
4
- import * as i1 from '@angular/forms';
5
- import { FormsModule } from '@angular/forms';
6
4
  import { firstValueFrom } from 'rxjs';
7
- import { ConfirmationService, MessageService } from 'primeng/api';
8
5
  import { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';
9
6
  import { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';
10
- import { AngularModule, PrimeModule } from '@flusys/ng-shared';
7
+ import { EMAIL_CONFIG_PERMISSIONS, AngularModule, PrimeModule, HasPermissionDirective } from '@flusys/ng-shared';
8
+ import { ConfirmationService, MessageService } from 'primeng/api';
11
9
  import { EmailConfigApiService, EmailProviderEnum } from './flusys-ng-email.mjs';
10
+ import * as i1 from '@angular/forms';
12
11
  import * as i2 from 'primeng/button';
13
12
  import * as i3 from 'primeng/confirmdialog';
14
13
  import * as i4 from 'primeng/dialog';
15
14
  import * as i5 from 'primeng/inputtext';
16
15
  import * as i7 from 'primeng/table';
17
16
  import * as i8 from 'primeng/tag';
18
- import * as i9 from 'primeng/toast';
17
+ import * as i8$1 from 'primeng/toast';
19
18
  import * as i10 from 'primeng/tooltip';
20
19
  import * as i11 from '@angular/common';
21
20
 
@@ -23,27 +22,26 @@ import * as i11 from '@angular/common';
23
22
  * Email configuration list component
24
23
  */
25
24
  class EmailConfigListComponent {
25
+ // Permission constants for template
26
+ EMAIL_CONFIG_PERMISSIONS = EMAIL_CONFIG_PERMISSIONS;
26
27
  router = inject(Router);
27
28
  configService = inject(EmailConfigApiService);
28
29
  confirmationService = inject(ConfirmationService);
29
30
  messageService = inject(MessageService);
30
31
  appConfig = inject(APP_CONFIG);
31
- companyContext = inject(LAYOUT_AUTH_STATE);
32
+ companyContext = inject(LAYOUT_AUTH_STATE, { optional: true });
32
33
  isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
33
34
  configs = signal([], ...(ngDevMode ? [{ debugName: "configs" }] : []));
34
35
  totalRecords = signal(0, ...(ngDevMode ? [{ debugName: "totalRecords" }] : []));
35
36
  pageSize = signal(10, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
36
- currentPage = signal(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
37
- showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
38
- currentCompanyName = computed(() => this.companyContext.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
39
- // Test dialog state
40
- showTestDialog = false;
37
+ first = signal(0, ...(ngDevMode ? [{ debugName: "first" }] : []));
38
+ showCompanyInfo = computed(() => this.appConfig.enableCompanyFeature && !!this.companyContext, ...(ngDevMode ? [{ debugName: "showCompanyInfo" }] : []));
39
+ currentCompanyName = computed(() => this.companyContext?.currentCompanyInfo()?.name ?? DEFAULT_APP_NAME, ...(ngDevMode ? [{ debugName: "currentCompanyName" }] : []));
40
+ // Test dialog state (signals for zoneless change detection)
41
+ showTestDialog = signal(false, ...(ngDevMode ? [{ debugName: "showTestDialog" }] : []));
41
42
  selectedConfig = signal(null, ...(ngDevMode ? [{ debugName: "selectedConfig" }] : []));
42
43
  isSendingTest = signal(false, ...(ngDevMode ? [{ debugName: "isSendingTest" }] : []));
43
- testRecipient = '';
44
- ngOnInit() {
45
- this.loadConfigs();
46
- }
44
+ testRecipient = signal('', ...(ngDevMode ? [{ debugName: "testRecipient" }] : []));
47
45
  onCreate() {
48
46
  this.router.navigate(['/email/configs/new']);
49
47
  }
@@ -55,7 +53,7 @@ class EmailConfigListComponent {
55
53
  try {
56
54
  const response = await firstValueFrom(this.configService.getAll('', {
57
55
  pagination: {
58
- currentPage: this.currentPage() - 1,
56
+ currentPage: Math.floor(this.first() / this.pageSize()),
59
57
  pageSize: this.pageSize(),
60
58
  },
61
59
  filter: {},
@@ -71,55 +69,35 @@ class EmailConfigListComponent {
71
69
  this.isLoading.set(false);
72
70
  }
73
71
  }
74
- onPageChange(event) {
75
- this.currentPage.set(event.first / event.rows + 1);
76
- this.pageSize.set(event.rows);
72
+ onLazyLoad(event) {
73
+ this.first.set(event.first ?? 0);
74
+ this.pageSize.set(event.rows ?? 10);
77
75
  this.loadConfigs();
78
76
  }
77
+ providerMeta = {
78
+ [EmailProviderEnum.SMTP]: { icon: 'pi pi-server', label: 'SMTP', severity: 'info' },
79
+ [EmailProviderEnum.SENDGRID]: { icon: 'pi pi-cloud', label: 'SendGrid', severity: 'success' },
80
+ [EmailProviderEnum.MAILGUN]: { icon: 'pi pi-cloud', label: 'Mailgun', severity: 'warn' },
81
+ };
82
+ defaultProviderMeta = { icon: 'pi pi-envelope', label: '', severity: 'secondary' };
79
83
  getProviderIcon(provider) {
80
- switch (provider) {
81
- case EmailProviderEnum.SMTP:
82
- return 'pi pi-server';
83
- case EmailProviderEnum.SENDGRID:
84
- return 'pi pi-cloud';
85
- case EmailProviderEnum.MAILGUN:
86
- return 'pi pi-cloud';
87
- default:
88
- return 'pi pi-envelope';
89
- }
84
+ return this.providerMeta[provider]?.icon ?? this.defaultProviderMeta.icon;
90
85
  }
91
86
  getProviderLabel(provider) {
92
- switch (provider) {
93
- case EmailProviderEnum.SMTP:
94
- return 'SMTP';
95
- case EmailProviderEnum.SENDGRID:
96
- return 'SendGrid';
97
- case EmailProviderEnum.MAILGUN:
98
- return 'Mailgun';
99
- default:
100
- return provider || '';
101
- }
87
+ return this.providerMeta[provider]?.label ?? provider ?? '';
102
88
  }
103
89
  getProviderSeverity(provider) {
104
- switch (provider) {
105
- case EmailProviderEnum.SMTP:
106
- return 'info';
107
- case EmailProviderEnum.SENDGRID:
108
- return 'success';
109
- case EmailProviderEnum.MAILGUN:
110
- return 'warn';
111
- default:
112
- return 'secondary';
113
- }
90
+ return this.providerMeta[provider]?.severity ?? this.defaultProviderMeta.severity;
114
91
  }
115
92
  onTest(config) {
116
93
  this.selectedConfig.set(config);
117
- this.testRecipient = '';
118
- this.showTestDialog = true;
94
+ this.testRecipient.set('');
95
+ this.showTestDialog.set(true);
119
96
  }
120
97
  async sendTestEmail() {
121
98
  const config = this.selectedConfig();
122
- if (!config || !this.testRecipient) {
99
+ const recipient = this.testRecipient();
100
+ if (!config || !recipient) {
123
101
  this.messageService.add({
124
102
  severity: 'warn',
125
103
  summary: 'Validation',
@@ -129,14 +107,14 @@ class EmailConfigListComponent {
129
107
  }
130
108
  this.isSendingTest.set(true);
131
109
  try {
132
- const response = await firstValueFrom(this.configService.sendTest(config.id, this.testRecipient));
110
+ const response = await firstValueFrom(this.configService.sendTest(config.id, recipient));
133
111
  if (response.data?.success) {
134
112
  this.messageService.add({
135
113
  severity: 'success',
136
114
  summary: 'Success',
137
115
  detail: `Test email sent successfully! Message ID: ${response.data.messageId}`,
138
116
  });
139
- this.showTestDialog = false;
117
+ this.showTestDialog.set(false);
140
118
  }
141
119
  else {
142
120
  this.messageService.add({
@@ -146,12 +124,8 @@ class EmailConfigListComponent {
146
124
  });
147
125
  }
148
126
  }
149
- catch (error) {
150
- this.messageService.add({
151
- severity: 'error',
152
- summary: 'Error',
153
- detail: error?.message || 'Failed to send test email.',
154
- });
127
+ catch {
128
+ // Error toast handled by global interceptor
155
129
  }
156
130
  finally {
157
131
  this.isSendingTest.set(false);
@@ -173,118 +147,133 @@ class EmailConfigListComponent {
173
147
  });
174
148
  this.loadConfigs();
175
149
  }
176
- catch (error) {
177
- this.messageService.add({
178
- severity: 'error',
179
- summary: 'Error',
180
- detail: 'Failed to delete configuration.',
181
- });
150
+ catch {
151
+ // Error toast handled by global interceptor
182
152
  }
183
153
  },
184
154
  });
185
155
  }
186
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailConfigListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
187
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: EmailConfigListComponent, isStandalone: true, selector: "lib-email-config-list", providers: [ConfirmationService, MessageService], ngImport: i0, template: `
156
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: EmailConfigListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
157
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: EmailConfigListComponent, isStandalone: true, selector: "lib-email-config-list", providers: [ConfirmationService, MessageService], ngImport: i0, template: `
188
158
  <div class="card">
189
- <div class="flex justify-between items-center mb-4">
159
+ <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
190
160
  <div>
191
- <h3 class="text-xl font-semibold">Email Configurations</h3>
161
+ <h3 class="text-lg sm:text-xl font-semibold m-0">Email Configurations</h3>
192
162
  @if (showCompanyInfo()) {
193
- <p class="text-sm text-gray-600 mt-1">
163
+ <p class="text-sm text-muted-color mt-1">
194
164
  Company: {{ currentCompanyName() }}
195
165
  </p>
196
166
  }
197
167
  </div>
198
168
  <p-button
169
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.CREATE"
199
170
  label="New Configuration"
200
171
  icon="pi pi-plus"
201
172
  (onClick)="onCreate()"
173
+ styleClass="w-full sm:w-auto"
202
174
  />
203
175
  </div>
204
176
 
205
- <p-table
206
- [value]="configs()"
207
- [loading]="isLoading()"
208
- [paginator]="true"
209
- [rows]="pageSize()"
210
- [totalRecords]="totalRecords()"
211
- [lazy]="true"
212
- (onLazyLoad)="onPageChange($event)"
213
- styleClass="p-datatable-sm"
214
- >
215
- <ng-template #header>
216
- <tr>
217
- <th>Name</th>
218
- <th>Provider</th>
219
- <th>From Email</th>
220
- <th>Status</th>
221
- <th>Created</th>
222
- <th style="width: 150px">Actions</th>
223
- </tr>
224
- </ng-template>
177
+ <div class="overflow-x-auto -mx-4 sm:mx-0">
178
+ <p-table
179
+ [value]="configs()"
180
+ [loading]="isLoading()"
181
+ [paginator]="totalRecords() > 0"
182
+ [rows]="pageSize()"
183
+ [first]="first()"
184
+ [totalRecords]="totalRecords()"
185
+ [lazy]="true"
186
+ (onLazyLoad)="onLazyLoad($event)"
187
+ [rowsPerPageOptions]="[10, 25, 50]"
188
+ styleClass="p-datatable-sm"
189
+ [tableStyle]="{ 'min-width': '50rem' }"
190
+ >
191
+ <ng-template #header>
192
+ <tr>
193
+ <th>Name</th>
194
+ <th>Provider</th>
195
+ <th class="hidden md:table-cell">From Email</th>
196
+ <th>Status</th>
197
+ <th class="hidden lg:table-cell">Created</th>
198
+ <th class="w-[120px]">Actions</th>
199
+ </tr>
200
+ </ng-template>
225
201
 
226
- <ng-template #body let-config>
227
- <tr>
228
- <td>
229
- <i [class]="getProviderIcon(config.provider)" class="mr-2"></i>
230
- {{ config.name }}
231
- </td>
232
- <td>
233
- <p-tag
234
- [value]="getProviderLabel(config.provider)"
235
- [severity]="getProviderSeverity(config.provider)"
236
- />
237
- </td>
238
- <td>{{ config.fromEmail || '-' }}</td>
239
- <td>
240
- <p-tag
241
- [value]="config.isActive ? 'Active' : 'Inactive'"
242
- [severity]="config.isActive ? 'success' : 'secondary'"
243
- />
244
- </td>
245
- <td>{{ config.createdAt | date: 'short' }}</td>
246
- <td>
247
- <p-button
248
- icon="pi pi-send"
249
- [text]="true"
250
- size="small"
251
- pTooltip="Test"
252
- (onClick)="onTest(config)"
253
- />
254
- <p-button
255
- icon="pi pi-pencil"
256
- [text]="true"
257
- severity="secondary"
258
- size="small"
259
- (onClick)="onEdit(config.id)"
260
- />
261
- <p-button
262
- icon="pi pi-trash"
263
- [text]="true"
264
- severity="danger"
265
- size="small"
266
- (onClick)="onDelete(config)"
267
- />
268
- </td>
269
- </tr>
270
- </ng-template>
202
+ <ng-template #body let-config>
203
+ <tr>
204
+ <td>
205
+ <i [class]="getProviderIcon(config.provider)" class="mr-2 text-muted-color"></i>
206
+ {{ config.name }}
207
+ @if (config.isDefault) {
208
+ <p-tag value="Default" severity="contrast" class="ml-2" />
209
+ }
210
+ </td>
211
+ <td>
212
+ <p-tag
213
+ [value]="getProviderLabel(config.provider)"
214
+ [severity]="getProviderSeverity(config.provider)"
215
+ />
216
+ </td>
217
+ <td class="hidden md:table-cell">{{ config.fromEmail || '-' }}</td>
218
+ <td>
219
+ <p-tag
220
+ [value]="config.isActive ? 'Active' : 'Inactive'"
221
+ [severity]="config.isActive ? 'success' : 'secondary'"
222
+ />
223
+ </td>
224
+ <td class="hidden lg:table-cell">{{ config.createdAt | date: 'short' }}</td>
225
+ <td>
226
+ <div class="flex gap-1">
227
+ <p-button
228
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.UPDATE"
229
+ icon="pi pi-send"
230
+ [text]="true"
231
+ size="small"
232
+ pTooltip="Test"
233
+ (onClick)="onTest(config)"
234
+ />
235
+ <p-button
236
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.UPDATE"
237
+ icon="pi pi-pencil"
238
+ [text]="true"
239
+ severity="secondary"
240
+ size="small"
241
+ pTooltip="Edit"
242
+ (onClick)="onEdit(config.id)"
243
+ />
244
+ <p-button
245
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.DELETE"
246
+ icon="pi pi-trash"
247
+ [text]="true"
248
+ severity="danger"
249
+ size="small"
250
+ pTooltip="Delete"
251
+ (onClick)="onDelete(config)"
252
+ />
253
+ </div>
254
+ </td>
255
+ </tr>
256
+ </ng-template>
271
257
 
272
- <ng-template #emptymessage>
273
- <tr>
274
- <td colspan="6" class="text-center py-4">
275
- No email configurations found. Add a provider to start sending emails.
276
- </td>
277
- </tr>
278
- </ng-template>
279
- </p-table>
258
+ <ng-template #emptymessage>
259
+ <tr>
260
+ <td colspan="6" class="text-center py-4 text-muted-color">
261
+ No email configurations found. Add a provider to start sending emails.
262
+ </td>
263
+ </tr>
264
+ </ng-template>
265
+ </p-table>
266
+ </div>
280
267
  </div>
281
268
 
282
269
  <!-- Test Email Dialog -->
283
270
  <p-dialog
284
271
  header="Test Email Configuration"
285
- [(visible)]="showTestDialog"
272
+ [visible]="showTestDialog()"
273
+ (visibleChange)="showTestDialog.set($event)"
286
274
  [modal]="true"
287
- [style]="{ width: '400px' }"
275
+ [style]="{ width: '95vw', maxWidth: '400px' }"
276
+ [breakpoints]="{ '575px': '95vw' }"
288
277
  >
289
278
  <div class="grid gap-4">
290
279
  <div class="field">
@@ -309,12 +298,13 @@ class EmailConfigListComponent {
309
298
  <label class="block font-medium mb-2">Recipient Email *</label>
310
299
  <input
311
300
  pInputText
312
- [(ngModel)]="testRecipient"
301
+ [ngModel]="testRecipient()"
302
+ (ngModelChange)="testRecipient.set($event)"
313
303
  class="w-full"
314
304
  placeholder="recipient@example.com"
315
305
  type="email"
316
306
  />
317
- <small class="text-gray-500 mt-1 block">
307
+ <small class="text-muted-color mt-1 block">
318
308
  A test email will be sent to this address
319
309
  </small>
320
310
  </div>
@@ -323,8 +313,9 @@ class EmailConfigListComponent {
323
313
  <ng-template #footer>
324
314
  <p-button
325
315
  label="Cancel"
326
- [text]="true"
327
- (onClick)="showTestDialog = false"
316
+ severity="secondary"
317
+ [outlined]="true"
318
+ (onClick)="showTestDialog.set(false)"
328
319
  />
329
320
  <p-button
330
321
  label="Send Test"
@@ -337,117 +328,135 @@ class EmailConfigListComponent {
337
328
 
338
329
  <p-confirmDialog />
339
330
  <p-toast />
340
- `, 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: 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: i9.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: "ngmodule", type: FormsModule }, { kind: "pipe", type: i11.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
331
+ `, 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: 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 });
341
332
  }
342
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailConfigListComponent, decorators: [{
333
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: EmailConfigListComponent, decorators: [{
343
334
  type: Component,
344
335
  args: [{
345
336
  selector: 'lib-email-config-list',
346
- standalone: true,
347
337
  changeDetection: ChangeDetectionStrategy.OnPush,
348
- imports: [AngularModule, PrimeModule, FormsModule],
338
+ imports: [AngularModule, PrimeModule, HasPermissionDirective],
349
339
  providers: [ConfirmationService, MessageService],
350
340
  template: `
351
341
  <div class="card">
352
- <div class="flex justify-between items-center mb-4">
342
+ <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
353
343
  <div>
354
- <h3 class="text-xl font-semibold">Email Configurations</h3>
344
+ <h3 class="text-lg sm:text-xl font-semibold m-0">Email Configurations</h3>
355
345
  @if (showCompanyInfo()) {
356
- <p class="text-sm text-gray-600 mt-1">
346
+ <p class="text-sm text-muted-color mt-1">
357
347
  Company: {{ currentCompanyName() }}
358
348
  </p>
359
349
  }
360
350
  </div>
361
351
  <p-button
352
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.CREATE"
362
353
  label="New Configuration"
363
354
  icon="pi pi-plus"
364
355
  (onClick)="onCreate()"
356
+ styleClass="w-full sm:w-auto"
365
357
  />
366
358
  </div>
367
359
 
368
- <p-table
369
- [value]="configs()"
370
- [loading]="isLoading()"
371
- [paginator]="true"
372
- [rows]="pageSize()"
373
- [totalRecords]="totalRecords()"
374
- [lazy]="true"
375
- (onLazyLoad)="onPageChange($event)"
376
- styleClass="p-datatable-sm"
377
- >
378
- <ng-template #header>
379
- <tr>
380
- <th>Name</th>
381
- <th>Provider</th>
382
- <th>From Email</th>
383
- <th>Status</th>
384
- <th>Created</th>
385
- <th style="width: 150px">Actions</th>
386
- </tr>
387
- </ng-template>
360
+ <div class="overflow-x-auto -mx-4 sm:mx-0">
361
+ <p-table
362
+ [value]="configs()"
363
+ [loading]="isLoading()"
364
+ [paginator]="totalRecords() > 0"
365
+ [rows]="pageSize()"
366
+ [first]="first()"
367
+ [totalRecords]="totalRecords()"
368
+ [lazy]="true"
369
+ (onLazyLoad)="onLazyLoad($event)"
370
+ [rowsPerPageOptions]="[10, 25, 50]"
371
+ styleClass="p-datatable-sm"
372
+ [tableStyle]="{ 'min-width': '50rem' }"
373
+ >
374
+ <ng-template #header>
375
+ <tr>
376
+ <th>Name</th>
377
+ <th>Provider</th>
378
+ <th class="hidden md:table-cell">From Email</th>
379
+ <th>Status</th>
380
+ <th class="hidden lg:table-cell">Created</th>
381
+ <th class="w-[120px]">Actions</th>
382
+ </tr>
383
+ </ng-template>
388
384
 
389
- <ng-template #body let-config>
390
- <tr>
391
- <td>
392
- <i [class]="getProviderIcon(config.provider)" class="mr-2"></i>
393
- {{ config.name }}
394
- </td>
395
- <td>
396
- <p-tag
397
- [value]="getProviderLabel(config.provider)"
398
- [severity]="getProviderSeverity(config.provider)"
399
- />
400
- </td>
401
- <td>{{ config.fromEmail || '-' }}</td>
402
- <td>
403
- <p-tag
404
- [value]="config.isActive ? 'Active' : 'Inactive'"
405
- [severity]="config.isActive ? 'success' : 'secondary'"
406
- />
407
- </td>
408
- <td>{{ config.createdAt | date: 'short' }}</td>
409
- <td>
410
- <p-button
411
- icon="pi pi-send"
412
- [text]="true"
413
- size="small"
414
- pTooltip="Test"
415
- (onClick)="onTest(config)"
416
- />
417
- <p-button
418
- icon="pi pi-pencil"
419
- [text]="true"
420
- severity="secondary"
421
- size="small"
422
- (onClick)="onEdit(config.id)"
423
- />
424
- <p-button
425
- icon="pi pi-trash"
426
- [text]="true"
427
- severity="danger"
428
- size="small"
429
- (onClick)="onDelete(config)"
430
- />
431
- </td>
432
- </tr>
433
- </ng-template>
385
+ <ng-template #body let-config>
386
+ <tr>
387
+ <td>
388
+ <i [class]="getProviderIcon(config.provider)" class="mr-2 text-muted-color"></i>
389
+ {{ config.name }}
390
+ @if (config.isDefault) {
391
+ <p-tag value="Default" severity="contrast" class="ml-2" />
392
+ }
393
+ </td>
394
+ <td>
395
+ <p-tag
396
+ [value]="getProviderLabel(config.provider)"
397
+ [severity]="getProviderSeverity(config.provider)"
398
+ />
399
+ </td>
400
+ <td class="hidden md:table-cell">{{ config.fromEmail || '-' }}</td>
401
+ <td>
402
+ <p-tag
403
+ [value]="config.isActive ? 'Active' : 'Inactive'"
404
+ [severity]="config.isActive ? 'success' : 'secondary'"
405
+ />
406
+ </td>
407
+ <td class="hidden lg:table-cell">{{ config.createdAt | date: 'short' }}</td>
408
+ <td>
409
+ <div class="flex gap-1">
410
+ <p-button
411
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.UPDATE"
412
+ icon="pi pi-send"
413
+ [text]="true"
414
+ size="small"
415
+ pTooltip="Test"
416
+ (onClick)="onTest(config)"
417
+ />
418
+ <p-button
419
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.UPDATE"
420
+ icon="pi pi-pencil"
421
+ [text]="true"
422
+ severity="secondary"
423
+ size="small"
424
+ pTooltip="Edit"
425
+ (onClick)="onEdit(config.id)"
426
+ />
427
+ <p-button
428
+ *hasPermission="EMAIL_CONFIG_PERMISSIONS.DELETE"
429
+ icon="pi pi-trash"
430
+ [text]="true"
431
+ severity="danger"
432
+ size="small"
433
+ pTooltip="Delete"
434
+ (onClick)="onDelete(config)"
435
+ />
436
+ </div>
437
+ </td>
438
+ </tr>
439
+ </ng-template>
434
440
 
435
- <ng-template #emptymessage>
436
- <tr>
437
- <td colspan="6" class="text-center py-4">
438
- No email configurations found. Add a provider to start sending emails.
439
- </td>
440
- </tr>
441
- </ng-template>
442
- </p-table>
441
+ <ng-template #emptymessage>
442
+ <tr>
443
+ <td colspan="6" class="text-center py-4 text-muted-color">
444
+ No email configurations found. Add a provider to start sending emails.
445
+ </td>
446
+ </tr>
447
+ </ng-template>
448
+ </p-table>
449
+ </div>
443
450
  </div>
444
451
 
445
452
  <!-- Test Email Dialog -->
446
453
  <p-dialog
447
454
  header="Test Email Configuration"
448
- [(visible)]="showTestDialog"
455
+ [visible]="showTestDialog()"
456
+ (visibleChange)="showTestDialog.set($event)"
449
457
  [modal]="true"
450
- [style]="{ width: '400px' }"
458
+ [style]="{ width: '95vw', maxWidth: '400px' }"
459
+ [breakpoints]="{ '575px': '95vw' }"
451
460
  >
452
461
  <div class="grid gap-4">
453
462
  <div class="field">
@@ -472,12 +481,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
472
481
  <label class="block font-medium mb-2">Recipient Email *</label>
473
482
  <input
474
483
  pInputText
475
- [(ngModel)]="testRecipient"
484
+ [ngModel]="testRecipient()"
485
+ (ngModelChange)="testRecipient.set($event)"
476
486
  class="w-full"
477
487
  placeholder="recipient@example.com"
478
488
  type="email"
479
489
  />
480
- <small class="text-gray-500 mt-1 block">
490
+ <small class="text-muted-color mt-1 block">
481
491
  A test email will be sent to this address
482
492
  </small>
483
493
  </div>
@@ -486,8 +496,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
486
496
  <ng-template #footer>
487
497
  <p-button
488
498
  label="Cancel"
489
- [text]="true"
490
- (onClick)="showTestDialog = false"
499
+ severity="secondary"
500
+ [outlined]="true"
501
+ (onClick)="showTestDialog.set(false)"
491
502
  />
492
503
  <p-button
493
504
  label="Send Test"
@@ -505,4 +516,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
505
516
  }] });
506
517
 
507
518
  export { EmailConfigListComponent };
508
- //# sourceMappingURL=flusys-ng-email-email-config-list.component-CJhSPkCZ.mjs.map
519
+ //# sourceMappingURL=flusys-ng-email-email-config-list.component-Dt9PY9xD.mjs.map