@senior-gestao-relacionamento/angular-components 2.4.0 → 2.5.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,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { NgModule, Injectable, Pipe, inject, NgZone, Component, ViewChild, EventEmitter, Input, Output } from '@angular/core';
2
+ import { NgModule, Injectable, Pipe, inject, NgZone, Component, ViewChild, EventEmitter, Input, Output, input, output, signal, computed } from '@angular/core';
3
3
  import * as i1$2 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
- import { BehaviorSubject, of, tap, Subject, debounceTime, switchMap } from 'rxjs';
5
+ import { BehaviorSubject, of, tap, Subject, debounceTime, switchMap, firstValueFrom } from 'rxjs';
6
6
  import * as i1 from '@angular/common/http';
7
7
  import { HttpParams } from '@angular/common/http';
8
8
  import * as i1$3 from '@angular/forms';
@@ -38,6 +38,10 @@ import { TableModule } from 'primeng/table';
38
38
  import * as i16 from 'primeng/autocomplete';
39
39
  import { AutoCompleteModule } from 'primeng/autocomplete';
40
40
  import * as i8 from 'primeng/api';
41
+ import * as i6 from 'primeng/radiobutton';
42
+ import { RadioButtonModule } from 'primeng/radiobutton';
43
+ import * as i7$1 from 'primeng/checkbox';
44
+ import { CheckboxModule } from 'primeng/checkbox';
41
45
 
42
46
  /**
43
47
  * Módulo principal da biblioteca @senior-gestao-relacionamento/angular-components
@@ -2023,6 +2027,655 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2023
2027
  type: Output
2024
2028
  }] } });
2025
2029
 
2030
+ /**
2031
+ * Serviço que dispara os signals de exportação no `crmx-data-export-backend`.
2032
+ *
2033
+ * Os signals são fire-and-forget: o backend gera o arquivo, faz upload
2034
+ * e notifica o usuário via WebSocket/notificação.
2035
+ */
2036
+ class ExportDataService {
2037
+ http;
2038
+ signalsUrl = '/crmx/data_export/signals';
2039
+ constructor(http) {
2040
+ this.http = http;
2041
+ }
2042
+ /**
2043
+ * Dispara `exportSelectedRecordsSignal` — exporta apenas os IDs informados.
2044
+ */
2045
+ async exportSelectedRecords(payload) {
2046
+ await firstValueFrom(this.http.post(`${this.signalsUrl}/exportSelectedRecordsSignal`, payload));
2047
+ }
2048
+ /**
2049
+ * Dispara `exportAllRecordsSignal` — exporta todos os registros com filtro.
2050
+ */
2051
+ async exportAllRecords(payload) {
2052
+ await firstValueFrom(this.http.post(`${this.signalsUrl}/exportAllRecordsSignal`, payload));
2053
+ }
2054
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
2055
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataService, providedIn: 'root' });
2056
+ }
2057
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataService, decorators: [{
2058
+ type: Injectable,
2059
+ args: [{ providedIn: 'root' }]
2060
+ }], ctorParameters: () => [{ type: i1.HttpClient }] });
2061
+
2062
+ var ExportFileType;
2063
+ (function (ExportFileType) {
2064
+ ExportFileType["Excel"] = "EXCEL";
2065
+ ExportFileType["Pdf"] = "PDF";
2066
+ })(ExportFileType || (ExportFileType = {}));
2067
+ var ExportOrientation;
2068
+ (function (ExportOrientation) {
2069
+ ExportOrientation["Portrait"] = "PORTRAIT";
2070
+ ExportOrientation["Landscape"] = "LANDSCAPE";
2071
+ })(ExportOrientation || (ExportOrientation = {}));
2072
+ var ExportFileFormat;
2073
+ (function (ExportFileFormat) {
2074
+ ExportFileFormat["A3"] = "A3";
2075
+ ExportFileFormat["A4"] = "A4";
2076
+ })(ExportFileFormat || (ExportFileFormat = {}));
2077
+ var ExportColumnType;
2078
+ (function (ExportColumnType) {
2079
+ ExportColumnType["Text"] = "TEXT";
2080
+ ExportColumnType["Enum"] = "ENUM";
2081
+ ExportColumnType["Date"] = "DATE";
2082
+ ExportColumnType["Datetime"] = "DATETIME";
2083
+ ExportColumnType["Currency"] = "CURRENCY";
2084
+ ExportColumnType["Number"] = "NUMBER";
2085
+ })(ExportColumnType || (ExportColumnType = {}));
2086
+
2087
+ class ExportDataComponent {
2088
+ exportService = inject(ExportDataService);
2089
+ webSocketService = inject(WebSocketService);
2090
+ ngZone = inject(NgZone);
2091
+ wsSubscription = null;
2092
+ wsConfig = {
2093
+ domain: 'crmx',
2094
+ service: 'dataexport',
2095
+ primitive: 'wsExportCompleted',
2096
+ userScoped: true
2097
+ };
2098
+ visible = input(false);
2099
+ visibleChange = output();
2100
+ config = input.required();
2101
+ selectedIds = input([]);
2102
+ exported = output();
2103
+ exportError = output();
2104
+ selectedFileType = signal(ExportFileType.Excel);
2105
+ selectedOrientation = signal(ExportOrientation.Landscape);
2106
+ selectedFileFormat = signal(ExportFileFormat.A4);
2107
+ exportScope = signal('all');
2108
+ exporting = signal(false);
2109
+ columnSelections = signal([]);
2110
+ get selectedFileTypeModel() { return this.selectedFileType(); }
2111
+ set selectedFileTypeModel(v) { this.selectedFileType.set(v); }
2112
+ get selectedOrientationModel() { return this.selectedOrientation(); }
2113
+ set selectedOrientationModel(v) { this.selectedOrientation.set(v); }
2114
+ get selectedFileFormatModel() { return this.selectedFileFormat(); }
2115
+ set selectedFileFormatModel(v) { this.selectedFileFormat.set(v); }
2116
+ get exportScopeModel() { return this.exportScope(); }
2117
+ set exportScopeModel(v) { this.exportScope.set(v); }
2118
+ selectedColumnsCount = computed(() => this.columnSelections().filter(c => c.selected).length);
2119
+ hasSelectedIds = computed(() => this.selectedIds().length > 0);
2120
+ canExport = computed(() => this.selectedColumnsCount() > 0 && !this.exporting());
2121
+ fileTypes = [
2122
+ { value: ExportFileType.Excel, label: 'Excel', icon: 'pi-file-excel', description: 'Planilha com colunas ajustáveis' },
2123
+ { value: ExportFileType.Pdf, label: 'PDF', icon: 'pi-file-pdf', description: 'Documento formatado para impressão' }
2124
+ ];
2125
+ orientations = [
2126
+ { value: ExportOrientation.Landscape, label: 'Paisagem (Horizontal)', icon: 'pi-arrows-h' },
2127
+ { value: ExportOrientation.Portrait, label: 'Retrato (Vertical)', icon: 'pi-arrows-v' }
2128
+ ];
2129
+ fileFormats = [
2130
+ { value: ExportFileFormat.A4, label: 'A4', description: 'Padrão (210×297mm)' },
2131
+ { value: ExportFileFormat.A3, label: 'A3', description: 'Grande (297×420mm)' }
2132
+ ];
2133
+ ngOnChanges() {
2134
+ const cfg = this.config();
2135
+ if (cfg?.columns) {
2136
+ this.columnSelections.set(cfg.columns.map(col => ({ column: this.normalizeColumn(col), selected: true })));
2137
+ }
2138
+ // Sincroniza escopo com presença de IDs
2139
+ this.exportScope.set(this.selectedIds().length > 0 ? 'selected' : 'all');
2140
+ // Subscribe ao WebSocket quando o dialog fica visível
2141
+ if (this.visible() && !this.wsSubscription) {
2142
+ this.subscribeToWebSocket();
2143
+ }
2144
+ }
2145
+ ngOnDestroy() {
2146
+ this.wsSubscription?.unsubscribe();
2147
+ this.webSocketService.unsubscribe(this.wsConfig);
2148
+ this.webSocketService.disconnect();
2149
+ }
2150
+ subscribeToWebSocket() {
2151
+ this.wsSubscription = this.webSocketService
2152
+ .subscribe(this.wsConfig)
2153
+ .subscribe(msg => {
2154
+ this.ngZone.run(() => {
2155
+ const data = msg.body.data;
2156
+ if (data?.success) {
2157
+ this.exported.emit();
2158
+ this.visibleChange.emit(false);
2159
+ }
2160
+ else {
2161
+ this.exportError.emit(data?.entityName ?? '');
2162
+ }
2163
+ this.exporting.set(false);
2164
+ });
2165
+ });
2166
+ }
2167
+ isExportColumn(col) {
2168
+ return 'columnName' in col && 'translateKey' in col && 'columnType' in col;
2169
+ }
2170
+ normalizeColumn(col) {
2171
+ if (this.isExportColumn(col)) {
2172
+ return col;
2173
+ }
2174
+ const input = col;
2175
+ const typeMap = {
2176
+ 'TEXT': ExportColumnType.Text, 'NUMBER': ExportColumnType.Number,
2177
+ 'ENUM': ExportColumnType.Enum, 'DATE': ExportColumnType.Date,
2178
+ 'DATETIME': ExportColumnType.Datetime, 'CURRENCY': ExportColumnType.Currency,
2179
+ 'status': ExportColumnType.Enum, 'number': ExportColumnType.Number, 'text': ExportColumnType.Text
2180
+ };
2181
+ return {
2182
+ columnName: input.field,
2183
+ translateKey: input.header,
2184
+ columnType: typeMap[input.exportType ?? 'TEXT'] ?? ExportColumnType.Text,
2185
+ translationKeyPrefix: input.translationKeyPrefix
2186
+ };
2187
+ }
2188
+ selectAllColumns() {
2189
+ this.columnSelections.update(cols => cols.map(c => ({ ...c, selected: true })));
2190
+ }
2191
+ deselectAllColumns() {
2192
+ this.columnSelections.update(cols => cols.map(c => ({ ...c, selected: false })));
2193
+ }
2194
+ toggleColumn(col) {
2195
+ col.selected = !col.selected;
2196
+ this.refreshColumns();
2197
+ }
2198
+ refreshColumns() {
2199
+ this.columnSelections.update(cols => [...cols]);
2200
+ }
2201
+ onDialogHide() {
2202
+ this.visibleChange.emit(false);
2203
+ }
2204
+ async doExport() {
2205
+ this.exporting.set(true);
2206
+ const cfg = this.config();
2207
+ const selectedCols = this.columnSelections().filter(c => c.selected).map(c => c.column);
2208
+ try {
2209
+ await this.exportViaBackend(cfg, selectedCols);
2210
+ // O dialog permanece aberto com loading até o WebSocket notificar o resultado
2211
+ }
2212
+ catch (e) {
2213
+ this.exporting.set(false);
2214
+ this.exportError.emit(cfg.entityName);
2215
+ }
2216
+ }
2217
+ async exportViaBackend(cfg, columns) {
2218
+ const fileType = this.selectedFileType();
2219
+ const orientation = this.selectedOrientation();
2220
+ const fileFormat = this.selectedFileFormat();
2221
+ if (this.hasSelectedIds() && this.exportScope() === 'selected') {
2222
+ await this.exportService.exportSelectedRecords({
2223
+ datasource: cfg.datasource, fields: columns,
2224
+ selectedIds: this.selectedIds(), entityName: cfg.entityName, fileType, orientation, fileFormat
2225
+ });
2226
+ }
2227
+ else {
2228
+ await this.exportService.exportAllRecords({
2229
+ datasource: cfg.datasource, fields: columns,
2230
+ filter: cfg.activeFilter ?? '', entityName: cfg.entityName, fileType, orientation, fileFormat
2231
+ });
2232
+ }
2233
+ }
2234
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2235
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ExportDataComponent, isStandalone: true, selector: "s-export-data", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, selectedIds: { classPropertyName: "selectedIds", publicName: "selectedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visibleChange: "visibleChange", exported: "exported", exportError: "exportError" }, usesOnChanges: true, ngImport: i0, template: `
2236
+ <p-dialog
2237
+ [visible]="visible()"
2238
+ (visibleChange)="visibleChange.emit($event)"
2239
+ (onHide)="onDialogHide()"
2240
+ [modal]="true"
2241
+ [draggable]="false"
2242
+ [resizable]="false"
2243
+ [style]="{ width: '700px' }"
2244
+ styleClass="s-export-dialog"
2245
+ >
2246
+ <ng-template pTemplate="header">
2247
+ <div class="custom-header">
2248
+ <i class="pi pi-download header-icon"></i>
2249
+ <span>Exportar Dados</span>
2250
+ </div>
2251
+ </ng-template>
2252
+
2253
+ <div class="export-content">
2254
+
2255
+ <!-- Formato de Exportação -->
2256
+ <div class="export-section">
2257
+ <div class="section-header">
2258
+ <div class="section-icon"><i class="pi pi-file"></i></div>
2259
+ <h3>Formato do arquivo</h3>
2260
+ </div>
2261
+ <div class="format-options">
2262
+ <div
2263
+ *ngFor="let fmt of fileTypes"
2264
+ class="format-card"
2265
+ [class.selected]="selectedFileType() === fmt.value"
2266
+ (click)="selectedFileType.set(fmt.value)"
2267
+ >
2268
+ <div class="card-radio">
2269
+ <p-radioButton
2270
+ [value]="fmt.value"
2271
+ [(ngModel)]="selectedFileTypeModel"
2272
+ [inputId]="'fmt_' + fmt.value"
2273
+ ></p-radioButton>
2274
+ </div>
2275
+ <div class="card-icon" [class.excel]="fmt.value === 'EXCEL'" [class.pdf]="fmt.value === 'PDF'">
2276
+ <i class="pi" [ngClass]="fmt.icon"></i>
2277
+ </div>
2278
+ <div class="card-info">
2279
+ <label [for]="'fmt_' + fmt.value" class="card-label">{{ fmt.label }}</label>
2280
+ <span class="card-description">{{ fmt.description }}</span>
2281
+ </div>
2282
+ </div>
2283
+ </div>
2284
+ </div>
2285
+
2286
+ <!-- Orientação + Formato da folha (apenas PDF) -->
2287
+ @if (selectedFileType() === 'PDF') {
2288
+ <div class="export-section">
2289
+ <div class="section-header">
2290
+ <div class="section-icon"><i class="pi pi-arrows-alt"></i></div>
2291
+ <h3>Orientação da página</h3>
2292
+ </div>
2293
+ <div class="format-options">
2294
+ <div
2295
+ *ngFor="let o of orientations"
2296
+ class="format-card"
2297
+ [class.selected]="selectedOrientation() === o.value"
2298
+ (click)="selectedOrientation.set(o.value)"
2299
+ >
2300
+ <div class="card-radio">
2301
+ <p-radioButton
2302
+ [value]="o.value"
2303
+ [(ngModel)]="selectedOrientationModel"
2304
+ [inputId]="'orient_' + o.value"
2305
+ ></p-radioButton>
2306
+ </div>
2307
+ <div class="card-icon">
2308
+ <i class="pi" [ngClass]="o.icon"></i>
2309
+ </div>
2310
+ <div class="card-info">
2311
+ <label [for]="'orient_' + o.value" class="card-label">{{ o.label }}</label>
2312
+ </div>
2313
+ </div>
2314
+ </div>
2315
+ </div>
2316
+
2317
+ <div class="export-section">
2318
+ <div class="section-header">
2319
+ <div class="section-icon"><i class="pi pi-stop"></i></div>
2320
+ <h3>Formato da folha</h3>
2321
+ </div>
2322
+ <div class="format-options">
2323
+ <div
2324
+ *ngFor="let f of fileFormats"
2325
+ class="format-card"
2326
+ [class.selected]="selectedFileFormat() === f.value"
2327
+ (click)="selectedFileFormat.set(f.value)"
2328
+ >
2329
+ <div class="card-radio">
2330
+ <p-radioButton
2331
+ [value]="f.value"
2332
+ [(ngModel)]="selectedFileFormatModel"
2333
+ [inputId]="'format_' + f.value"
2334
+ ></p-radioButton>
2335
+ </div>
2336
+ <div class="card-icon">
2337
+ <i class="pi pi-file"></i>
2338
+ </div>
2339
+ <div class="card-info">
2340
+ <label [for]="'format_' + f.value" class="card-label">{{ f.label }}</label>
2341
+ <span class="card-description">{{ f.description }}</span>
2342
+ </div>
2343
+ </div>
2344
+ </div>
2345
+ </div>
2346
+ }
2347
+
2348
+ <!-- Escopo -->
2349
+ <div class="export-section">
2350
+ <div class="section-header">
2351
+ <div class="section-icon"><i class="pi pi-filter"></i></div>
2352
+ <h3>Escopo da exportação</h3>
2353
+ </div>
2354
+ <div class="scope-options">
2355
+ <div
2356
+ class="scope-card"
2357
+ [class.selected]="!hasSelectedIds()"
2358
+ (click)="exportScope.set('all')"
2359
+ >
2360
+ <div class="card-radio">
2361
+ <p-radioButton value="all" [(ngModel)]="exportScopeModel" inputId="scope_all"></p-radioButton>
2362
+ </div>
2363
+ <div class="card-icon"><i class="pi pi-list"></i></div>
2364
+ <div class="card-info">
2365
+ <label for="scope_all" class="card-label">Todos os registros</label>
2366
+ <span class="card-description">Exporta todos com o filtro ativo</span>
2367
+ </div>
2368
+ </div>
2369
+ <div
2370
+ class="scope-card"
2371
+ [class.selected]="hasSelectedIds()"
2372
+ [class.disabled]="!hasSelectedIds()"
2373
+ (click)="hasSelectedIds() ? exportScope.set('selected') : null"
2374
+ >
2375
+ <div class="card-radio">
2376
+ <p-radioButton
2377
+ value="selected"
2378
+ [(ngModel)]="exportScopeModel"
2379
+ inputId="scope_selected"
2380
+ [disabled]="!hasSelectedIds()"
2381
+ ></p-radioButton>
2382
+ </div>
2383
+ <div class="card-icon"><i class="pi pi-check-square"></i></div>
2384
+ <div class="card-info">
2385
+ <label for="scope_selected" class="card-label">Registros selecionados</label>
2386
+ <span class="card-description">Exporta apenas os itens marcados</span>
2387
+ <span class="card-count">
2388
+ <i class="pi pi-circle-fill"></i>
2389
+ {{ selectedIds().length }} registro(s) selecionado(s)
2390
+ </span>
2391
+ </div>
2392
+ </div>
2393
+ </div>
2394
+ </div>
2395
+
2396
+ <!-- Seleção de Colunas -->
2397
+ <div class="export-section">
2398
+ <div class="section-header">
2399
+ <div class="section-icon"><i class="pi pi-table"></i></div>
2400
+ <h3>Colunas</h3>
2401
+ <div class="column-actions">
2402
+ <button type="button" class="action-link" (click)="selectAllColumns()">Todas</button>
2403
+ <span class="separator">|</span>
2404
+ <button type="button" class="action-link" (click)="deselectAllColumns()">Nenhuma</button>
2405
+ </div>
2406
+ </div>
2407
+ <div class="columns-grid">
2408
+ <div
2409
+ *ngFor="let col of columnSelections()"
2410
+ class="column-item"
2411
+ [class.selected]="col.selected"
2412
+ (click)="toggleColumn(col)"
2413
+ >
2414
+ <p-checkbox
2415
+ [(ngModel)]="col.selected"
2416
+ [binary]="true"
2417
+ [inputId]="'col_' + col.column.columnName"
2418
+ (onChange)="refreshColumns()"
2419
+ ></p-checkbox>
2420
+ <label [for]="'col_' + col.column.columnName" class="column-label">
2421
+ {{ col.column.translateKey }}
2422
+ </label>
2423
+ </div>
2424
+ </div>
2425
+ <div class="columns-summary">
2426
+ <i class="pi pi-info-circle"></i>
2427
+ <span>{{ selectedColumnsCount() }} de {{ columnSelections().length }} colunas selecionadas</span>
2428
+ </div>
2429
+ </div>
2430
+
2431
+ </div>
2432
+
2433
+ <ng-template pTemplate="footer">
2434
+ <p-button
2435
+ label="Cancelar"
2436
+ icon="pi pi-times"
2437
+ severity="secondary"
2438
+ [text]="true"
2439
+ (onClick)="onDialogHide()"
2440
+ />
2441
+ <p-button
2442
+ label="Exportar"
2443
+ icon="pi pi-download"
2444
+ [disabled]="!canExport()"
2445
+ [loading]="exporting()"
2446
+ (onClick)="doExport()"
2447
+ />
2448
+ </ng-template>
2449
+ </p-dialog>
2450
+ `, isInline: true, styles: ["::ng-deep .s-export-dialog .p-dialog{border-radius:16px;overflow:hidden;box-shadow:0 20px 60px #0000004d}::ng-deep .s-export-dialog .p-dialog-content{padding:0;background:#fff;max-height:70vh;overflow-y:auto}::ng-deep .s-export-dialog .p-dialog-footer{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:16px 32px}.custom-header{display:flex;align-items:center;gap:12px;font-size:18px;font-weight:600}.custom-header .header-icon{font-size:22px;color:var(--p-primary-500)}.export-content{padding:28px 32px}.export-section{margin-bottom:32px}.export-section:last-child{margin-bottom:0}.section-header{display:flex;align-items:center;gap:12px;margin-bottom:20px}.section-icon{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.1),rgba(var(--p-primary-rgb),.15));border-radius:10px}.section-icon i{font-size:18px;color:var(--p-primary-500)}.section-header h3{margin:0;font-size:17px;font-weight:700;color:var(--neutral-color-800, #1f2937);flex:1}.column-actions{display:flex;align-items:center;gap:10px;font-size:13px}.action-link{background:none;border:none;color:var(--p-primary-500);cursor:pointer;font-weight:600;padding:0;transition:all .2s;font-size:13px}.separator{color:var(--neutral-color-300, #d1d5db)}.format-options{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}.format-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.format-card:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.03),rgba(var(--p-primary-rgb),.06));opacity:0;transition:opacity .3s}.format-card:hover{border-color:var(--p-primary-500);transform:translateY(-2px);box-shadow:0 8px 24px rgba(var(--p-primary-rgb),.15)}.format-card:hover:before{opacity:1}.format-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.format-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.format-card.selected .card-icon i{color:#fff}.card-icon{width:52px;height:52px;display:flex;align-items:center;justify-content:center;border-radius:12px;transition:all .3s;position:relative;z-index:1;background:linear-gradient(135deg,var(--p-primary-50, #f0fdf4) 0%,var(--p-primary-100, #dcfce7) 100%)}.card-icon.excel{background:linear-gradient(135deg,var(--p-primary-50, #f0fdf4) 0%,var(--p-primary-100, #dcfce7) 100%)}.card-icon.excel i{color:#107c41;font-size:26px}.card-icon.pdf{background:linear-gradient(135deg,#fee8e8,#fdd4d4)}.card-icon.pdf i{color:#dc2626;font-size:26px}.card-icon i{font-size:22px;color:var(--p-primary-500)}.card-info{flex:1;display:flex;flex-direction:column;gap:4px;position:relative;z-index:1}.card-label{font-size:16px;font-weight:700;color:var(--neutral-color-800, #1f2937);cursor:pointer;margin:0}.card-description{font-size:13px;color:var(--neutral-color-600, #4b5563);line-height:1.4}.scope-options{display:flex;flex-direction:column;gap:14px}.scope-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.scope-card:hover:not(.disabled){border-color:var(--p-primary-500);box-shadow:0 4px 16px rgba(var(--p-primary-rgb),.12)}.scope-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.scope-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.scope-card.selected .card-icon i{color:#fff}.scope-card.disabled{opacity:.5;cursor:not-allowed;background:#f9fafb}.card-count{font-size:13px;font-weight:600;color:var(--p-primary-500);margin-top:6px;display:inline-flex;align-items:center;gap:6px}.card-count i{font-size:6px}.columns-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}.column-item{display:flex;align-items:center;gap:12px;padding:16px;border:2px solid #e5e7eb;border-radius:10px;transition:all .2s;background:#fff;cursor:pointer}.column-item:hover{border-color:var(--p-primary-500);background:rgba(var(--p-primary-rgb),.03)}.column-item.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08))}.column-label{font-size:14px;font-weight:600;color:var(--neutral-color-800, #1f2937);cursor:pointer;margin:0;-webkit-user-select:none;user-select:none}.columns-summary{display:flex;align-items:center;gap:10px;padding:14px 18px;background:linear-gradient(135deg,var(--p-primary-50, #f0fdf4) 0%,var(--p-primary-100, #dcfce7) 100%);border-radius:10px;font-size:14px;color:var(--neutral-color-700, #374151);font-weight:600}.columns-summary i{color:var(--p-primary-500);font-size:18px}@media (max-width: 768px){::ng-deep .s-export-dialog .p-dialog{width:95vw!important;margin:0}.export-content{padding:24px}.format-options,.columns-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i4.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i8.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i3.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i6.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i7$1.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }] });
2451
+ }
2452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataComponent, decorators: [{
2453
+ type: Component,
2454
+ args: [{ selector: 's-export-data', standalone: true, imports: [
2455
+ CommonModule,
2456
+ FormsModule,
2457
+ DialogModule,
2458
+ ButtonModule,
2459
+ RadioButtonModule,
2460
+ CheckboxModule
2461
+ ], template: `
2462
+ <p-dialog
2463
+ [visible]="visible()"
2464
+ (visibleChange)="visibleChange.emit($event)"
2465
+ (onHide)="onDialogHide()"
2466
+ [modal]="true"
2467
+ [draggable]="false"
2468
+ [resizable]="false"
2469
+ [style]="{ width: '700px' }"
2470
+ styleClass="s-export-dialog"
2471
+ >
2472
+ <ng-template pTemplate="header">
2473
+ <div class="custom-header">
2474
+ <i class="pi pi-download header-icon"></i>
2475
+ <span>Exportar Dados</span>
2476
+ </div>
2477
+ </ng-template>
2478
+
2479
+ <div class="export-content">
2480
+
2481
+ <!-- Formato de Exportação -->
2482
+ <div class="export-section">
2483
+ <div class="section-header">
2484
+ <div class="section-icon"><i class="pi pi-file"></i></div>
2485
+ <h3>Formato do arquivo</h3>
2486
+ </div>
2487
+ <div class="format-options">
2488
+ <div
2489
+ *ngFor="let fmt of fileTypes"
2490
+ class="format-card"
2491
+ [class.selected]="selectedFileType() === fmt.value"
2492
+ (click)="selectedFileType.set(fmt.value)"
2493
+ >
2494
+ <div class="card-radio">
2495
+ <p-radioButton
2496
+ [value]="fmt.value"
2497
+ [(ngModel)]="selectedFileTypeModel"
2498
+ [inputId]="'fmt_' + fmt.value"
2499
+ ></p-radioButton>
2500
+ </div>
2501
+ <div class="card-icon" [class.excel]="fmt.value === 'EXCEL'" [class.pdf]="fmt.value === 'PDF'">
2502
+ <i class="pi" [ngClass]="fmt.icon"></i>
2503
+ </div>
2504
+ <div class="card-info">
2505
+ <label [for]="'fmt_' + fmt.value" class="card-label">{{ fmt.label }}</label>
2506
+ <span class="card-description">{{ fmt.description }}</span>
2507
+ </div>
2508
+ </div>
2509
+ </div>
2510
+ </div>
2511
+
2512
+ <!-- Orientação + Formato da folha (apenas PDF) -->
2513
+ @if (selectedFileType() === 'PDF') {
2514
+ <div class="export-section">
2515
+ <div class="section-header">
2516
+ <div class="section-icon"><i class="pi pi-arrows-alt"></i></div>
2517
+ <h3>Orientação da página</h3>
2518
+ </div>
2519
+ <div class="format-options">
2520
+ <div
2521
+ *ngFor="let o of orientations"
2522
+ class="format-card"
2523
+ [class.selected]="selectedOrientation() === o.value"
2524
+ (click)="selectedOrientation.set(o.value)"
2525
+ >
2526
+ <div class="card-radio">
2527
+ <p-radioButton
2528
+ [value]="o.value"
2529
+ [(ngModel)]="selectedOrientationModel"
2530
+ [inputId]="'orient_' + o.value"
2531
+ ></p-radioButton>
2532
+ </div>
2533
+ <div class="card-icon">
2534
+ <i class="pi" [ngClass]="o.icon"></i>
2535
+ </div>
2536
+ <div class="card-info">
2537
+ <label [for]="'orient_' + o.value" class="card-label">{{ o.label }}</label>
2538
+ </div>
2539
+ </div>
2540
+ </div>
2541
+ </div>
2542
+
2543
+ <div class="export-section">
2544
+ <div class="section-header">
2545
+ <div class="section-icon"><i class="pi pi-stop"></i></div>
2546
+ <h3>Formato da folha</h3>
2547
+ </div>
2548
+ <div class="format-options">
2549
+ <div
2550
+ *ngFor="let f of fileFormats"
2551
+ class="format-card"
2552
+ [class.selected]="selectedFileFormat() === f.value"
2553
+ (click)="selectedFileFormat.set(f.value)"
2554
+ >
2555
+ <div class="card-radio">
2556
+ <p-radioButton
2557
+ [value]="f.value"
2558
+ [(ngModel)]="selectedFileFormatModel"
2559
+ [inputId]="'format_' + f.value"
2560
+ ></p-radioButton>
2561
+ </div>
2562
+ <div class="card-icon">
2563
+ <i class="pi pi-file"></i>
2564
+ </div>
2565
+ <div class="card-info">
2566
+ <label [for]="'format_' + f.value" class="card-label">{{ f.label }}</label>
2567
+ <span class="card-description">{{ f.description }}</span>
2568
+ </div>
2569
+ </div>
2570
+ </div>
2571
+ </div>
2572
+ }
2573
+
2574
+ <!-- Escopo -->
2575
+ <div class="export-section">
2576
+ <div class="section-header">
2577
+ <div class="section-icon"><i class="pi pi-filter"></i></div>
2578
+ <h3>Escopo da exportação</h3>
2579
+ </div>
2580
+ <div class="scope-options">
2581
+ <div
2582
+ class="scope-card"
2583
+ [class.selected]="!hasSelectedIds()"
2584
+ (click)="exportScope.set('all')"
2585
+ >
2586
+ <div class="card-radio">
2587
+ <p-radioButton value="all" [(ngModel)]="exportScopeModel" inputId="scope_all"></p-radioButton>
2588
+ </div>
2589
+ <div class="card-icon"><i class="pi pi-list"></i></div>
2590
+ <div class="card-info">
2591
+ <label for="scope_all" class="card-label">Todos os registros</label>
2592
+ <span class="card-description">Exporta todos com o filtro ativo</span>
2593
+ </div>
2594
+ </div>
2595
+ <div
2596
+ class="scope-card"
2597
+ [class.selected]="hasSelectedIds()"
2598
+ [class.disabled]="!hasSelectedIds()"
2599
+ (click)="hasSelectedIds() ? exportScope.set('selected') : null"
2600
+ >
2601
+ <div class="card-radio">
2602
+ <p-radioButton
2603
+ value="selected"
2604
+ [(ngModel)]="exportScopeModel"
2605
+ inputId="scope_selected"
2606
+ [disabled]="!hasSelectedIds()"
2607
+ ></p-radioButton>
2608
+ </div>
2609
+ <div class="card-icon"><i class="pi pi-check-square"></i></div>
2610
+ <div class="card-info">
2611
+ <label for="scope_selected" class="card-label">Registros selecionados</label>
2612
+ <span class="card-description">Exporta apenas os itens marcados</span>
2613
+ <span class="card-count">
2614
+ <i class="pi pi-circle-fill"></i>
2615
+ {{ selectedIds().length }} registro(s) selecionado(s)
2616
+ </span>
2617
+ </div>
2618
+ </div>
2619
+ </div>
2620
+ </div>
2621
+
2622
+ <!-- Seleção de Colunas -->
2623
+ <div class="export-section">
2624
+ <div class="section-header">
2625
+ <div class="section-icon"><i class="pi pi-table"></i></div>
2626
+ <h3>Colunas</h3>
2627
+ <div class="column-actions">
2628
+ <button type="button" class="action-link" (click)="selectAllColumns()">Todas</button>
2629
+ <span class="separator">|</span>
2630
+ <button type="button" class="action-link" (click)="deselectAllColumns()">Nenhuma</button>
2631
+ </div>
2632
+ </div>
2633
+ <div class="columns-grid">
2634
+ <div
2635
+ *ngFor="let col of columnSelections()"
2636
+ class="column-item"
2637
+ [class.selected]="col.selected"
2638
+ (click)="toggleColumn(col)"
2639
+ >
2640
+ <p-checkbox
2641
+ [(ngModel)]="col.selected"
2642
+ [binary]="true"
2643
+ [inputId]="'col_' + col.column.columnName"
2644
+ (onChange)="refreshColumns()"
2645
+ ></p-checkbox>
2646
+ <label [for]="'col_' + col.column.columnName" class="column-label">
2647
+ {{ col.column.translateKey }}
2648
+ </label>
2649
+ </div>
2650
+ </div>
2651
+ <div class="columns-summary">
2652
+ <i class="pi pi-info-circle"></i>
2653
+ <span>{{ selectedColumnsCount() }} de {{ columnSelections().length }} colunas selecionadas</span>
2654
+ </div>
2655
+ </div>
2656
+
2657
+ </div>
2658
+
2659
+ <ng-template pTemplate="footer">
2660
+ <p-button
2661
+ label="Cancelar"
2662
+ icon="pi pi-times"
2663
+ severity="secondary"
2664
+ [text]="true"
2665
+ (onClick)="onDialogHide()"
2666
+ />
2667
+ <p-button
2668
+ label="Exportar"
2669
+ icon="pi pi-download"
2670
+ [disabled]="!canExport()"
2671
+ [loading]="exporting()"
2672
+ (onClick)="doExport()"
2673
+ />
2674
+ </ng-template>
2675
+ </p-dialog>
2676
+ `, styles: ["::ng-deep .s-export-dialog .p-dialog{border-radius:16px;overflow:hidden;box-shadow:0 20px 60px #0000004d}::ng-deep .s-export-dialog .p-dialog-content{padding:0;background:#fff;max-height:70vh;overflow-y:auto}::ng-deep .s-export-dialog .p-dialog-footer{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding:16px 32px}.custom-header{display:flex;align-items:center;gap:12px;font-size:18px;font-weight:600}.custom-header .header-icon{font-size:22px;color:var(--p-primary-500)}.export-content{padding:28px 32px}.export-section{margin-bottom:32px}.export-section:last-child{margin-bottom:0}.section-header{display:flex;align-items:center;gap:12px;margin-bottom:20px}.section-icon{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.1),rgba(var(--p-primary-rgb),.15));border-radius:10px}.section-icon i{font-size:18px;color:var(--p-primary-500)}.section-header h3{margin:0;font-size:17px;font-weight:700;color:var(--neutral-color-800, #1f2937);flex:1}.column-actions{display:flex;align-items:center;gap:10px;font-size:13px}.action-link{background:none;border:none;color:var(--p-primary-500);cursor:pointer;font-weight:600;padding:0;transition:all .2s;font-size:13px}.separator{color:var(--neutral-color-300, #d1d5db)}.format-options{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}.format-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.format-card:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.03),rgba(var(--p-primary-rgb),.06));opacity:0;transition:opacity .3s}.format-card:hover{border-color:var(--p-primary-500);transform:translateY(-2px);box-shadow:0 8px 24px rgba(var(--p-primary-rgb),.15)}.format-card:hover:before{opacity:1}.format-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.format-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.format-card.selected .card-icon i{color:#fff}.card-icon{width:52px;height:52px;display:flex;align-items:center;justify-content:center;border-radius:12px;transition:all .3s;position:relative;z-index:1;background:linear-gradient(135deg,var(--p-primary-50, #f0fdf4) 0%,var(--p-primary-100, #dcfce7) 100%)}.card-icon.excel{background:linear-gradient(135deg,var(--p-primary-50, #f0fdf4) 0%,var(--p-primary-100, #dcfce7) 100%)}.card-icon.excel i{color:#107c41;font-size:26px}.card-icon.pdf{background:linear-gradient(135deg,#fee8e8,#fdd4d4)}.card-icon.pdf i{color:#dc2626;font-size:26px}.card-icon i{font-size:22px;color:var(--p-primary-500)}.card-info{flex:1;display:flex;flex-direction:column;gap:4px;position:relative;z-index:1}.card-label{font-size:16px;font-weight:700;color:var(--neutral-color-800, #1f2937);cursor:pointer;margin:0}.card-description{font-size:13px;color:var(--neutral-color-600, #4b5563);line-height:1.4}.scope-options{display:flex;flex-direction:column;gap:14px}.scope-card{display:flex;align-items:center;gap:16px;padding:20px;border:2px solid #e5e7eb;border-radius:12px;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);background:#fff;position:relative;overflow:hidden}.scope-card:hover:not(.disabled){border-color:var(--p-primary-500);box-shadow:0 4px 16px rgba(var(--p-primary-rgb),.12)}.scope-card.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08));box-shadow:0 0 0 4px rgba(var(--p-primary-rgb),.1)}.scope-card.selected .card-icon{background:linear-gradient(135deg,var(--p-primary-500) 0%,var(--p-primary-500) 100%);transform:scale(1.05)}.scope-card.selected .card-icon i{color:#fff}.scope-card.disabled{opacity:.5;cursor:not-allowed;background:#f9fafb}.card-count{font-size:13px;font-weight:600;color:var(--p-primary-500);margin-top:6px;display:inline-flex;align-items:center;gap:6px}.card-count i{font-size:6px}.columns-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:16px}.column-item{display:flex;align-items:center;gap:12px;padding:16px;border:2px solid #e5e7eb;border-radius:10px;transition:all .2s;background:#fff;cursor:pointer}.column-item:hover{border-color:var(--p-primary-500);background:rgba(var(--p-primary-rgb),.03)}.column-item.selected{border-color:var(--p-primary-500);background:linear-gradient(135deg,rgba(var(--p-primary-rgb),.05),rgba(var(--p-primary-rgb),.08))}.column-label{font-size:14px;font-weight:600;color:var(--neutral-color-800, #1f2937);cursor:pointer;margin:0;-webkit-user-select:none;user-select:none}.columns-summary{display:flex;align-items:center;gap:10px;padding:14px 18px;background:linear-gradient(135deg,var(--p-primary-50, #f0fdf4) 0%,var(--p-primary-100, #dcfce7) 100%);border-radius:10px;font-size:14px;color:var(--neutral-color-700, #374151);font-weight:600}.columns-summary i{color:var(--p-primary-500);font-size:18px}@media (max-width: 768px){::ng-deep .s-export-dialog .p-dialog{width:95vw!important;margin:0}.export-content{padding:24px}.format-options,.columns-grid{grid-template-columns:1fr}}\n"] }]
2677
+ }] });
2678
+
2026
2679
  /*
2027
2680
  * Public API Surface of @senior-gestao-relacionamento/angular-components
2028
2681
  */
@@ -2031,5 +2684,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2031
2684
  * Generated bundle index. Do not edit.
2032
2685
  */
2033
2686
 
2034
- export { AngularComponentsModule, ChatbotComponent, CurrentCollaboratorService, Month, OrdinalWeekDay, ParticipantConfirmation, RepeatWhen, ScheduleDetailComponent, ScheduleFormComponent, SchedulePriority, ScheduleRecurrence, ScheduleService, ScheduleTypeService, Status, StorageService, WeekDay, provideAngularComponentsTranslations };
2687
+ export { AngularComponentsModule, ChatbotComponent, CurrentCollaboratorService, ExportColumnType, ExportDataComponent, ExportDataService, ExportFileFormat, ExportFileType, ExportOrientation, Month, OrdinalWeekDay, ParticipantConfirmation, RepeatWhen, ScheduleDetailComponent, ScheduleFormComponent, SchedulePriority, ScheduleRecurrence, ScheduleService, ScheduleTypeService, Status, StorageService, WeekDay, provideAngularComponentsTranslations };
2035
2688
  //# sourceMappingURL=senior-gestao-relacionamento-angular-components.mjs.map