@senior-gestao-relacionamento/angular-components 2.4.0 → 2.5.0-master-5861fa25

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,661 @@ 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
+ let cfg;
2135
+ try {
2136
+ cfg = this.config();
2137
+ }
2138
+ catch {
2139
+ return; // config ainda não foi setado (required input)
2140
+ }
2141
+ if (cfg?.columns) {
2142
+ this.columnSelections.set(cfg.columns.map(col => ({ column: this.normalizeColumn(col), selected: true })));
2143
+ }
2144
+ // Sincroniza escopo com presença de IDs
2145
+ this.exportScope.set(this.selectedIds().length > 0 ? 'selected' : 'all');
2146
+ // Subscribe ao WebSocket quando o dialog fica visível
2147
+ if (this.visible() && !this.wsSubscription) {
2148
+ this.subscribeToWebSocket();
2149
+ }
2150
+ }
2151
+ ngOnDestroy() {
2152
+ this.wsSubscription?.unsubscribe();
2153
+ this.webSocketService.unsubscribe(this.wsConfig);
2154
+ this.webSocketService.disconnect();
2155
+ }
2156
+ subscribeToWebSocket() {
2157
+ this.wsSubscription = this.webSocketService
2158
+ .subscribe(this.wsConfig)
2159
+ .subscribe(msg => {
2160
+ this.ngZone.run(() => {
2161
+ const data = msg.body.data;
2162
+ if (data?.success) {
2163
+ this.exported.emit();
2164
+ this.visibleChange.emit(false);
2165
+ }
2166
+ else {
2167
+ this.exportError.emit(data?.entityName ?? '');
2168
+ }
2169
+ this.exporting.set(false);
2170
+ });
2171
+ });
2172
+ }
2173
+ isExportColumn(col) {
2174
+ return 'columnName' in col && 'translateKey' in col && 'columnType' in col;
2175
+ }
2176
+ normalizeColumn(col) {
2177
+ if (this.isExportColumn(col)) {
2178
+ return col;
2179
+ }
2180
+ const input = col;
2181
+ const typeMap = {
2182
+ 'TEXT': ExportColumnType.Text, 'NUMBER': ExportColumnType.Number,
2183
+ 'ENUM': ExportColumnType.Enum, 'DATE': ExportColumnType.Date,
2184
+ 'DATETIME': ExportColumnType.Datetime, 'CURRENCY': ExportColumnType.Currency,
2185
+ 'status': ExportColumnType.Enum, 'number': ExportColumnType.Number, 'text': ExportColumnType.Text
2186
+ };
2187
+ return {
2188
+ columnName: input.field,
2189
+ translateKey: input.header,
2190
+ columnType: typeMap[input.exportType ?? 'TEXT'] ?? ExportColumnType.Text,
2191
+ translationKeyPrefix: input.translationKeyPrefix
2192
+ };
2193
+ }
2194
+ selectAllColumns() {
2195
+ this.columnSelections.update(cols => cols.map(c => ({ ...c, selected: true })));
2196
+ }
2197
+ deselectAllColumns() {
2198
+ this.columnSelections.update(cols => cols.map(c => ({ ...c, selected: false })));
2199
+ }
2200
+ toggleColumn(col) {
2201
+ col.selected = !col.selected;
2202
+ this.refreshColumns();
2203
+ }
2204
+ refreshColumns() {
2205
+ this.columnSelections.update(cols => [...cols]);
2206
+ }
2207
+ onDialogHide() {
2208
+ this.visibleChange.emit(false);
2209
+ }
2210
+ async doExport() {
2211
+ this.exporting.set(true);
2212
+ const cfg = this.config();
2213
+ const selectedCols = this.columnSelections().filter(c => c.selected).map(c => c.column);
2214
+ try {
2215
+ await this.exportViaBackend(cfg, selectedCols);
2216
+ // O dialog permanece aberto com loading até o WebSocket notificar o resultado
2217
+ }
2218
+ catch (e) {
2219
+ this.exporting.set(false);
2220
+ this.exportError.emit(cfg.entityName);
2221
+ }
2222
+ }
2223
+ async exportViaBackend(cfg, columns) {
2224
+ const fileType = this.selectedFileType();
2225
+ const orientation = this.selectedOrientation();
2226
+ const fileFormat = this.selectedFileFormat();
2227
+ if (this.hasSelectedIds() && this.exportScope() === 'selected') {
2228
+ await this.exportService.exportSelectedRecords({
2229
+ datasource: cfg.datasource, fields: columns,
2230
+ selectedIds: this.selectedIds(), entityName: cfg.entityName, fileType, orientation, fileFormat
2231
+ });
2232
+ }
2233
+ else {
2234
+ await this.exportService.exportAllRecords({
2235
+ datasource: cfg.datasource, fields: columns,
2236
+ filter: cfg.activeFilter ?? '', entityName: cfg.entityName, fileType, orientation, fileFormat
2237
+ });
2238
+ }
2239
+ }
2240
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2241
+ 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: `
2242
+ <p-dialog
2243
+ [visible]="visible()"
2244
+ (visibleChange)="visibleChange.emit($event)"
2245
+ (onHide)="onDialogHide()"
2246
+ [modal]="true"
2247
+ [draggable]="false"
2248
+ [resizable]="false"
2249
+ [style]="{ width: '700px' }"
2250
+ styleClass="s-export-dialog"
2251
+ >
2252
+ <ng-template pTemplate="header">
2253
+ <div class="custom-header">
2254
+ <i class="pi pi-download header-icon"></i>
2255
+ <span>Exportar Dados</span>
2256
+ </div>
2257
+ </ng-template>
2258
+
2259
+ <div class="export-content">
2260
+
2261
+ <!-- Formato de Exportação -->
2262
+ <div class="export-section">
2263
+ <div class="section-header">
2264
+ <div class="section-icon"><i class="pi pi-file"></i></div>
2265
+ <h3>Formato do arquivo</h3>
2266
+ </div>
2267
+ <div class="format-options">
2268
+ <div
2269
+ *ngFor="let fmt of fileTypes"
2270
+ class="format-card"
2271
+ [class.selected]="selectedFileType() === fmt.value"
2272
+ (click)="selectedFileType.set(fmt.value)"
2273
+ >
2274
+ <div class="card-radio">
2275
+ <p-radioButton
2276
+ [value]="fmt.value"
2277
+ [(ngModel)]="selectedFileTypeModel"
2278
+ [inputId]="'fmt_' + fmt.value"
2279
+ ></p-radioButton>
2280
+ </div>
2281
+ <div class="card-icon" [class.excel]="fmt.value === 'EXCEL'" [class.pdf]="fmt.value === 'PDF'">
2282
+ <i class="pi" [ngClass]="fmt.icon"></i>
2283
+ </div>
2284
+ <div class="card-info">
2285
+ <label [for]="'fmt_' + fmt.value" class="card-label">{{ fmt.label }}</label>
2286
+ <span class="card-description">{{ fmt.description }}</span>
2287
+ </div>
2288
+ </div>
2289
+ </div>
2290
+ </div>
2291
+
2292
+ <!-- Orientação + Formato da folha (apenas PDF) -->
2293
+ @if (selectedFileType() === 'PDF') {
2294
+ <div class="export-section">
2295
+ <div class="section-header">
2296
+ <div class="section-icon"><i class="pi pi-arrows-alt"></i></div>
2297
+ <h3>Orientação da página</h3>
2298
+ </div>
2299
+ <div class="format-options">
2300
+ <div
2301
+ *ngFor="let o of orientations"
2302
+ class="format-card"
2303
+ [class.selected]="selectedOrientation() === o.value"
2304
+ (click)="selectedOrientation.set(o.value)"
2305
+ >
2306
+ <div class="card-radio">
2307
+ <p-radioButton
2308
+ [value]="o.value"
2309
+ [(ngModel)]="selectedOrientationModel"
2310
+ [inputId]="'orient_' + o.value"
2311
+ ></p-radioButton>
2312
+ </div>
2313
+ <div class="card-icon">
2314
+ <i class="pi" [ngClass]="o.icon"></i>
2315
+ </div>
2316
+ <div class="card-info">
2317
+ <label [for]="'orient_' + o.value" class="card-label">{{ o.label }}</label>
2318
+ </div>
2319
+ </div>
2320
+ </div>
2321
+ </div>
2322
+
2323
+ <div class="export-section">
2324
+ <div class="section-header">
2325
+ <div class="section-icon"><i class="pi pi-stop"></i></div>
2326
+ <h3>Formato da folha</h3>
2327
+ </div>
2328
+ <div class="format-options">
2329
+ <div
2330
+ *ngFor="let f of fileFormats"
2331
+ class="format-card"
2332
+ [class.selected]="selectedFileFormat() === f.value"
2333
+ (click)="selectedFileFormat.set(f.value)"
2334
+ >
2335
+ <div class="card-radio">
2336
+ <p-radioButton
2337
+ [value]="f.value"
2338
+ [(ngModel)]="selectedFileFormatModel"
2339
+ [inputId]="'format_' + f.value"
2340
+ ></p-radioButton>
2341
+ </div>
2342
+ <div class="card-icon">
2343
+ <i class="pi pi-file"></i>
2344
+ </div>
2345
+ <div class="card-info">
2346
+ <label [for]="'format_' + f.value" class="card-label">{{ f.label }}</label>
2347
+ <span class="card-description">{{ f.description }}</span>
2348
+ </div>
2349
+ </div>
2350
+ </div>
2351
+ </div>
2352
+ }
2353
+
2354
+ <!-- Escopo -->
2355
+ <div class="export-section">
2356
+ <div class="section-header">
2357
+ <div class="section-icon"><i class="pi pi-filter"></i></div>
2358
+ <h3>Escopo da exportação</h3>
2359
+ </div>
2360
+ <div class="scope-options">
2361
+ <div
2362
+ class="scope-card"
2363
+ [class.selected]="!hasSelectedIds()"
2364
+ (click)="exportScope.set('all')"
2365
+ >
2366
+ <div class="card-radio">
2367
+ <p-radioButton value="all" [(ngModel)]="exportScopeModel" inputId="scope_all"></p-radioButton>
2368
+ </div>
2369
+ <div class="card-icon"><i class="pi pi-list"></i></div>
2370
+ <div class="card-info">
2371
+ <label for="scope_all" class="card-label">Todos os registros</label>
2372
+ <span class="card-description">Exporta todos com o filtro ativo</span>
2373
+ </div>
2374
+ </div>
2375
+ <div
2376
+ class="scope-card"
2377
+ [class.selected]="hasSelectedIds()"
2378
+ [class.disabled]="!hasSelectedIds()"
2379
+ (click)="hasSelectedIds() ? exportScope.set('selected') : null"
2380
+ >
2381
+ <div class="card-radio">
2382
+ <p-radioButton
2383
+ value="selected"
2384
+ [(ngModel)]="exportScopeModel"
2385
+ inputId="scope_selected"
2386
+ [disabled]="!hasSelectedIds()"
2387
+ ></p-radioButton>
2388
+ </div>
2389
+ <div class="card-icon"><i class="pi pi-check-square"></i></div>
2390
+ <div class="card-info">
2391
+ <label for="scope_selected" class="card-label">Registros selecionados</label>
2392
+ <span class="card-description">Exporta apenas os itens marcados</span>
2393
+ <span class="card-count">
2394
+ <i class="pi pi-circle-fill"></i>
2395
+ {{ selectedIds().length }} registro(s) selecionado(s)
2396
+ </span>
2397
+ </div>
2398
+ </div>
2399
+ </div>
2400
+ </div>
2401
+
2402
+ <!-- Seleção de Colunas -->
2403
+ <div class="export-section">
2404
+ <div class="section-header">
2405
+ <div class="section-icon"><i class="pi pi-table"></i></div>
2406
+ <h3>Colunas</h3>
2407
+ <div class="column-actions">
2408
+ <button type="button" class="action-link" (click)="selectAllColumns()">Todas</button>
2409
+ <span class="separator">|</span>
2410
+ <button type="button" class="action-link" (click)="deselectAllColumns()">Nenhuma</button>
2411
+ </div>
2412
+ </div>
2413
+ <div class="columns-grid">
2414
+ <div
2415
+ *ngFor="let col of columnSelections()"
2416
+ class="column-item"
2417
+ [class.selected]="col.selected"
2418
+ (click)="toggleColumn(col)"
2419
+ >
2420
+ <p-checkbox
2421
+ [(ngModel)]="col.selected"
2422
+ [binary]="true"
2423
+ [inputId]="'col_' + col.column.columnName"
2424
+ (onChange)="refreshColumns()"
2425
+ ></p-checkbox>
2426
+ <label [for]="'col_' + col.column.columnName" class="column-label">
2427
+ {{ col.column.translateKey }}
2428
+ </label>
2429
+ </div>
2430
+ </div>
2431
+ <div class="columns-summary">
2432
+ <i class="pi pi-info-circle"></i>
2433
+ <span>{{ selectedColumnsCount() }} de {{ columnSelections().length }} colunas selecionadas</span>
2434
+ </div>
2435
+ </div>
2436
+
2437
+ </div>
2438
+
2439
+ <ng-template pTemplate="footer">
2440
+ <p-button
2441
+ label="Cancelar"
2442
+ icon="pi pi-times"
2443
+ severity="secondary"
2444
+ [text]="true"
2445
+ (onClick)="onDialogHide()"
2446
+ />
2447
+ <p-button
2448
+ label="Exportar"
2449
+ icon="pi pi-download"
2450
+ [disabled]="!canExport()"
2451
+ [loading]="exporting()"
2452
+ (onClick)="doExport()"
2453
+ />
2454
+ </ng-template>
2455
+ </p-dialog>
2456
+ `, 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"] }] });
2457
+ }
2458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ExportDataComponent, decorators: [{
2459
+ type: Component,
2460
+ args: [{ selector: 's-export-data', standalone: true, imports: [
2461
+ CommonModule,
2462
+ FormsModule,
2463
+ DialogModule,
2464
+ ButtonModule,
2465
+ RadioButtonModule,
2466
+ CheckboxModule
2467
+ ], template: `
2468
+ <p-dialog
2469
+ [visible]="visible()"
2470
+ (visibleChange)="visibleChange.emit($event)"
2471
+ (onHide)="onDialogHide()"
2472
+ [modal]="true"
2473
+ [draggable]="false"
2474
+ [resizable]="false"
2475
+ [style]="{ width: '700px' }"
2476
+ styleClass="s-export-dialog"
2477
+ >
2478
+ <ng-template pTemplate="header">
2479
+ <div class="custom-header">
2480
+ <i class="pi pi-download header-icon"></i>
2481
+ <span>Exportar Dados</span>
2482
+ </div>
2483
+ </ng-template>
2484
+
2485
+ <div class="export-content">
2486
+
2487
+ <!-- Formato de Exportação -->
2488
+ <div class="export-section">
2489
+ <div class="section-header">
2490
+ <div class="section-icon"><i class="pi pi-file"></i></div>
2491
+ <h3>Formato do arquivo</h3>
2492
+ </div>
2493
+ <div class="format-options">
2494
+ <div
2495
+ *ngFor="let fmt of fileTypes"
2496
+ class="format-card"
2497
+ [class.selected]="selectedFileType() === fmt.value"
2498
+ (click)="selectedFileType.set(fmt.value)"
2499
+ >
2500
+ <div class="card-radio">
2501
+ <p-radioButton
2502
+ [value]="fmt.value"
2503
+ [(ngModel)]="selectedFileTypeModel"
2504
+ [inputId]="'fmt_' + fmt.value"
2505
+ ></p-radioButton>
2506
+ </div>
2507
+ <div class="card-icon" [class.excel]="fmt.value === 'EXCEL'" [class.pdf]="fmt.value === 'PDF'">
2508
+ <i class="pi" [ngClass]="fmt.icon"></i>
2509
+ </div>
2510
+ <div class="card-info">
2511
+ <label [for]="'fmt_' + fmt.value" class="card-label">{{ fmt.label }}</label>
2512
+ <span class="card-description">{{ fmt.description }}</span>
2513
+ </div>
2514
+ </div>
2515
+ </div>
2516
+ </div>
2517
+
2518
+ <!-- Orientação + Formato da folha (apenas PDF) -->
2519
+ @if (selectedFileType() === 'PDF') {
2520
+ <div class="export-section">
2521
+ <div class="section-header">
2522
+ <div class="section-icon"><i class="pi pi-arrows-alt"></i></div>
2523
+ <h3>Orientação da página</h3>
2524
+ </div>
2525
+ <div class="format-options">
2526
+ <div
2527
+ *ngFor="let o of orientations"
2528
+ class="format-card"
2529
+ [class.selected]="selectedOrientation() === o.value"
2530
+ (click)="selectedOrientation.set(o.value)"
2531
+ >
2532
+ <div class="card-radio">
2533
+ <p-radioButton
2534
+ [value]="o.value"
2535
+ [(ngModel)]="selectedOrientationModel"
2536
+ [inputId]="'orient_' + o.value"
2537
+ ></p-radioButton>
2538
+ </div>
2539
+ <div class="card-icon">
2540
+ <i class="pi" [ngClass]="o.icon"></i>
2541
+ </div>
2542
+ <div class="card-info">
2543
+ <label [for]="'orient_' + o.value" class="card-label">{{ o.label }}</label>
2544
+ </div>
2545
+ </div>
2546
+ </div>
2547
+ </div>
2548
+
2549
+ <div class="export-section">
2550
+ <div class="section-header">
2551
+ <div class="section-icon"><i class="pi pi-stop"></i></div>
2552
+ <h3>Formato da folha</h3>
2553
+ </div>
2554
+ <div class="format-options">
2555
+ <div
2556
+ *ngFor="let f of fileFormats"
2557
+ class="format-card"
2558
+ [class.selected]="selectedFileFormat() === f.value"
2559
+ (click)="selectedFileFormat.set(f.value)"
2560
+ >
2561
+ <div class="card-radio">
2562
+ <p-radioButton
2563
+ [value]="f.value"
2564
+ [(ngModel)]="selectedFileFormatModel"
2565
+ [inputId]="'format_' + f.value"
2566
+ ></p-radioButton>
2567
+ </div>
2568
+ <div class="card-icon">
2569
+ <i class="pi pi-file"></i>
2570
+ </div>
2571
+ <div class="card-info">
2572
+ <label [for]="'format_' + f.value" class="card-label">{{ f.label }}</label>
2573
+ <span class="card-description">{{ f.description }}</span>
2574
+ </div>
2575
+ </div>
2576
+ </div>
2577
+ </div>
2578
+ }
2579
+
2580
+ <!-- Escopo -->
2581
+ <div class="export-section">
2582
+ <div class="section-header">
2583
+ <div class="section-icon"><i class="pi pi-filter"></i></div>
2584
+ <h3>Escopo da exportação</h3>
2585
+ </div>
2586
+ <div class="scope-options">
2587
+ <div
2588
+ class="scope-card"
2589
+ [class.selected]="!hasSelectedIds()"
2590
+ (click)="exportScope.set('all')"
2591
+ >
2592
+ <div class="card-radio">
2593
+ <p-radioButton value="all" [(ngModel)]="exportScopeModel" inputId="scope_all"></p-radioButton>
2594
+ </div>
2595
+ <div class="card-icon"><i class="pi pi-list"></i></div>
2596
+ <div class="card-info">
2597
+ <label for="scope_all" class="card-label">Todos os registros</label>
2598
+ <span class="card-description">Exporta todos com o filtro ativo</span>
2599
+ </div>
2600
+ </div>
2601
+ <div
2602
+ class="scope-card"
2603
+ [class.selected]="hasSelectedIds()"
2604
+ [class.disabled]="!hasSelectedIds()"
2605
+ (click)="hasSelectedIds() ? exportScope.set('selected') : null"
2606
+ >
2607
+ <div class="card-radio">
2608
+ <p-radioButton
2609
+ value="selected"
2610
+ [(ngModel)]="exportScopeModel"
2611
+ inputId="scope_selected"
2612
+ [disabled]="!hasSelectedIds()"
2613
+ ></p-radioButton>
2614
+ </div>
2615
+ <div class="card-icon"><i class="pi pi-check-square"></i></div>
2616
+ <div class="card-info">
2617
+ <label for="scope_selected" class="card-label">Registros selecionados</label>
2618
+ <span class="card-description">Exporta apenas os itens marcados</span>
2619
+ <span class="card-count">
2620
+ <i class="pi pi-circle-fill"></i>
2621
+ {{ selectedIds().length }} registro(s) selecionado(s)
2622
+ </span>
2623
+ </div>
2624
+ </div>
2625
+ </div>
2626
+ </div>
2627
+
2628
+ <!-- Seleção de Colunas -->
2629
+ <div class="export-section">
2630
+ <div class="section-header">
2631
+ <div class="section-icon"><i class="pi pi-table"></i></div>
2632
+ <h3>Colunas</h3>
2633
+ <div class="column-actions">
2634
+ <button type="button" class="action-link" (click)="selectAllColumns()">Todas</button>
2635
+ <span class="separator">|</span>
2636
+ <button type="button" class="action-link" (click)="deselectAllColumns()">Nenhuma</button>
2637
+ </div>
2638
+ </div>
2639
+ <div class="columns-grid">
2640
+ <div
2641
+ *ngFor="let col of columnSelections()"
2642
+ class="column-item"
2643
+ [class.selected]="col.selected"
2644
+ (click)="toggleColumn(col)"
2645
+ >
2646
+ <p-checkbox
2647
+ [(ngModel)]="col.selected"
2648
+ [binary]="true"
2649
+ [inputId]="'col_' + col.column.columnName"
2650
+ (onChange)="refreshColumns()"
2651
+ ></p-checkbox>
2652
+ <label [for]="'col_' + col.column.columnName" class="column-label">
2653
+ {{ col.column.translateKey }}
2654
+ </label>
2655
+ </div>
2656
+ </div>
2657
+ <div class="columns-summary">
2658
+ <i class="pi pi-info-circle"></i>
2659
+ <span>{{ selectedColumnsCount() }} de {{ columnSelections().length }} colunas selecionadas</span>
2660
+ </div>
2661
+ </div>
2662
+
2663
+ </div>
2664
+
2665
+ <ng-template pTemplate="footer">
2666
+ <p-button
2667
+ label="Cancelar"
2668
+ icon="pi pi-times"
2669
+ severity="secondary"
2670
+ [text]="true"
2671
+ (onClick)="onDialogHide()"
2672
+ />
2673
+ <p-button
2674
+ label="Exportar"
2675
+ icon="pi pi-download"
2676
+ [disabled]="!canExport()"
2677
+ [loading]="exporting()"
2678
+ (onClick)="doExport()"
2679
+ />
2680
+ </ng-template>
2681
+ </p-dialog>
2682
+ `, 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"] }]
2683
+ }] });
2684
+
2026
2685
  /*
2027
2686
  * Public API Surface of @senior-gestao-relacionamento/angular-components
2028
2687
  */
@@ -2031,5 +2690,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2031
2690
  * Generated bundle index. Do not edit.
2032
2691
  */
2033
2692
 
2034
- export { AngularComponentsModule, ChatbotComponent, CurrentCollaboratorService, Month, OrdinalWeekDay, ParticipantConfirmation, RepeatWhen, ScheduleDetailComponent, ScheduleFormComponent, SchedulePriority, ScheduleRecurrence, ScheduleService, ScheduleTypeService, Status, StorageService, WeekDay, provideAngularComponentsTranslations };
2693
+ 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
2694
  //# sourceMappingURL=senior-gestao-relacionamento-angular-components.mjs.map