@flusys/ng-email 1.0.0-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-email-template-list.component-91djzx28.mjs","sources":["../../../projects/ng-email/pages/template/template-list.component.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport { Router } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\nimport { APP_CONFIG, DEFAULT_APP_NAME } from '@flusys/ng-core';\nimport { LAYOUT_AUTH_STATE } from '@flusys/ng-layout';\nimport { AngularModule, EMAIL_TEMPLATE_PERMISSIONS, HasPermissionDirective, PrimeModule } 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 standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AngularModule, PrimeModule, HasPermissionDirective],\n providers: [ConfirmationService, MessageService],\n template: `\n <div class=\"card\">\n <div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4\">\n <div>\n <h3 class=\"text-lg sm:text-xl font-semibold m-0\">Email Templates</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">\n Company: {{ currentCompanyName() }}\n </p>\n }\n </div>\n <p-button\n *hasPermission=\"EMAIL_TEMPLATE_PERMISSIONS.CREATE\"\n label=\"New Template\"\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>Name</th>\n <th class=\"hidden md:table-cell\">Slug</th>\n <th class=\"hidden lg:table-cell\">Subject</th>\n <th>Type</th>\n <th>Status</th>\n <th class=\"hidden xl:table-cell\">Created</th>\n <th class=\"w-[120px]\">Actions</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 ? 'HTML' : 'Text'\"\n [severity]=\"template.isHtml ? 'info' : 'secondary'\"\n />\n </td>\n <td>\n <p-tag\n [value]=\"template.isActive ? 'Active' : 'Inactive'\"\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=\"Test Send\"\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=\"Edit\"\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=\"Delete\"\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 No email templates found. Create your first template to get started.\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n\n <!-- Test Send Dialog -->\n <p-dialog\n header=\"Test Send Email\"\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\">Template</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 Configuration *</label>\n <p-select\n [ngModel]=\"testSendModel.configId\"\n (ngModelChange)=\"updateTestSendModel('configId', $event)\"\n [options]=\"configOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n placeholder=\"Select configuration\"\n class=\"w-full\"\n [loading]=\"isLoadingConfigs()\"\n />\n </div>\n\n <div class=\"field\">\n <label class=\"block font-medium mb-2\">Recipient Email *</label>\n <input\n pInputText\n [ngModel]=\"testSendModel.recipient\"\n (ngModelChange)=\"updateTestSendModel('recipient', $event)\"\n class=\"w-full\"\n placeholder=\"recipient@example.com\"\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 Template Variables\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]=\"'Enter value for ' + variable\"\n />\n </div>\n }\n </div>\n </div>\n }\n </div>\n\n <ng-template #footer>\n <p-button\n label=\"Cancel\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (onClick)=\"showTestDialog.set(false)\"\n />\n <p-button\n label=\"Send Test\"\n icon=\"pi pi-send\"\n [loading]=\"isSendingTest()\"\n (onClick)=\"sendTestEmail()\"\n />\n </ng-template>\n </p-dialog>\n\n <p-confirmDialog />\n <p-toast />\n `,\n})\nexport class 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\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} (${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 } 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 } 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: 'Validation',\n detail: 'Please select a configuration and enter recipient email.',\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: 'Success',\n detail: `Test email sent! Message ID: ${response.data.messageId}`,\n });\n this.showTestDialog.set(false);\n } else {\n this.messageService.add({\n severity: 'error',\n summary: 'Error',\n detail: response.data?.error || 'Failed to send test email.',\n });\n }\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this.isSendingTest.set(false);\n }\n }\n\n onDelete(template: IEmailTemplate): void {\n this.confirmationService.confirm({\n message: `Are you sure you want to delete \"${template.name}\"?`,\n header: 'Delete Template',\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: 'Success',\n detail: 'Template deleted successfully.',\n });\n this.loadTemplates();\n } catch {\n // Error toast handled by global interceptor\n }\n },\n });\n }\n}\n"],"names":["i9"],"mappings":"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"}
@@ -0,0 +1,579 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injectable, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { HttpClient } from '@angular/common/http';
4
+ import { ApiResourceService, AngularModule } from '@flusys/ng-shared';
5
+ import { firstValueFrom } from 'rxjs';
6
+ import { APP_CONFIG } from '@flusys/ng-core';
7
+ import * as i1 from '@angular/router';
8
+
9
+ /**
10
+ * Supported email provider types
11
+ */
12
+ var EmailProviderEnum;
13
+ (function (EmailProviderEnum) {
14
+ EmailProviderEnum["SMTP"] = "smtp";
15
+ EmailProviderEnum["SENDGRID"] = "sendgrid";
16
+ EmailProviderEnum["MAILGUN"] = "mailgun";
17
+ })(EmailProviderEnum || (EmailProviderEnum = {}));
18
+
19
+ /**
20
+ * Email content block types
21
+ */
22
+ var EmailBlockType;
23
+ (function (EmailBlockType) {
24
+ EmailBlockType["TEXT"] = "text";
25
+ EmailBlockType["IMAGE"] = "image";
26
+ EmailBlockType["BUTTON"] = "button";
27
+ EmailBlockType["DIVIDER"] = "divider";
28
+ EmailBlockType["HTML"] = "html";
29
+ })(EmailBlockType || (EmailBlockType = {}));
30
+
31
+ /**
32
+ * Default email settings
33
+ */
34
+ const DEFAULT_EMAIL_SETTINGS = {
35
+ maxWidth: '600px',
36
+ backgroundColor: '#f5f5f5',
37
+ fontFamily: 'Arial, sans-serif',
38
+ baseTextColor: '#333333',
39
+ };
40
+ /**
41
+ * Create a new email schema
42
+ */
43
+ function createEmailSchema(partial = {}) {
44
+ return {
45
+ id: crypto.randomUUID(),
46
+ version: '1.0.0',
47
+ name: partial.name || 'Untitled Template',
48
+ subject: partial.subject || '',
49
+ sections: partial.sections || [
50
+ { id: crypto.randomUUID(), type: 'header', name: 'Header', blocks: [], order: 0 },
51
+ { id: crypto.randomUUID(), type: 'body', name: 'Body', blocks: [], order: 1 },
52
+ { id: crypto.randomUUID(), type: 'footer', name: 'Footer', blocks: [], order: 2 },
53
+ ],
54
+ settings: { ...DEFAULT_EMAIL_SETTINGS, ...partial.settings },
55
+ variables: partial.variables || [],
56
+ ...partial,
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Email Config API Service
62
+ * Handles email configuration CRUD operations
63
+ * Endpoint: POST /email/email-config/*
64
+ */
65
+ class EmailConfigApiService extends ApiResourceService {
66
+ constructor() {
67
+ const http = inject(HttpClient);
68
+ super('email/email-config', http);
69
+ }
70
+ /**
71
+ * Send test email to verify configuration
72
+ */
73
+ sendTest(configId, recipient) {
74
+ return this.http.post(`${this.baseUrl.replace('/email-config', '')}/send/test`, { emailConfigId: configId, recipient });
75
+ }
76
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
77
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailConfigApiService, providedIn: 'root' });
78
+ }
79
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailConfigApiService, decorators: [{
80
+ type: Injectable,
81
+ args: [{
82
+ providedIn: 'root',
83
+ }]
84
+ }], ctorParameters: () => [] });
85
+
86
+ /**
87
+ * Email Template API Service
88
+ * Handles email template CRUD operations
89
+ * Endpoint: POST /email/email-template/*
90
+ */
91
+ class EmailTemplateApiService extends ApiResourceService {
92
+ constructor() {
93
+ const http = inject(HttpClient);
94
+ super('email/email-template', http);
95
+ }
96
+ /**
97
+ * Get template by slug (POST-only RPC pattern)
98
+ */
99
+ getBySlug(slug) {
100
+ return this.http.post(`${this.baseUrl}/get-by-slug`, { slug });
101
+ }
102
+ /**
103
+ * Create a new email template
104
+ * POST /email/email-template/insert
105
+ * Uses ICreateEmailTemplateDto which has different required fields than update
106
+ */
107
+ createTemplate(dto) {
108
+ return this.http.post(`${this.baseUrl}/insert`, dto);
109
+ }
110
+ /**
111
+ * Create a new email template (async/await version)
112
+ * POST /email/email-template/insert
113
+ */
114
+ async createTemplateAsync(dto) {
115
+ return firstValueFrom(this.createTemplate(dto));
116
+ }
117
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailTemplateApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
118
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailTemplateApiService, providedIn: 'root' });
119
+ }
120
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailTemplateApiService, decorators: [{
121
+ type: Injectable,
122
+ args: [{
123
+ providedIn: 'root',
124
+ }]
125
+ }], ctorParameters: () => [] });
126
+
127
+ /**
128
+ * Email Send Service
129
+ * Handles sending emails directly or using templates
130
+ * Endpoint: POST /email/send/*
131
+ *
132
+ * Note: For testing configurations, use EmailConfigApiService.sendTest()
133
+ */
134
+ class EmailSendService {
135
+ http = inject(HttpClient);
136
+ baseUrl = `${inject(APP_CONFIG).apiBaseUrl}/email/send`;
137
+ /**
138
+ * Send email directly (without template)
139
+ */
140
+ sendDirect(dto) {
141
+ return this.http.post(`${this.baseUrl}/direct`, dto);
142
+ }
143
+ /**
144
+ * Send email using template
145
+ */
146
+ sendTemplate(dto) {
147
+ return this.http.post(`${this.baseUrl}/template`, dto);
148
+ }
149
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailSendService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
150
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailSendService, providedIn: 'root' });
151
+ }
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailSendService, decorators: [{
153
+ type: Injectable,
154
+ args: [{
155
+ providedIn: 'root',
156
+ }]
157
+ }] });
158
+
159
+ /**
160
+ * Manages the email builder UI state including schema, selection, and UI toggles.
161
+ * Should be provided at the EmailBuilder component level (not root).
162
+ */
163
+ class EmailBuilderStateService {
164
+ // Schema state
165
+ _schema = signal(createEmailSchema(), ...(ngDevMode ? [{ debugName: "_schema" }] : []));
166
+ _isDirty = signal(false, ...(ngDevMode ? [{ debugName: "_isDirty" }] : []));
167
+ // Selection state
168
+ _selectedSectionId = signal(null, ...(ngDevMode ? [{ debugName: "_selectedSectionId" }] : []));
169
+ _selectedBlockId = signal(null, ...(ngDevMode ? [{ debugName: "_selectedBlockId" }] : []));
170
+ // View mode
171
+ _viewMode = signal('visual', ...(ngDevMode ? [{ debugName: "_viewMode" }] : []));
172
+ // Public readonly signals
173
+ schema = this._schema.asReadonly();
174
+ isDirty = this._isDirty.asReadonly();
175
+ selectedSectionId = this._selectedSectionId.asReadonly();
176
+ selectedBlockId = this._selectedBlockId.asReadonly();
177
+ viewMode = this._viewMode.asReadonly();
178
+ // Computed signals
179
+ sections = computed(() => this._schema().sections, ...(ngDevMode ? [{ debugName: "sections" }] : []));
180
+ templateName = computed(() => this._schema().name, ...(ngDevMode ? [{ debugName: "templateName" }] : []));
181
+ subject = computed(() => this._schema().subject, ...(ngDevMode ? [{ debugName: "subject" }] : []));
182
+ settings = computed(() => this._schema().settings ?? DEFAULT_EMAIL_SETTINGS, ...(ngDevMode ? [{ debugName: "settings" }] : []));
183
+ variables = computed(() => this._schema().variables ?? [], ...(ngDevMode ? [{ debugName: "variables" }] : []));
184
+ headerSection = computed(() => {
185
+ return this.sections().find((s) => s.type === 'header') ?? null;
186
+ }, ...(ngDevMode ? [{ debugName: "headerSection" }] : []));
187
+ bodySection = computed(() => {
188
+ return this.sections().find((s) => s.type === 'body') ?? null;
189
+ }, ...(ngDevMode ? [{ debugName: "bodySection" }] : []));
190
+ footerSection = computed(() => {
191
+ return this.sections().find((s) => s.type === 'footer') ?? null;
192
+ }, ...(ngDevMode ? [{ debugName: "footerSection" }] : []));
193
+ selectedSection = computed(() => {
194
+ const sectionId = this._selectedSectionId();
195
+ if (!sectionId)
196
+ return null;
197
+ return this.sections().find((s) => s.id === sectionId) ?? null;
198
+ }, ...(ngDevMode ? [{ debugName: "selectedSection" }] : []));
199
+ selectedBlock = computed(() => {
200
+ const blockId = this._selectedBlockId();
201
+ if (!blockId)
202
+ return null;
203
+ for (const section of this.sections()) {
204
+ const block = section.blocks.find((b) => b.id === blockId);
205
+ if (block)
206
+ return block;
207
+ }
208
+ return null;
209
+ }, ...(ngDevMode ? [{ debugName: "selectedBlock" }] : []));
210
+ allBlocks = computed(() => {
211
+ return this.sections().flatMap((s) => s.blocks);
212
+ }, ...(ngDevMode ? [{ debugName: "allBlocks" }] : []));
213
+ // =========================================
214
+ // Schema Operations
215
+ // =========================================
216
+ /**
217
+ * Load an existing schema into the builder
218
+ */
219
+ loadSchema(schema) {
220
+ const cloned = structuredClone(schema);
221
+ this._schema.set(cloned);
222
+ this._isDirty.set(false);
223
+ this._selectedSectionId.set(null);
224
+ this._selectedBlockId.set(null);
225
+ }
226
+ /**
227
+ * Create a new empty schema
228
+ */
229
+ createNewSchema(name = 'Untitled Template') {
230
+ this._schema.set(createEmailSchema({ name }));
231
+ this._isDirty.set(true);
232
+ this._selectedSectionId.set(null);
233
+ this._selectedBlockId.set(null);
234
+ }
235
+ /**
236
+ * Update template metadata
237
+ */
238
+ updateSchemaMeta(updates) {
239
+ this._schema.update((schema) => ({
240
+ ...schema,
241
+ ...updates,
242
+ }));
243
+ this._isDirty.set(true);
244
+ }
245
+ /**
246
+ * Mark schema as saved (clears dirty flag)
247
+ */
248
+ markAsSaved() {
249
+ this._isDirty.set(false);
250
+ }
251
+ /**
252
+ * Set view mode (visual or html)
253
+ */
254
+ setViewMode(mode) {
255
+ this._viewMode.set(mode);
256
+ }
257
+ // =========================================
258
+ // Section Operations
259
+ // =========================================
260
+ /**
261
+ * Update section properties
262
+ */
263
+ updateSection(sectionId, updates) {
264
+ this._schema.update((schema) => ({
265
+ ...schema,
266
+ sections: schema.sections.map((s) => s.id === sectionId ? { ...s, ...updates } : s),
267
+ }));
268
+ this._isDirty.set(true);
269
+ }
270
+ // =========================================
271
+ // Block Operations
272
+ // =========================================
273
+ /**
274
+ * Add a new block to a section
275
+ */
276
+ addBlock(sectionId, blockType, block) {
277
+ const newBlock = {
278
+ id: crypto.randomUUID(),
279
+ type: blockType,
280
+ order: 0,
281
+ content: this.getDefaultContent(blockType),
282
+ ...block,
283
+ };
284
+ this._schema.update((schema) => ({
285
+ ...schema,
286
+ sections: schema.sections.map((s) => {
287
+ if (s.id !== sectionId)
288
+ return s;
289
+ const blocks = [...s.blocks, newBlock];
290
+ // Update order
291
+ blocks.forEach((b, i) => (b.order = i));
292
+ return { ...s, blocks };
293
+ }),
294
+ }));
295
+ this._isDirty.set(true);
296
+ return newBlock.id;
297
+ }
298
+ /**
299
+ * Update block properties
300
+ */
301
+ updateBlock(sectionId, blockId, updates) {
302
+ this._schema.update((schema) => ({
303
+ ...schema,
304
+ sections: schema.sections.map((s) => {
305
+ if (s.id !== sectionId)
306
+ return s;
307
+ return {
308
+ ...s,
309
+ blocks: s.blocks.map((b) => (b.id === blockId ? { ...b, ...updates } : b)),
310
+ };
311
+ }),
312
+ }));
313
+ this._isDirty.set(true);
314
+ }
315
+ /**
316
+ * Delete a block from a section
317
+ */
318
+ deleteBlock(sectionId, blockId) {
319
+ this._schema.update((schema) => ({
320
+ ...schema,
321
+ sections: schema.sections.map((s) => {
322
+ if (s.id !== sectionId)
323
+ return s;
324
+ const blocks = s.blocks.filter((b) => b.id !== blockId);
325
+ // Update order
326
+ blocks.forEach((b, i) => (b.order = i));
327
+ return { ...s, blocks };
328
+ }),
329
+ }));
330
+ this._isDirty.set(true);
331
+ // Clear selection if deleted block was selected
332
+ if (this._selectedBlockId() === blockId) {
333
+ this._selectedBlockId.set(null);
334
+ }
335
+ }
336
+ /**
337
+ * Reorder blocks within a section
338
+ */
339
+ reorderBlocks(sectionId, fromIndex, toIndex) {
340
+ this._schema.update((schema) => ({
341
+ ...schema,
342
+ sections: schema.sections.map((s) => {
343
+ if (s.id !== sectionId)
344
+ return s;
345
+ const blocks = [...s.blocks];
346
+ const [removed] = blocks.splice(fromIndex, 1);
347
+ blocks.splice(toIndex, 0, removed);
348
+ // Update order
349
+ blocks.forEach((b, i) => (b.order = i));
350
+ return { ...s, blocks };
351
+ }),
352
+ }));
353
+ this._isDirty.set(true);
354
+ }
355
+ /**
356
+ * Duplicate a block
357
+ */
358
+ duplicateBlock(sectionId, blockId) {
359
+ const section = this.sections().find((s) => s.id === sectionId);
360
+ const block = section?.blocks.find((b) => b.id === blockId);
361
+ if (!block)
362
+ return null;
363
+ const newBlock = {
364
+ ...structuredClone(block),
365
+ id: crypto.randomUUID(),
366
+ };
367
+ this._schema.update((schema) => ({
368
+ ...schema,
369
+ sections: schema.sections.map((s) => {
370
+ if (s.id !== sectionId)
371
+ return s;
372
+ const blockIndex = s.blocks.findIndex((b) => b.id === blockId);
373
+ const blocks = [...s.blocks];
374
+ blocks.splice(blockIndex + 1, 0, newBlock);
375
+ // Update order
376
+ blocks.forEach((b, i) => (b.order = i));
377
+ return { ...s, blocks };
378
+ }),
379
+ }));
380
+ this._isDirty.set(true);
381
+ return newBlock.id;
382
+ }
383
+ // =========================================
384
+ // Selection Operations
385
+ // =========================================
386
+ /**
387
+ * Select a section
388
+ */
389
+ selectSection(sectionId) {
390
+ this._selectedSectionId.set(sectionId);
391
+ this._selectedBlockId.set(null);
392
+ }
393
+ /**
394
+ * Select a block
395
+ */
396
+ selectBlock(blockId) {
397
+ if (blockId) {
398
+ // Find the section containing this block
399
+ for (const section of this.sections()) {
400
+ if (section.blocks.some((b) => b.id === blockId)) {
401
+ this._selectedSectionId.set(section.id);
402
+ break;
403
+ }
404
+ }
405
+ }
406
+ this._selectedBlockId.set(blockId);
407
+ }
408
+ /**
409
+ * Clear all selection
410
+ */
411
+ clearSelection() {
412
+ this._selectedSectionId.set(null);
413
+ this._selectedBlockId.set(null);
414
+ }
415
+ // =========================================
416
+ // Helper Methods
417
+ // =========================================
418
+ /**
419
+ * Get default content for a block type
420
+ */
421
+ getDefaultContent(type) {
422
+ switch (type) {
423
+ case EmailBlockType.TEXT:
424
+ return { text: 'Enter your text here...', html: '<p>Enter your text here...</p>' };
425
+ case EmailBlockType.IMAGE:
426
+ return { src: '', alt: 'Image' };
427
+ case EmailBlockType.BUTTON:
428
+ return {
429
+ text: 'Click Here',
430
+ link: 'https://',
431
+ backgroundColor: '#007bff',
432
+ textColor: '#ffffff',
433
+ borderRadius: '4px',
434
+ };
435
+ case EmailBlockType.DIVIDER:
436
+ return { height: '1px', color: '#cccccc', style: 'solid' };
437
+ case EmailBlockType.HTML:
438
+ return { html: '<!-- Custom HTML -->' };
439
+ default:
440
+ return {};
441
+ }
442
+ }
443
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailBuilderStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
444
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailBuilderStateService });
445
+ }
446
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailBuilderStateService, decorators: [{
447
+ type: Injectable
448
+ }] });
449
+
450
+ /**
451
+ * Email module routes
452
+ */
453
+ const EMAIL_ROUTES = [
454
+ {
455
+ path: '',
456
+ loadComponent: () => Promise.resolve().then(function () { return emailContainer_component; }).then((m) => m.EmailContainerComponent),
457
+ children: [
458
+ // Templates
459
+ {
460
+ path: 'templates',
461
+ children: [
462
+ {
463
+ path: '',
464
+ loadComponent: () => import('./flusys-ng-email-template-list.component-91djzx28.mjs').then((m) => m.TemplateListComponent),
465
+ },
466
+ {
467
+ path: 'new',
468
+ loadComponent: () => import('./flusys-ng-email-template-form.component-u40UX6yY.mjs').then((m) => m.TemplateFormComponent),
469
+ },
470
+ {
471
+ path: ':id',
472
+ loadComponent: () => import('./flusys-ng-email-template-form.component-u40UX6yY.mjs').then((m) => m.TemplateFormComponent),
473
+ },
474
+ ],
475
+ },
476
+ // Configs
477
+ {
478
+ path: 'configs',
479
+ children: [
480
+ {
481
+ path: '',
482
+ loadComponent: () => import('./flusys-ng-email-email-config-list.component-DnIJdHdf.mjs').then((m) => m.EmailConfigListComponent),
483
+ },
484
+ {
485
+ path: 'new',
486
+ loadComponent: () => import('./flusys-ng-email-email-config-form.component-BK195yNS.mjs').then((m) => m.EmailConfigFormComponent),
487
+ },
488
+ {
489
+ path: ':id',
490
+ loadComponent: () => import('./flusys-ng-email-email-config-form.component-BK195yNS.mjs').then((m) => m.EmailConfigFormComponent),
491
+ },
492
+ ],
493
+ },
494
+ // Default redirect
495
+ {
496
+ path: '',
497
+ redirectTo: 'templates',
498
+ pathMatch: 'full',
499
+ },
500
+ ],
501
+ },
502
+ ];
503
+
504
+ /**
505
+ * Email container component with tab navigation
506
+ */
507
+ class EmailContainerComponent {
508
+ tabs = [
509
+ { id: 'templates', label: 'Templates', icon: 'pi pi-file', route: 'templates' },
510
+ { id: 'configs', label: 'Configurations', icon: 'pi pi-cog', route: 'configs' },
511
+ ];
512
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
513
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: EmailContainerComponent, isStandalone: true, selector: "lib-email-container", ngImport: i0, template: `
514
+ <div class="p-2 sm:p-4">
515
+ <div class="mb-4">
516
+ <h1 class="text-xl sm:text-2xl font-bold m-0">Email</h1>
517
+ <p class="text-sm text-muted-color mt-1">Manage email templates and provider configurations</p>
518
+ </div>
519
+
520
+ <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap -mx-2 sm:mx-0 px-2 sm:px-0">
521
+ @for (tab of tabs; track tab.id) {
522
+ <a
523
+ [routerLink]="tab.route"
524
+ routerLinkActive="tab-active"
525
+ [routerLinkActiveOptions]="{ exact: false }"
526
+ class="flex items-center gap-2 px-3 sm:px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0 text-sm sm:text-base">
527
+ <i [class]="tab.icon"></i>
528
+ <span>{{ tab.label }}</span>
529
+ </a>
530
+ }
531
+ </div>
532
+
533
+ <router-outlet />
534
+ </div>
535
+ `, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
536
+ }
537
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailContainerComponent, decorators: [{
538
+ type: Component,
539
+ args: [{ selector: 'lib-email-container', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AngularModule], template: `
540
+ <div class="p-2 sm:p-4">
541
+ <div class="mb-4">
542
+ <h1 class="text-xl sm:text-2xl font-bold m-0">Email</h1>
543
+ <p class="text-sm text-muted-color mt-1">Manage email templates and provider configurations</p>
544
+ </div>
545
+
546
+ <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap -mx-2 sm:mx-0 px-2 sm:px-0">
547
+ @for (tab of tabs; track tab.id) {
548
+ <a
549
+ [routerLink]="tab.route"
550
+ routerLinkActive="tab-active"
551
+ [routerLinkActiveOptions]="{ exact: false }"
552
+ class="flex items-center gap-2 px-3 sm:px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0 text-sm sm:text-base">
553
+ <i [class]="tab.icon"></i>
554
+ <span>{{ tab.label }}</span>
555
+ </a>
556
+ }
557
+ </div>
558
+
559
+ <router-outlet />
560
+ </div>
561
+ `, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
562
+ }] });
563
+
564
+ var emailContainer_component = /*#__PURE__*/Object.freeze({
565
+ __proto__: null,
566
+ EmailContainerComponent: EmailContainerComponent
567
+ });
568
+
569
+ // =============================================================================
570
+ // @flusys/ng-email - Email Package
571
+ // =============================================================================
572
+ // Enums
573
+
574
+ /**
575
+ * Generated bundle index. Do not edit.
576
+ */
577
+
578
+ export { DEFAULT_EMAIL_SETTINGS, EMAIL_ROUTES, EmailBlockType, EmailBuilderStateService, EmailConfigApiService, EmailContainerComponent, EmailProviderEnum, EmailSendService, EmailTemplateApiService, createEmailSchema };
579
+ //# sourceMappingURL=flusys-ng-email.mjs.map