tin-spa 20.11.3 → 20.11.6
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.
- package/fesm2022/tin-spa.mjs +812 -812
- package/fesm2022/tin-spa.mjs.map +1 -1
- package/package.json +1 -1
package/fesm2022/tin-spa.mjs
CHANGED
|
@@ -4809,11 +4809,11 @@ class ImportDialogComponent {
|
|
|
4809
4809
|
this.stepper.selectedIndex = index; }, 0);
|
|
4810
4810
|
}
|
|
4811
4811
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ImportDialogComponent, deps: [{ token: HttpService }, { token: i1$1.HttpClient }, { token: MessageService }, { token: i4.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4812
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ImportDialogComponent, isStandalone: false, selector: "spa-import-dialog", viewQueries: [{ propertyName: "stepper", first: true, predicate: ["stepper"], descendants: true }], ngImport: i0, template: "<div class=\"import-dialog\">\n\n <div class=\"import-header\">\n <h2>{{ title }}</h2>\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\n </div>\n\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\n\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\n\n <!-- Step 1: Upload -->\n <mat-step label=\"Upload\">\n <div class=\"step-body\">\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\n\n <div class=\"upload-row\">\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\n <mat-icon>download</mat-icon> Download template\n </button>\n </div>\n\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 2: Map columns -->\n <mat-step label=\"Map columns\">\n <div class=\"step-body\">\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\n </div>\n\n <table class=\"map-table\">\n <thead>\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let m of mappings\">\n <td class=\"col-header\">{{ m.header }}</td>\n <td class=\"col-samples\">\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\n </td>\n <td>\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\n </mat-option>\n </mat-select>\n </td>\n <td>\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 3: Review -->\n <mat-step label=\"Review\">\n <div class=\"step-body\">\n <div class=\"tiles\">\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\n </div>\n\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\n\n <div class=\"grid-scroll\">\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\n\n <ng-container matColumnDef=\"rowNumber\">\n <th mat-header-cell *matHeaderCellDef>#</th>\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\n </ng-container>\n\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\n <td mat-cell *matCellDef=\"let row\"\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\n </ng-container>\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>Status</th>\n <td mat-cell *matCellDef=\"let row\">\n <span class=\"status-chip\" [ngClass]=\"{\n 'chip-green': row.status === 1,\n 'chip-orange': row.status === 2,\n 'chip-red': row.status === 3\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\n </ng-container>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\n Import {{ counts.total - counts.error }} record(s)\n </button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 4: Done -->\n <mat-step label=\"Done\">\n <div class=\"step-body done-body\">\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\n <h3>Imported {{ committedCount }} record(s)</h3>\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\n </div>\n </div>\n </mat-step>\n\n </mat-horizontal-stepper>\n</div>\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i7.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i7.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i7.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i7.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i7.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i7.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i7.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i7.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i7.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i7.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: i13.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "component", type: i13.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
4812
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ImportDialogComponent, isStandalone: false, selector: "spa-import-dialog", viewQueries: [{ propertyName: "stepper", first: true, predicate: ["stepper"], descendants: true }], ngImport: i0, template: "<div class=\"import-dialog\">\r\n\r\n <div class=\"import-header\">\r\n <h2>{{ title }}</h2>\r\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\r\n </div>\r\n\r\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\r\n\r\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\r\n\r\n <!-- Step 1: Upload -->\r\n <mat-step label=\"Upload\">\r\n <div class=\"step-body\">\r\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\r\n\r\n <div class=\"upload-row\">\r\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\r\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\r\n <mat-icon>download</mat-icon> Download template\r\n </button>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 2: Map columns -->\r\n <mat-step label=\"Map columns\">\r\n <div class=\"step-body\">\r\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\r\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\r\n </div>\r\n\r\n <table class=\"map-table\">\r\n <thead>\r\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let m of mappings\">\r\n <td class=\"col-header\">{{ m.header }}</td>\r\n <td class=\"col-samples\">\r\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\r\n </td>\r\n <td>\r\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\r\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\r\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\r\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\r\n </mat-option>\r\n </mat-select>\r\n </td>\r\n <td>\r\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\r\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\r\n </span>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 3: Review -->\r\n <mat-step label=\"Review\">\r\n <div class=\"step-body\">\r\n <div class=\"tiles\">\r\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\r\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\r\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\r\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\r\n </div>\r\n\r\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\r\n\r\n <div class=\"grid-scroll\">\r\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\r\n\r\n <ng-container matColumnDef=\"rowNumber\">\r\n <th mat-header-cell *matHeaderCellDef>#</th>\r\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\r\n </ng-container>\r\n\r\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\r\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\r\n <td mat-cell *matCellDef=\"let row\"\r\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\r\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\r\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\r\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\r\n </ng-container>\r\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\r\n </td>\r\n </ng-container>\r\n\r\n <ng-container matColumnDef=\"status\">\r\n <th mat-header-cell *matHeaderCellDef>Status</th>\r\n <td mat-cell *matCellDef=\"let row\">\r\n <span class=\"status-chip\" [ngClass]=\"{\r\n 'chip-green': row.status === 1,\r\n 'chip-orange': row.status === 2,\r\n 'chip-red': row.status === 3\r\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\r\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\r\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\r\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\r\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\r\n </ng-container>\r\n </td>\r\n </ng-container>\r\n\r\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\r\n Import {{ counts.total - counts.error }} record(s)\r\n </button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 4: Done -->\r\n <mat-step label=\"Done\">\r\n <div class=\"step-body done-body\">\r\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\r\n <h3>Imported {{ committedCount }} record(s)</h3>\r\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n </mat-horizontal-stepper>\r\n</div>\r\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i7.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i7.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i7.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i7.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i7.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i7.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i7.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i7.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i7.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i7.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: i13.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "component", type: i13.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
4813
4813
|
}
|
|
4814
4814
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ImportDialogComponent, decorators: [{
|
|
4815
4815
|
type: Component,
|
|
4816
|
-
args: [{ selector: 'spa-import-dialog', standalone: false, template: "<div class=\"import-dialog\">\n\n <div class=\"import-header\">\n <h2>{{ title }}</h2>\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\n </div>\n\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\n\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\n\n <!-- Step 1: Upload -->\n <mat-step label=\"Upload\">\n <div class=\"step-body\">\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\n\n <div class=\"upload-row\">\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\n <mat-icon>download</mat-icon> Download template\n </button>\n </div>\n\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 2: Map columns -->\n <mat-step label=\"Map columns\">\n <div class=\"step-body\">\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\n </div>\n\n <table class=\"map-table\">\n <thead>\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let m of mappings\">\n <td class=\"col-header\">{{ m.header }}</td>\n <td class=\"col-samples\">\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\n </td>\n <td>\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\n </mat-option>\n </mat-select>\n </td>\n <td>\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 3: Review -->\n <mat-step label=\"Review\">\n <div class=\"step-body\">\n <div class=\"tiles\">\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\n </div>\n\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\n\n <div class=\"grid-scroll\">\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\n\n <ng-container matColumnDef=\"rowNumber\">\n <th mat-header-cell *matHeaderCellDef>#</th>\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\n </ng-container>\n\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\n <td mat-cell *matCellDef=\"let row\"\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\n </ng-container>\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>Status</th>\n <td mat-cell *matCellDef=\"let row\">\n <span class=\"status-chip\" [ngClass]=\"{\n 'chip-green': row.status === 1,\n 'chip-orange': row.status === 2,\n 'chip-red': row.status === 3\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\n </ng-container>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\n Import {{ counts.total - counts.error }} record(s)\n </button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 4: Done -->\n <mat-step label=\"Done\">\n <div class=\"step-body done-body\">\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\n <h3>Imported {{ committedCount }} record(s)</h3>\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\n </div>\n </div>\n </mat-step>\n\n </mat-horizontal-stepper>\n</div>\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"] }]
|
|
4816
|
+
args: [{ selector: 'spa-import-dialog', standalone: false, template: "<div class=\"import-dialog\">\r\n\r\n <div class=\"import-header\">\r\n <h2>{{ title }}</h2>\r\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\r\n </div>\r\n\r\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\r\n\r\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\r\n\r\n <!-- Step 1: Upload -->\r\n <mat-step label=\"Upload\">\r\n <div class=\"step-body\">\r\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\r\n\r\n <div class=\"upload-row\">\r\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\r\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\r\n <mat-icon>download</mat-icon> Download template\r\n </button>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 2: Map columns -->\r\n <mat-step label=\"Map columns\">\r\n <div class=\"step-body\">\r\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\r\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\r\n </div>\r\n\r\n <table class=\"map-table\">\r\n <thead>\r\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let m of mappings\">\r\n <td class=\"col-header\">{{ m.header }}</td>\r\n <td class=\"col-samples\">\r\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\r\n </td>\r\n <td>\r\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\r\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\r\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\r\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\r\n </mat-option>\r\n </mat-select>\r\n </td>\r\n <td>\r\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\r\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\r\n </span>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 3: Review -->\r\n <mat-step label=\"Review\">\r\n <div class=\"step-body\">\r\n <div class=\"tiles\">\r\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\r\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\r\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\r\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\r\n </div>\r\n\r\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\r\n\r\n <div class=\"grid-scroll\">\r\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\r\n\r\n <ng-container matColumnDef=\"rowNumber\">\r\n <th mat-header-cell *matHeaderCellDef>#</th>\r\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\r\n </ng-container>\r\n\r\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\r\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\r\n <td mat-cell *matCellDef=\"let row\"\r\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\r\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\r\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\r\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\r\n </ng-container>\r\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\r\n </td>\r\n </ng-container>\r\n\r\n <ng-container matColumnDef=\"status\">\r\n <th mat-header-cell *matHeaderCellDef>Status</th>\r\n <td mat-cell *matCellDef=\"let row\">\r\n <span class=\"status-chip\" [ngClass]=\"{\r\n 'chip-green': row.status === 1,\r\n 'chip-orange': row.status === 2,\r\n 'chip-red': row.status === 3\r\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\r\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\r\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\r\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\r\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\r\n </ng-container>\r\n </td>\r\n </ng-container>\r\n\r\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\r\n Import {{ counts.total - counts.error }} record(s)\r\n </button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 4: Done -->\r\n <mat-step label=\"Done\">\r\n <div class=\"step-body done-body\">\r\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\r\n <h3>Imported {{ committedCount }} record(s)</h3>\r\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n </mat-horizontal-stepper>\r\n</div>\r\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"] }]
|
|
4817
4817
|
}], ctorParameters: () => [{ type: HttpService }, { type: i1$1.HttpClient }, { type: MessageService }, { type: i4.MatDialogRef }, { type: undefined, decorators: [{
|
|
4818
4818
|
type: Inject,
|
|
4819
4819
|
args: [MAT_DIALOG_DATA]
|
|
@@ -8861,11 +8861,11 @@ class SetupGuideComponent {
|
|
|
8861
8861
|
this.dataService.Navigate(act.link || step.link);
|
|
8862
8862
|
}
|
|
8863
8863
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SetupGuideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8864
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SetupGuideComponent, isStandalone: false, selector: "spa-setup-guide", ngImport: i0, template: "<div class=\"setup-page\" *ngIf=\"status\">\n\n <!-- Hero: overall readiness + celebration state at 100% -->\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\n <div class=\"hero-text\">\n <h1>{{ title }}</h1>\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\n </div>\n <div class=\"hero-progress\">\n <span class=\"hero-percent\">{{ status.percent }}%</span>\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\n </div>\n </div>\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\n <div class=\"hero-text\">\n <h1>You're all set!</h1>\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\n </div>\n </div>\n </mat-card>\n\n <!-- Category groups, top-down -->\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\n <div class=\"group-header\">\n <h2>{{ group.name }}</h2>\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\n </div>\n <mat-accordion displayMode=\"flat\">\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\n </mat-panel-title>\n <mat-panel-description>\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\n </mat-panel-description>\n </mat-expansion-panel-header>\n <p class=\"step-description\">{{ step.description }}</p>\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\n </div>\n </mat-expansion-panel>\n </mat-accordion>\n </mat-card>\n\n</div>\n\n<!-- Graceful empty state (feature disabled or status unavailable) -->\n<div class=\"setup-empty\" *ngIf=\"!status\">\n <mat-icon>rocket_launch</mat-icon>\n <p>Setup status is not available yet.</p>\n</div>\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i18.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "directive", type: i6$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i6$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i6$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i6$1.MatExpansionPanelDescription, selector: "mat-panel-description" }] }); }
|
|
8864
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SetupGuideComponent, isStandalone: false, selector: "spa-setup-guide", ngImport: i0, template: "<div class=\"setup-page\" *ngIf=\"status\">\r\n\r\n <!-- Hero: overall readiness + celebration state at 100% -->\r\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\r\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\r\n <div class=\"hero-text\">\r\n <h1>{{ title }}</h1>\r\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\r\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\r\n </div>\r\n <div class=\"hero-progress\">\r\n <span class=\"hero-percent\">{{ status.percent }}%</span>\r\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\r\n </div>\r\n </div>\r\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\r\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\r\n <div class=\"hero-text\">\r\n <h1>You're all set!</h1>\r\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\r\n </div>\r\n </div>\r\n </mat-card>\r\n\r\n <!-- Category groups, top-down -->\r\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\r\n <div class=\"group-header\">\r\n <h2>{{ group.name }}</h2>\r\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\r\n </div>\r\n <mat-accordion displayMode=\"flat\">\r\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\r\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\r\n </mat-panel-title>\r\n <mat-panel-description>\r\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\r\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\r\n </mat-panel-description>\r\n </mat-expansion-panel-header>\r\n <p class=\"step-description\">{{ step.description }}</p>\r\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\r\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\r\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\r\n </div>\r\n </mat-expansion-panel>\r\n </mat-accordion>\r\n </mat-card>\r\n\r\n</div>\r\n\r\n<!-- Graceful empty state (feature disabled or status unavailable) -->\r\n<div class=\"setup-empty\" *ngIf=\"!status\">\r\n <mat-icon>rocket_launch</mat-icon>\r\n <p>Setup status is not available yet.</p>\r\n</div>\r\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i18.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "directive", type: i6$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i6$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i6$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i6$1.MatExpansionPanelDescription, selector: "mat-panel-description" }] }); }
|
|
8865
8865
|
}
|
|
8866
8866
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SetupGuideComponent, decorators: [{
|
|
8867
8867
|
type: Component,
|
|
8868
|
-
args: [{ selector: 'spa-setup-guide', standalone: false, template: "<div class=\"setup-page\" *ngIf=\"status\">\n\n <!-- Hero: overall readiness + celebration state at 100% -->\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\n <div class=\"hero-text\">\n <h1>{{ title }}</h1>\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\n </div>\n <div class=\"hero-progress\">\n <span class=\"hero-percent\">{{ status.percent }}%</span>\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\n </div>\n </div>\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\n <div class=\"hero-text\">\n <h1>You're all set!</h1>\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\n </div>\n </div>\n </mat-card>\n\n <!-- Category groups, top-down -->\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\n <div class=\"group-header\">\n <h2>{{ group.name }}</h2>\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\n </div>\n <mat-accordion displayMode=\"flat\">\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\n </mat-panel-title>\n <mat-panel-description>\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\n </mat-panel-description>\n </mat-expansion-panel-header>\n <p class=\"step-description\">{{ step.description }}</p>\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\n </div>\n </mat-expansion-panel>\n </mat-accordion>\n </mat-card>\n\n</div>\n\n<!-- Graceful empty state (feature disabled or status unavailable) -->\n<div class=\"setup-empty\" *ngIf=\"!status\">\n <mat-icon>rocket_launch</mat-icon>\n <p>Setup status is not available yet.</p>\n</div>\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"] }]
|
|
8868
|
+
args: [{ selector: 'spa-setup-guide', standalone: false, template: "<div class=\"setup-page\" *ngIf=\"status\">\r\n\r\n <!-- Hero: overall readiness + celebration state at 100% -->\r\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\r\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\r\n <div class=\"hero-text\">\r\n <h1>{{ title }}</h1>\r\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\r\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\r\n </div>\r\n <div class=\"hero-progress\">\r\n <span class=\"hero-percent\">{{ status.percent }}%</span>\r\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\r\n </div>\r\n </div>\r\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\r\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\r\n <div class=\"hero-text\">\r\n <h1>You're all set!</h1>\r\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\r\n </div>\r\n </div>\r\n </mat-card>\r\n\r\n <!-- Category groups, top-down -->\r\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\r\n <div class=\"group-header\">\r\n <h2>{{ group.name }}</h2>\r\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\r\n </div>\r\n <mat-accordion displayMode=\"flat\">\r\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\r\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\r\n </mat-panel-title>\r\n <mat-panel-description>\r\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\r\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\r\n </mat-panel-description>\r\n </mat-expansion-panel-header>\r\n <p class=\"step-description\">{{ step.description }}</p>\r\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\r\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\r\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\r\n </div>\r\n </mat-expansion-panel>\r\n </mat-accordion>\r\n </mat-card>\r\n\r\n</div>\r\n\r\n<!-- Graceful empty state (feature disabled or status unavailable) -->\r\n<div class=\"setup-empty\" *ngIf=\"!status\">\r\n <mat-icon>rocket_launch</mat-icon>\r\n <p>Setup status is not available yet.</p>\r\n</div>\r\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"] }]
|
|
8869
8869
|
}] });
|
|
8870
8870
|
|
|
8871
8871
|
// TinSync toolbar indicator — a persistent connection light that lives in the nav bar next to Notifications.
|
|
@@ -11194,11 +11194,11 @@ class TableRowComponent {
|
|
|
11194
11194
|
return false;
|
|
11195
11195
|
}
|
|
11196
11196
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableRowComponent, deps: [{ token: ButtonService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11197
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableRowComponent, isStandalone: false, selector: "app-table-row", inputs: { column: "column", row: "row", config: "config", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick", columnClick: "columnClick", showBannerEvent: "showBannerEvent" }, usesOnChanges: true, ngImport: i0, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color, 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CheckComponent, selector: "spa-check", inputs: ["readonly", "display", "value", "infoMessage"], outputs: ["valueChange", "click", "check", "uncheck", "infoClick"] }, { kind: "component", type: SelectLiteComponent, selector: "spa-select-lite" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
11197
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableRowComponent, isStandalone: false, selector: "app-table-row", inputs: { column: "column", row: "row", config: "config", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick", columnClick: "columnClick", showBannerEvent: "showBannerEvent" }, usesOnChanges: true, ngImport: i0, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color || '#eceff1', 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CheckComponent, selector: "spa-check", inputs: ["readonly", "display", "value", "infoMessage"], outputs: ["valueChange", "click", "check", "uncheck", "infoClick"] }, { kind: "component", type: SelectLiteComponent, selector: "spa-select-lite" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
11198
11198
|
}
|
|
11199
11199
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableRowComponent, decorators: [{
|
|
11200
11200
|
type: Component,
|
|
11201
|
-
args: [{ selector: 'app-table-row', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color, 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"] }]
|
|
11201
|
+
args: [{ selector: 'app-table-row', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color || '#eceff1', 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"] }]
|
|
11202
11202
|
}], ctorParameters: () => [{ type: ButtonService }], propDecorators: { column: [{
|
|
11203
11203
|
type: Input
|
|
11204
11204
|
}], row: [{
|
|
@@ -11314,11 +11314,11 @@ class TableActionComponent {
|
|
|
11314
11314
|
: [];
|
|
11315
11315
|
}
|
|
11316
11316
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableActionComponent, deps: [{ token: ButtonService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11317
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableActionComponent, isStandalone: false, selector: "app-table-action", inputs: { displayedButtons: "displayedButtons", config: "config", row: "row", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\n<ng-container *ngFor=\"let vm of visibleVMs\">\n\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\n <label style=\"margin-right: 35px;\"></label>\n </ng-container>\n\n</ng-container>\n\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\n <mat-icon>more_vert</mat-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let vm of overflowVMs\">\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">\n {{vm.icon}}\n </mat-icon>\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\n </button>\n </ng-container>\n</mat-menu>\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11317
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableActionComponent, isStandalone: false, selector: "app-table-action", inputs: { displayedButtons: "displayedButtons", config: "config", row: "row", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\r\n<ng-container *ngFor=\"let vm of visibleVMs\">\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\r\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\r\n <label style=\"margin-right: 35px;\"></label>\r\n </ng-container>\r\n\r\n</ng-container>\r\n\r\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\r\n <mat-icon>more_vert</mat-icon>\r\n</button>\r\n\r\n<mat-menu #menu=\"matMenu\">\r\n <ng-container *ngFor=\"let vm of overflowVMs\">\r\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">\r\n {{vm.icon}}\r\n </mat-icon>\r\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\r\n </button>\r\n </ng-container>\r\n</mat-menu>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11318
11318
|
}
|
|
11319
11319
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableActionComponent, decorators: [{
|
|
11320
11320
|
type: Component,
|
|
11321
|
-
args: [{ selector: 'app-table-action', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\n<ng-container *ngFor=\"let vm of visibleVMs\">\n\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\n <label style=\"margin-right: 35px;\"></label>\n </ng-container>\n\n</ng-container>\n\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\n <mat-icon>more_vert</mat-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let vm of overflowVMs\">\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">\n {{vm.icon}}\n </mat-icon>\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\n </button>\n </ng-container>\n</mat-menu>\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"] }]
|
|
11321
|
+
args: [{ selector: 'app-table-action', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\r\n<ng-container *ngFor=\"let vm of visibleVMs\">\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\r\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\r\n <label style=\"margin-right: 35px;\"></label>\r\n </ng-container>\r\n\r\n</ng-container>\r\n\r\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\r\n <mat-icon>more_vert</mat-icon>\r\n</button>\r\n\r\n<mat-menu #menu=\"matMenu\">\r\n <ng-container *ngFor=\"let vm of overflowVMs\">\r\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">\r\n {{vm.icon}}\r\n </mat-icon>\r\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\r\n </button>\r\n </ng-container>\r\n</mat-menu>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"] }]
|
|
11322
11322
|
}], ctorParameters: () => [{ type: ButtonService }], propDecorators: { displayedButtons: [{
|
|
11323
11323
|
type: Input
|
|
11324
11324
|
}], config: [{
|
|
@@ -12396,58 +12396,58 @@ class InlineCellComponent {
|
|
|
12396
12396
|
this.valueChange.emit({ field: this.field, value: this.data[this.field.name] });
|
|
12397
12397
|
}
|
|
12398
12398
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: InlineCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
12399
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: InlineCellComponent, isStandalone: false, selector: "app-inline-cell", inputs: { field: "field", data: "data" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
|
|
12400
|
-
<ng-container [ngSwitch]="field.type">
|
|
12401
|
-
|
|
12402
|
-
<spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
|
|
12403
|
-
|
|
12404
|
-
<spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
|
|
12405
|
-
|
|
12406
|
-
<spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
|
|
12407
|
-
|
|
12408
|
-
<spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
|
|
12409
|
-
|
|
12410
|
-
<spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
|
|
12411
|
-
|
|
12412
|
-
<spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
|
|
12413
|
-
[optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
|
|
12414
|
-
[required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
|
|
12415
|
-
|
|
12416
|
-
<spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
|
|
12417
|
-
|
|
12418
|
-
<spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
|
|
12419
|
-
|
|
12420
|
-
<spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
|
|
12421
|
-
|
|
12422
|
-
</ng-container>
|
|
12399
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: InlineCellComponent, isStandalone: false, selector: "app-inline-cell", inputs: { field: "field", data: "data" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
|
|
12400
|
+
<ng-container [ngSwitch]="field.type">
|
|
12401
|
+
|
|
12402
|
+
<spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
|
|
12403
|
+
|
|
12404
|
+
<spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
|
|
12405
|
+
|
|
12406
|
+
<spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
|
|
12407
|
+
|
|
12408
|
+
<spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
|
|
12409
|
+
|
|
12410
|
+
<spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
|
|
12411
|
+
|
|
12412
|
+
<spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
|
|
12413
|
+
[optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
|
|
12414
|
+
[required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
|
|
12415
|
+
|
|
12416
|
+
<spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
|
|
12417
|
+
|
|
12418
|
+
<spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
|
|
12419
|
+
|
|
12420
|
+
<spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
|
|
12421
|
+
|
|
12422
|
+
</ng-container>
|
|
12423
12423
|
`, isInline: true, styles: [":host{display:block;min-width:90px;padding-top:6px}\n"], dependencies: [{ kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: TextComponent, selector: "spa-text", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "format", "type", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: TextAreaComponent, selector: "spa-text-area", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "rows", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: CheckComponent, selector: "spa-check", inputs: ["readonly", "display", "value", "infoMessage"], outputs: ["valueChange", "click", "check", "uncheck", "infoClick"] }, { kind: "component", type: DateComponent, selector: "spa-date", inputs: ["required", "min", "max", "readonly", "hint", "value", "display", "placeholder", "width", "suffix", "infoMessage", "copyContent", "clearContent"], outputs: ["valueChange"] }, { kind: "component", type: DatetimeComponent, selector: "spa-datetime", inputs: ["display", "value", "readonly", "width", "min", "max", "suffix", "infoMessage", "copyContent", "clearContent"], outputs: ["valueChange"] }, { kind: "component", type: SelectComponent, selector: "spa-select", inputs: ["detailsConfig"] }, { kind: "component", type: MoneyComponent, selector: "spa-money", inputs: ["readonly", "hint", "display", "placeholder", "value", "width", "currency", "required", "min", "max", "infoMessage", "copyContent", "clearContent", "suffix"], outputs: ["valueChange", "leave", "enterPress", "infoClick"] }, { kind: "component", type: NumberComponent, selector: "spa-number", inputs: ["readonly", "hint", "display", "placeholder", "value", "width", "required", "min", "max", "step", "suffix", "infoMessage", "copyContent", "clearContent"], outputs: ["valueChange", "leave", "enterPress", "infoClick"] }, { kind: "component", type: EmailComponent, selector: "spa-email", inputs: ["display", "value", "readonly", "required", "hint", "suffix", "infoMessage", "copyContent", "clearContent", "options", "optionValue"], outputs: ["valueChange"] }] }); }
|
|
12424
12424
|
}
|
|
12425
12425
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: InlineCellComponent, decorators: [{
|
|
12426
12426
|
type: Component,
|
|
12427
|
-
args: [{ selector: 'app-inline-cell', template: `
|
|
12428
|
-
<ng-container [ngSwitch]="field.type">
|
|
12429
|
-
|
|
12430
|
-
<spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
|
|
12431
|
-
|
|
12432
|
-
<spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
|
|
12433
|
-
|
|
12434
|
-
<spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
|
|
12435
|
-
|
|
12436
|
-
<spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
|
|
12437
|
-
|
|
12438
|
-
<spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
|
|
12439
|
-
|
|
12440
|
-
<spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
|
|
12441
|
-
[optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
|
|
12442
|
-
[required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
|
|
12443
|
-
|
|
12444
|
-
<spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
|
|
12445
|
-
|
|
12446
|
-
<spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
|
|
12447
|
-
|
|
12448
|
-
<spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
|
|
12449
|
-
|
|
12450
|
-
</ng-container>
|
|
12427
|
+
args: [{ selector: 'app-inline-cell', template: `
|
|
12428
|
+
<ng-container [ngSwitch]="field.type">
|
|
12429
|
+
|
|
12430
|
+
<spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
|
|
12431
|
+
|
|
12432
|
+
<spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
|
|
12433
|
+
|
|
12434
|
+
<spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
|
|
12435
|
+
|
|
12436
|
+
<spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
|
|
12437
|
+
|
|
12438
|
+
<spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
|
|
12439
|
+
|
|
12440
|
+
<spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
|
|
12441
|
+
[optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
|
|
12442
|
+
[required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
|
|
12443
|
+
|
|
12444
|
+
<spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
|
|
12445
|
+
|
|
12446
|
+
<spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
|
|
12447
|
+
|
|
12448
|
+
<spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
|
|
12449
|
+
|
|
12450
|
+
</ng-container>
|
|
12451
12451
|
`, standalone: false, styles: [":host{display:block;min-width:90px;padding-top:6px}\n"] }]
|
|
12452
12452
|
}], propDecorators: { field: [{
|
|
12453
12453
|
type: Input
|
|
@@ -14584,7 +14584,7 @@ class ToastComponent {
|
|
|
14584
14584
|
this.subs.forEach(s => s.unsubscribe());
|
|
14585
14585
|
}
|
|
14586
14586
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ToastComponent, deps: [{ token: SignalRService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14587
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ToastComponent, isStandalone: false, selector: "spa-toast", ngImport: i0, template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\n<div class=\"toast-container\">\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\n class=\"toast-item\"\n [@toastAnim]\n (click)=\"dismiss(toast.id)\">\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\n <div class=\"toast-content\">\n <span class=\"toast-category\">{{toast.category}}</span>\n <span class=\"toast-message\">{{toast.message}}</span>\n </div>\n <mat-icon class=\"toast-close\">close</mat-icon>\n </div>\n</div>\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], animations: [
|
|
14587
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ToastComponent, isStandalone: false, selector: "spa-toast", ngImport: i0, template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\r\n<div class=\"toast-container\">\r\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\r\n class=\"toast-item\"\r\n [@toastAnim]\r\n (click)=\"dismiss(toast.id)\">\r\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\r\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-category\">{{toast.category}}</span>\r\n <span class=\"toast-message\">{{toast.message}}</span>\r\n </div>\r\n <mat-icon class=\"toast-close\">close</mat-icon>\r\n </div>\r\n</div>\r\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], animations: [
|
|
14588
14588
|
trigger('toastAnim', [
|
|
14589
14589
|
transition(':enter', [
|
|
14590
14590
|
style({ opacity: 0, transform: 'translateX(100%)' }),
|
|
@@ -14608,7 +14608,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
14608
14608
|
animate('250ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateX(100%)' }))
|
|
14609
14609
|
])
|
|
14610
14610
|
])
|
|
14611
|
-
], template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\n<div class=\"toast-container\">\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\n class=\"toast-item\"\n [@toastAnim]\n (click)=\"dismiss(toast.id)\">\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\n <div class=\"toast-content\">\n <span class=\"toast-category\">{{toast.category}}</span>\n <span class=\"toast-message\">{{toast.message}}</span>\n </div>\n <mat-icon class=\"toast-close\">close</mat-icon>\n </div>\n</div>\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"] }]
|
|
14611
|
+
], template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\r\n<div class=\"toast-container\">\r\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\r\n class=\"toast-item\"\r\n [@toastAnim]\r\n (click)=\"dismiss(toast.id)\">\r\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\r\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-category\">{{toast.category}}</span>\r\n <span class=\"toast-message\">{{toast.message}}</span>\r\n </div>\r\n <mat-icon class=\"toast-close\">close</mat-icon>\r\n </div>\r\n</div>\r\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"] }]
|
|
14612
14612
|
}], ctorParameters: () => [{ type: SignalRService }] });
|
|
14613
14613
|
|
|
14614
14614
|
// Floating chat widget component for in-app Agent (renamed from AssistantComponent)
|
|
@@ -14717,7 +14717,7 @@ class AgentComponent {
|
|
|
14717
14717
|
}
|
|
14718
14718
|
}
|
|
14719
14719
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AgentComponent, deps: [{ token: AgentService }, { token: SignalRService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
14720
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: AgentComponent, isStandalone: false, selector: "spa-agent", host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "messageContainer", first: true, predicate: ["messageContainer"], descendants: true }, { propertyName: "messageInput", first: true, predicate: ["messageInput"], descendants: true }], ngImport: i0, template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\n\n<!-- FAB toggle button -->\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\n</button>\n\n<!-- Chat window -->\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\n\n <!-- Header with online status -->\n <div class=\"agent-header\">\n <div class=\"header-info\">\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\n <div class=\"header-text\">\n <span class=\"header-title\">{{ agentName }}</span>\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\n <span class=\"status-dot\"></span>\n {{ isOnline ? 'Online' : 'Offline' }}\n </span>\n </div>\n </div>\n <div class=\"header-actions\">\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\n <mat-icon>add_comment</mat-icon>\n </button>\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\n <mat-icon>remove</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Messages area -->\n <div class=\"agent-messages\" #messageContainer>\n\n <!-- Welcome experience (show when no messages) -->\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\n <p class=\"greeting-text\">{{ greeting }}</p>\n\n <!-- Three capability pillars -->\n <div class=\"capability-pillars\">\n\n <!-- Pillar 1: Navigate & Inquire -->\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\n <span class=\"pillar-title\">Ask & Navigate</span>\n </div>\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\n </div>\n\n <!-- Pillar 2: Create & Modify -->\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\n <span class=\"pillar-title\">Create & Modify</span>\n </div>\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\n </div>\n\n <!-- Pillar 3: Get Information -->\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">search</mat-icon>\n <span class=\"pillar-title\">Get Information</span>\n </div>\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\n </div>\n\n </div>\n </div>\n\n <!-- Message bubbles -->\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\n {{ msg.content }}\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\n <div class=\"message-bubble agent-bubble typing-bubble\">\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n </div>\n </div>\n\n </div>\n\n <!-- Input area -->\n <div class=\"agent-input\">\n <mat-form-field appearance=\"outline\" class=\"input-field\">\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\n </mat-form-field>\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\n <mat-icon>send</mat-icon>\n </button>\n </div>\n\n</div>\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], animations: [
|
|
14720
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: AgentComponent, isStandalone: false, selector: "spa-agent", host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "messageContainer", first: true, predicate: ["messageContainer"], descendants: true }, { propertyName: "messageInput", first: true, predicate: ["messageInput"], descendants: true }], ngImport: i0, template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\r\n\r\n<!-- FAB toggle button -->\r\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\r\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\r\n</button>\r\n\r\n<!-- Chat window -->\r\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\r\n\r\n <!-- Header with online status -->\r\n <div class=\"agent-header\">\r\n <div class=\"header-info\">\r\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\r\n <div class=\"header-text\">\r\n <span class=\"header-title\">{{ agentName }}</span>\r\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\r\n <span class=\"status-dot\"></span>\r\n {{ isOnline ? 'Online' : 'Offline' }}\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\r\n <mat-icon>add_comment</mat-icon>\r\n </button>\r\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\r\n <mat-icon>remove</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Messages area -->\r\n <div class=\"agent-messages\" #messageContainer>\r\n\r\n <!-- Welcome experience (show when no messages) -->\r\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\r\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\r\n <p class=\"greeting-text\">{{ greeting }}</p>\r\n\r\n <!-- Three capability pillars -->\r\n <div class=\"capability-pillars\">\r\n\r\n <!-- Pillar 1: Navigate & Inquire -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\r\n <span class=\"pillar-title\">Ask & Navigate</span>\r\n </div>\r\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\r\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\r\n </div>\r\n\r\n <!-- Pillar 2: Create & Modify -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\r\n <span class=\"pillar-title\">Create & Modify</span>\r\n </div>\r\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\r\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\r\n </div>\r\n\r\n <!-- Pillar 3: Get Information -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">search</mat-icon>\r\n <span class=\"pillar-title\">Get Information</span>\r\n </div>\r\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\r\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Message bubbles -->\r\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\r\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\r\n {{ msg.content }}\r\n </div>\r\n </div>\r\n\r\n <!-- Typing indicator -->\r\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\r\n <div class=\"message-bubble agent-bubble typing-bubble\">\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n\r\n <!-- Input area -->\r\n <div class=\"agent-input\">\r\n <mat-form-field appearance=\"outline\" class=\"input-field\">\r\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\r\n </mat-form-field>\r\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\r\n <mat-icon>send</mat-icon>\r\n </button>\r\n </div>\r\n\r\n</div>\r\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], animations: [
|
|
14721
14721
|
trigger('slideUp', [
|
|
14722
14722
|
transition(':enter', [
|
|
14723
14723
|
style({ opacity: 0, transform: 'translateY(20px) scale(0.95)' }),
|
|
@@ -14741,7 +14741,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
14741
14741
|
animate('200ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateY(20px) scale(0.95)' }))
|
|
14742
14742
|
])
|
|
14743
14743
|
])
|
|
14744
|
-
], template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\n\n<!-- FAB toggle button -->\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\n</button>\n\n<!-- Chat window -->\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\n\n <!-- Header with online status -->\n <div class=\"agent-header\">\n <div class=\"header-info\">\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\n <div class=\"header-text\">\n <span class=\"header-title\">{{ agentName }}</span>\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\n <span class=\"status-dot\"></span>\n {{ isOnline ? 'Online' : 'Offline' }}\n </span>\n </div>\n </div>\n <div class=\"header-actions\">\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\n <mat-icon>add_comment</mat-icon>\n </button>\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\n <mat-icon>remove</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Messages area -->\n <div class=\"agent-messages\" #messageContainer>\n\n <!-- Welcome experience (show when no messages) -->\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\n <p class=\"greeting-text\">{{ greeting }}</p>\n\n <!-- Three capability pillars -->\n <div class=\"capability-pillars\">\n\n <!-- Pillar 1: Navigate & Inquire -->\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\n <span class=\"pillar-title\">Ask & Navigate</span>\n </div>\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\n </div>\n\n <!-- Pillar 2: Create & Modify -->\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\n <span class=\"pillar-title\">Create & Modify</span>\n </div>\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\n </div>\n\n <!-- Pillar 3: Get Information -->\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">search</mat-icon>\n <span class=\"pillar-title\">Get Information</span>\n </div>\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\n </div>\n\n </div>\n </div>\n\n <!-- Message bubbles -->\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\n {{ msg.content }}\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\n <div class=\"message-bubble agent-bubble typing-bubble\">\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n </div>\n </div>\n\n </div>\n\n <!-- Input area -->\n <div class=\"agent-input\">\n <mat-form-field appearance=\"outline\" class=\"input-field\">\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\n </mat-form-field>\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\n <mat-icon>send</mat-icon>\n </button>\n </div>\n\n</div>\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"] }]
|
|
14744
|
+
], template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\r\n\r\n<!-- FAB toggle button -->\r\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\r\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\r\n</button>\r\n\r\n<!-- Chat window -->\r\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\r\n\r\n <!-- Header with online status -->\r\n <div class=\"agent-header\">\r\n <div class=\"header-info\">\r\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\r\n <div class=\"header-text\">\r\n <span class=\"header-title\">{{ agentName }}</span>\r\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\r\n <span class=\"status-dot\"></span>\r\n {{ isOnline ? 'Online' : 'Offline' }}\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\r\n <mat-icon>add_comment</mat-icon>\r\n </button>\r\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\r\n <mat-icon>remove</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Messages area -->\r\n <div class=\"agent-messages\" #messageContainer>\r\n\r\n <!-- Welcome experience (show when no messages) -->\r\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\r\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\r\n <p class=\"greeting-text\">{{ greeting }}</p>\r\n\r\n <!-- Three capability pillars -->\r\n <div class=\"capability-pillars\">\r\n\r\n <!-- Pillar 1: Navigate & Inquire -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\r\n <span class=\"pillar-title\">Ask & Navigate</span>\r\n </div>\r\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\r\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\r\n </div>\r\n\r\n <!-- Pillar 2: Create & Modify -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\r\n <span class=\"pillar-title\">Create & Modify</span>\r\n </div>\r\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\r\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\r\n </div>\r\n\r\n <!-- Pillar 3: Get Information -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">search</mat-icon>\r\n <span class=\"pillar-title\">Get Information</span>\r\n </div>\r\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\r\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Message bubbles -->\r\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\r\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\r\n {{ msg.content }}\r\n </div>\r\n </div>\r\n\r\n <!-- Typing indicator -->\r\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\r\n <div class=\"message-bubble agent-bubble typing-bubble\">\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n\r\n <!-- Input area -->\r\n <div class=\"agent-input\">\r\n <mat-form-field appearance=\"outline\" class=\"input-field\">\r\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\r\n </mat-form-field>\r\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\r\n <mat-icon>send</mat-icon>\r\n </button>\r\n </div>\r\n\r\n</div>\r\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"] }]
|
|
14745
14745
|
}], ctorParameters: () => [{ type: AgentService }, { type: SignalRService }], propDecorators: { messageContainer: [{
|
|
14746
14746
|
type: ViewChild,
|
|
14747
14747
|
args: ['messageContainer']
|
|
@@ -17241,11 +17241,11 @@ class SpaLandingComponent {
|
|
|
17241
17241
|
s.setProperty('--lp-secondary-light', c.secondaryLight);
|
|
17242
17242
|
}
|
|
17243
17243
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SpaLandingComponent, deps: [{ token: i1$2.Router }, { token: AuthService }, { token: LastRouteService }, { token: i0.ElementRef }, { token: i3$2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
17244
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SpaLandingComponent, isStandalone: false, selector: "spa-landing", inputs: { config: "config" }, host: { listeners: { "window:scroll": "onWindowScroll()" } }, ngImport: i0, template: "<!-- ===== NAVBAR ===== -->\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\n <div class=\"lp-container lp-nav-inner\">\n <!-- Brand: custom SVG override OR default img+name -->\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\n </div>\n </ng-container>\n <ng-template #defaultNavBrand>\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\n </ng-template>\n </div>\n <!-- Desktop nav links -->\n <div class=\"lp-nav-links\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n </div>\n <!-- Mobile menu overlay -->\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n <div class=\"lp-mobile-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n </div>\n <!-- Desktop auth -->\n <div class=\"lp-nav-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n <!-- Hamburger for mobile -->\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\n <span></span><span></span><span></span>\n </button>\n </div>\n</nav>\n\n<!-- ===== HERO ===== -->\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\n <!-- Optional faded watermark SVG (e.g. company logo) -->\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\n </div>\n\n <div class=\"lp-container lp-hero-content\">\n <!-- Left / centered column: brand, headline, CTAs -->\n <div class=\"lp-hero-main\">\n <!-- App brand: logo + name + decorative divider -->\n <div class=\"lp-hero-brand\">\n <div class=\"lp-hero-brand-logo\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\n <span class=\"lp-hero-divider-line\"></span>\n <span class=\"lp-hero-divider-dot\"></span>\n <span class=\"lp-hero-divider-line\"></span>\n </div>\n </div>\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\n <div class=\"lp-hero-text\">\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\n <div class=\"lp-hero-ctas\">\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\n {{ config.hero.primaryCTA }}\n </button>\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\n {{ config.hero.secondaryCTA }}\n </button>\n </div>\n <!-- Trust signals with checkmark SVG icons -->\n <div class=\"lp-trust-signals\">\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ signal }}</span>\n </div>\n </div>\n </div>\n </div>\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\n <div class=\"lp-hero-visual\">\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\n </div>\n </div>\n</section>\n\n<!-- ===== METRICS (optional) ===== -->\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-metrics-grid\">\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FEATURES ===== -->\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.features.title }}</h2>\n <p>{{ config.features.subtitle }}</p>\n </div>\n <div class=\"lp-features-grid\">\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\n <div class=\"lp-feature-icon\"\n [style.background]=\"iconBg(feature.color)\"\n [style.color]=\"feature.color\">\n <span class=\"material-icons\">{{ feature.icon }}</span>\n </div>\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== WORKFLOW (optional) ===== -->\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.workflow!.title }}</h2>\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\n </div>\n\n <!-- Road metaphor layout: horizontal road line with waypoints -->\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\n <div class=\"lp-road-line\"></div>\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\n <div class=\"lp-waypoint-marker\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\n </div>\n </div>\n\n <!-- Default steps grid layout -->\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\n <div class=\"lp-step-number\">{{ step.number }}</div>\n <div class=\"lp-step-icon\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\n <p class=\"lp-step-desc\">{{ step.description }}</p>\n <!-- Connector line between steps, not after last -->\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== CUSTOM SECTION SLOT ===== -->\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\n using [lpCustomSection] attribute on a wrapper element -->\n<ng-content select=\"[lpCustomSection]\"></ng-content>\n\n<!-- ===== MODULES (optional) ===== -->\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.modules!.title }}</h2>\n <p>{{ config.modules!.subtitle }}</p>\n </div>\n <div class=\"lp-modules-grid\">\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\n <div class=\"lp-module-tiles\">\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\n <span class=\"material-icons\">{{ tile.icon }}</span>\n <span>{{ tile.name }}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header lp-header-light\">\n <h2>{{ config.security!.title }}</h2>\n <p>{{ config.security!.subtitle }}</p>\n </div>\n <div class=\"lp-security-grid\">\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\n <div class=\"lp-security-icon\">\n <span class=\"material-icons\">{{ item.icon }}</span>\n </div>\n <h3>{{ item.title }}</h3>\n <p>{{ item.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== TESTIMONIALS (optional) ===== -->\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.testimonials!.title }}</h2>\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\n </div>\n <div class=\"lp-testimonials-grid\">\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\n <div class=\"lp-stars\">\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n </div>\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\n <div class=\"lp-testimonial-author\">\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\n <div>\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== PRICING (optional) ===== -->\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.pricing!.title }}</h2>\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\n </div>\n <div class=\"lp-pricing-grid\">\n <div *ngFor=\"let plan of config.pricing!.plans\"\n class=\"lp-pricing-card\"\n [class.lp-pricing-popular]=\"plan.popular\">\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\n <div class=\"lp-pricing-header\">\n <h3>{{ plan.name }}</h3>\n <div class=\"lp-pricing-price\">\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\n </div>\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\n </div>\n <ul class=\"lp-pricing-features\">\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\n </ul>\n <!-- Popular plans get primary button, others get outline -->\n <button *ngIf=\"plan.popular\"\n class=\"lp-btn lp-btn-primary lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n <button *ngIf=\"!plan.popular\"\n class=\"lp-btn lp-btn-outline lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\n <div class=\"lp-container lp-cta-content\">\n <h2>{{ config.finalCta!.headline }}</h2>\n <p>{{ config.finalCta!.subtitle }}</p>\n <div class=\"lp-cta-buttons\">\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\n {{ config.finalCta!.primaryCTA }}\n </button>\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\n {{ config.finalCta!.secondaryCTA }}\n </button>\n </div>\n </div>\n</section>\n\n<!-- ===== FOOTER ===== -->\n<footer class=\"lp-footer\" id=\"lp-footer\">\n <div class=\"lp-container\">\n\n <!-- Rich multi-column footer when footerColumns provided -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\n <!-- Brand column -->\n <div class=\"lp-footer-brand-col\">\n <!-- Custom SVG footer brand OR default img+name -->\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\n <div class=\"lp-footer-brand-custom\">\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\n </div>\n </div>\n </ng-container>\n <ng-template #defaultFooterBrand>\n <div class=\"lp-footer-brand-default\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n </ng-template>\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\n </div>\n <!-- Link columns -->\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\n <h4>{{ col.title }}</h4>\n <a *ngFor=\"let link of col.links\"\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\n {{ link.label }}\n </a>\n </div>\n </div>\n\n <!-- Simple single-row footer (default) -->\n <ng-template #simpleFooter>\n <div class=\"lp-footer-inner\">\n <div class=\"lp-footer-brand\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-footer-links\">\n <a (click)=\"navigateTo('login')\">Log in</a>\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\n </div>\n <div class=\"lp-footer-copy\">\n © {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\n </div>\n </div>\n </ng-template>\n\n <!-- Footer bottom bar (shown for both layouts) -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\n <span>© {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\n </div>\n </div>\n</footer>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
|
|
17244
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SpaLandingComponent, isStandalone: false, selector: "spa-landing", inputs: { config: "config" }, host: { listeners: { "window:scroll": "onWindowScroll()" } }, ngImport: i0, template: "<!-- ===== NAVBAR ===== -->\r\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\r\n <div class=\"lp-container lp-nav-inner\">\r\n <!-- Brand: custom SVG override OR default img+name -->\r\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\r\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\r\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\r\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\r\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultNavBrand>\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\r\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\r\n </ng-template>\r\n </div>\r\n <!-- Desktop nav links -->\r\n <div class=\"lp-nav-links\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n </div>\r\n <!-- Mobile menu overlay -->\r\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n <div class=\"lp-mobile-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n </div>\r\n <!-- Desktop auth -->\r\n <div class=\"lp-nav-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n <!-- Hamburger for mobile -->\r\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\r\n <span></span><span></span><span></span>\r\n </button>\r\n </div>\r\n</nav>\r\n\r\n<!-- ===== HERO ===== -->\r\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\r\n <!-- Optional faded watermark SVG (e.g. company logo) -->\r\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\r\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\r\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\r\n </div>\r\n\r\n <div class=\"lp-container lp-hero-content\">\r\n <!-- Left / centered column: brand, headline, CTAs -->\r\n <div class=\"lp-hero-main\">\r\n <!-- App brand: logo + name + decorative divider -->\r\n <div class=\"lp-hero-brand\">\r\n <div class=\"lp-hero-brand-logo\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\r\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\r\n <span class=\"lp-hero-divider-line\"></span>\r\n <span class=\"lp-hero-divider-dot\"></span>\r\n <span class=\"lp-hero-divider-line\"></span>\r\n </div>\r\n </div>\r\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\r\n <div class=\"lp-hero-text\">\r\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\r\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\r\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\r\n <div class=\"lp-hero-ctas\">\r\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\r\n {{ config.hero.primaryCTA }}\r\n </button>\r\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\r\n {{ config.hero.secondaryCTA }}\r\n </button>\r\n </div>\r\n <!-- Trust signals with checkmark SVG icons -->\r\n <div class=\"lp-trust-signals\">\r\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n <span>{{ signal }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\r\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\r\n <div class=\"lp-hero-visual\">\r\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== METRICS (optional) ===== -->\r\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-metrics-grid\">\r\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\r\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\r\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FEATURES ===== -->\r\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.features.title }}</h2>\r\n <p>{{ config.features.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-features-grid\">\r\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\r\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\r\n <div class=\"lp-feature-icon\"\r\n [style.background]=\"iconBg(feature.color)\"\r\n [style.color]=\"feature.color\">\r\n <span class=\"material-icons\">{{ feature.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\r\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== WORKFLOW (optional) ===== -->\r\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.workflow!.title }}</h2>\r\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\r\n </div>\r\n\r\n <!-- Road metaphor layout: horizontal road line with waypoints -->\r\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\r\n <div class=\"lp-road-line\"></div>\r\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\r\n <div class=\"lp-waypoint-marker\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\r\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\r\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default steps grid layout -->\r\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\r\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\r\n <div class=\"lp-step-number\">{{ step.number }}</div>\r\n <div class=\"lp-step-icon\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\r\n <p class=\"lp-step-desc\">{{ step.description }}</p>\r\n <!-- Connector line between steps, not after last -->\r\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== CUSTOM SECTION SLOT ===== -->\r\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\r\n using [lpCustomSection] attribute on a wrapper element -->\r\n<ng-content select=\"[lpCustomSection]\"></ng-content>\r\n\r\n<!-- ===== MODULES (optional) ===== -->\r\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.modules!.title }}</h2>\r\n <p>{{ config.modules!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-modules-grid\">\r\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\r\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\r\n <div class=\"lp-module-tiles\">\r\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\r\n <span class=\"material-icons\">{{ tile.icon }}</span>\r\n <span>{{ tile.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\r\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header lp-header-light\">\r\n <h2>{{ config.security!.title }}</h2>\r\n <p>{{ config.security!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-security-grid\">\r\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\r\n <div class=\"lp-security-icon\">\r\n <span class=\"material-icons\">{{ item.icon }}</span>\r\n </div>\r\n <h3>{{ item.title }}</h3>\r\n <p>{{ item.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== TESTIMONIALS (optional) ===== -->\r\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.testimonials!.title }}</h2>\r\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-testimonials-grid\">\r\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\r\n <div class=\"lp-stars\">\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n </div>\r\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\r\n <div class=\"lp-testimonial-author\">\r\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\r\n <div>\r\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\r\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== PRICING (optional) ===== -->\r\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.pricing!.title }}</h2>\r\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-pricing-grid\">\r\n <div *ngFor=\"let plan of config.pricing!.plans\"\r\n class=\"lp-pricing-card\"\r\n [class.lp-pricing-popular]=\"plan.popular\">\r\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\r\n <div class=\"lp-pricing-header\">\r\n <h3>{{ plan.name }}</h3>\r\n <div class=\"lp-pricing-price\">\r\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\r\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\r\n </div>\r\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\r\n </div>\r\n <ul class=\"lp-pricing-features\">\r\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\r\n </ul>\r\n <!-- Popular plans get primary button, others get outline -->\r\n <button *ngIf=\"plan.popular\"\r\n class=\"lp-btn lp-btn-primary lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n <button *ngIf=\"!plan.popular\"\r\n class=\"lp-btn lp-btn-outline lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\r\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\r\n <div class=\"lp-container lp-cta-content\">\r\n <h2>{{ config.finalCta!.headline }}</h2>\r\n <p>{{ config.finalCta!.subtitle }}</p>\r\n <div class=\"lp-cta-buttons\">\r\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\r\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\r\n {{ config.finalCta!.primaryCTA }}\r\n </button>\r\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\r\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\r\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\r\n {{ config.finalCta!.secondaryCTA }}\r\n </button>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FOOTER ===== -->\r\n<footer class=\"lp-footer\" id=\"lp-footer\">\r\n <div class=\"lp-container\">\r\n\r\n <!-- Rich multi-column footer when footerColumns provided -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\r\n <!-- Brand column -->\r\n <div class=\"lp-footer-brand-col\">\r\n <!-- Custom SVG footer brand OR default img+name -->\r\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\r\n <div class=\"lp-footer-brand-custom\">\r\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\r\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\r\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultFooterBrand>\r\n <div class=\"lp-footer-brand-default\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n </ng-template>\r\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\r\n </div>\r\n <!-- Link columns -->\r\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\r\n <h4>{{ col.title }}</h4>\r\n <a *ngFor=\"let link of col.links\"\r\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\r\n {{ link.label }}\r\n </a>\r\n </div>\r\n </div>\r\n\r\n <!-- Simple single-row footer (default) -->\r\n <ng-template #simpleFooter>\r\n <div class=\"lp-footer-inner\">\r\n <div class=\"lp-footer-brand\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-footer-links\">\r\n <a (click)=\"navigateTo('login')\">Log in</a>\r\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\r\n </div>\r\n <div class=\"lp-footer-copy\">\r\n © {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Footer bottom bar (shown for both layouts) -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\r\n <span>© {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\r\n </div>\r\n </div>\r\n</footer>\r\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
|
|
17245
17245
|
}
|
|
17246
17246
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SpaLandingComponent, decorators: [{
|
|
17247
17247
|
type: Component,
|
|
17248
|
-
args: [{ selector: 'spa-landing', standalone: false, template: "<!-- ===== NAVBAR ===== -->\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\n <div class=\"lp-container lp-nav-inner\">\n <!-- Brand: custom SVG override OR default img+name -->\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\n </div>\n </ng-container>\n <ng-template #defaultNavBrand>\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\n </ng-template>\n </div>\n <!-- Desktop nav links -->\n <div class=\"lp-nav-links\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n </div>\n <!-- Mobile menu overlay -->\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n <div class=\"lp-mobile-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n </div>\n <!-- Desktop auth -->\n <div class=\"lp-nav-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n <!-- Hamburger for mobile -->\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\n <span></span><span></span><span></span>\n </button>\n </div>\n</nav>\n\n<!-- ===== HERO ===== -->\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\n <!-- Optional faded watermark SVG (e.g. company logo) -->\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\n </div>\n\n <div class=\"lp-container lp-hero-content\">\n <!-- Left / centered column: brand, headline, CTAs -->\n <div class=\"lp-hero-main\">\n <!-- App brand: logo + name + decorative divider -->\n <div class=\"lp-hero-brand\">\n <div class=\"lp-hero-brand-logo\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\n <span class=\"lp-hero-divider-line\"></span>\n <span class=\"lp-hero-divider-dot\"></span>\n <span class=\"lp-hero-divider-line\"></span>\n </div>\n </div>\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\n <div class=\"lp-hero-text\">\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\n <div class=\"lp-hero-ctas\">\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\n {{ config.hero.primaryCTA }}\n </button>\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\n {{ config.hero.secondaryCTA }}\n </button>\n </div>\n <!-- Trust signals with checkmark SVG icons -->\n <div class=\"lp-trust-signals\">\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ signal }}</span>\n </div>\n </div>\n </div>\n </div>\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\n <div class=\"lp-hero-visual\">\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\n </div>\n </div>\n</section>\n\n<!-- ===== METRICS (optional) ===== -->\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-metrics-grid\">\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FEATURES ===== -->\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.features.title }}</h2>\n <p>{{ config.features.subtitle }}</p>\n </div>\n <div class=\"lp-features-grid\">\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\n <div class=\"lp-feature-icon\"\n [style.background]=\"iconBg(feature.color)\"\n [style.color]=\"feature.color\">\n <span class=\"material-icons\">{{ feature.icon }}</span>\n </div>\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== WORKFLOW (optional) ===== -->\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.workflow!.title }}</h2>\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\n </div>\n\n <!-- Road metaphor layout: horizontal road line with waypoints -->\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\n <div class=\"lp-road-line\"></div>\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\n <div class=\"lp-waypoint-marker\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\n </div>\n </div>\n\n <!-- Default steps grid layout -->\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\n <div class=\"lp-step-number\">{{ step.number }}</div>\n <div class=\"lp-step-icon\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\n <p class=\"lp-step-desc\">{{ step.description }}</p>\n <!-- Connector line between steps, not after last -->\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== CUSTOM SECTION SLOT ===== -->\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\n using [lpCustomSection] attribute on a wrapper element -->\n<ng-content select=\"[lpCustomSection]\"></ng-content>\n\n<!-- ===== MODULES (optional) ===== -->\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.modules!.title }}</h2>\n <p>{{ config.modules!.subtitle }}</p>\n </div>\n <div class=\"lp-modules-grid\">\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\n <div class=\"lp-module-tiles\">\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\n <span class=\"material-icons\">{{ tile.icon }}</span>\n <span>{{ tile.name }}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header lp-header-light\">\n <h2>{{ config.security!.title }}</h2>\n <p>{{ config.security!.subtitle }}</p>\n </div>\n <div class=\"lp-security-grid\">\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\n <div class=\"lp-security-icon\">\n <span class=\"material-icons\">{{ item.icon }}</span>\n </div>\n <h3>{{ item.title }}</h3>\n <p>{{ item.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== TESTIMONIALS (optional) ===== -->\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.testimonials!.title }}</h2>\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\n </div>\n <div class=\"lp-testimonials-grid\">\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\n <div class=\"lp-stars\">\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n </div>\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\n <div class=\"lp-testimonial-author\">\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\n <div>\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== PRICING (optional) ===== -->\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.pricing!.title }}</h2>\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\n </div>\n <div class=\"lp-pricing-grid\">\n <div *ngFor=\"let plan of config.pricing!.plans\"\n class=\"lp-pricing-card\"\n [class.lp-pricing-popular]=\"plan.popular\">\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\n <div class=\"lp-pricing-header\">\n <h3>{{ plan.name }}</h3>\n <div class=\"lp-pricing-price\">\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\n </div>\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\n </div>\n <ul class=\"lp-pricing-features\">\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\n </ul>\n <!-- Popular plans get primary button, others get outline -->\n <button *ngIf=\"plan.popular\"\n class=\"lp-btn lp-btn-primary lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n <button *ngIf=\"!plan.popular\"\n class=\"lp-btn lp-btn-outline lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\n <div class=\"lp-container lp-cta-content\">\n <h2>{{ config.finalCta!.headline }}</h2>\n <p>{{ config.finalCta!.subtitle }}</p>\n <div class=\"lp-cta-buttons\">\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\n {{ config.finalCta!.primaryCTA }}\n </button>\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\n {{ config.finalCta!.secondaryCTA }}\n </button>\n </div>\n </div>\n</section>\n\n<!-- ===== FOOTER ===== -->\n<footer class=\"lp-footer\" id=\"lp-footer\">\n <div class=\"lp-container\">\n\n <!-- Rich multi-column footer when footerColumns provided -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\n <!-- Brand column -->\n <div class=\"lp-footer-brand-col\">\n <!-- Custom SVG footer brand OR default img+name -->\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\n <div class=\"lp-footer-brand-custom\">\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\n </div>\n </div>\n </ng-container>\n <ng-template #defaultFooterBrand>\n <div class=\"lp-footer-brand-default\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n </ng-template>\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\n </div>\n <!-- Link columns -->\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\n <h4>{{ col.title }}</h4>\n <a *ngFor=\"let link of col.links\"\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\n {{ link.label }}\n </a>\n </div>\n </div>\n\n <!-- Simple single-row footer (default) -->\n <ng-template #simpleFooter>\n <div class=\"lp-footer-inner\">\n <div class=\"lp-footer-brand\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-footer-links\">\n <a (click)=\"navigateTo('login')\">Log in</a>\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\n </div>\n <div class=\"lp-footer-copy\">\n © {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\n </div>\n </div>\n </ng-template>\n\n <!-- Footer bottom bar (shown for both layouts) -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\n <span>© {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\n </div>\n </div>\n</footer>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"] }]
|
|
17248
|
+
args: [{ selector: 'spa-landing', standalone: false, template: "<!-- ===== NAVBAR ===== -->\r\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\r\n <div class=\"lp-container lp-nav-inner\">\r\n <!-- Brand: custom SVG override OR default img+name -->\r\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\r\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\r\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\r\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\r\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultNavBrand>\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\r\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\r\n </ng-template>\r\n </div>\r\n <!-- Desktop nav links -->\r\n <div class=\"lp-nav-links\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n </div>\r\n <!-- Mobile menu overlay -->\r\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n <div class=\"lp-mobile-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n </div>\r\n <!-- Desktop auth -->\r\n <div class=\"lp-nav-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n <!-- Hamburger for mobile -->\r\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\r\n <span></span><span></span><span></span>\r\n </button>\r\n </div>\r\n</nav>\r\n\r\n<!-- ===== HERO ===== -->\r\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\r\n <!-- Optional faded watermark SVG (e.g. company logo) -->\r\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\r\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\r\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\r\n </div>\r\n\r\n <div class=\"lp-container lp-hero-content\">\r\n <!-- Left / centered column: brand, headline, CTAs -->\r\n <div class=\"lp-hero-main\">\r\n <!-- App brand: logo + name + decorative divider -->\r\n <div class=\"lp-hero-brand\">\r\n <div class=\"lp-hero-brand-logo\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\r\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\r\n <span class=\"lp-hero-divider-line\"></span>\r\n <span class=\"lp-hero-divider-dot\"></span>\r\n <span class=\"lp-hero-divider-line\"></span>\r\n </div>\r\n </div>\r\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\r\n <div class=\"lp-hero-text\">\r\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\r\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\r\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\r\n <div class=\"lp-hero-ctas\">\r\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\r\n {{ config.hero.primaryCTA }}\r\n </button>\r\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\r\n {{ config.hero.secondaryCTA }}\r\n </button>\r\n </div>\r\n <!-- Trust signals with checkmark SVG icons -->\r\n <div class=\"lp-trust-signals\">\r\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n <span>{{ signal }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\r\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\r\n <div class=\"lp-hero-visual\">\r\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== METRICS (optional) ===== -->\r\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-metrics-grid\">\r\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\r\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\r\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FEATURES ===== -->\r\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.features.title }}</h2>\r\n <p>{{ config.features.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-features-grid\">\r\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\r\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\r\n <div class=\"lp-feature-icon\"\r\n [style.background]=\"iconBg(feature.color)\"\r\n [style.color]=\"feature.color\">\r\n <span class=\"material-icons\">{{ feature.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\r\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== WORKFLOW (optional) ===== -->\r\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.workflow!.title }}</h2>\r\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\r\n </div>\r\n\r\n <!-- Road metaphor layout: horizontal road line with waypoints -->\r\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\r\n <div class=\"lp-road-line\"></div>\r\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\r\n <div class=\"lp-waypoint-marker\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\r\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\r\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default steps grid layout -->\r\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\r\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\r\n <div class=\"lp-step-number\">{{ step.number }}</div>\r\n <div class=\"lp-step-icon\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\r\n <p class=\"lp-step-desc\">{{ step.description }}</p>\r\n <!-- Connector line between steps, not after last -->\r\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== CUSTOM SECTION SLOT ===== -->\r\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\r\n using [lpCustomSection] attribute on a wrapper element -->\r\n<ng-content select=\"[lpCustomSection]\"></ng-content>\r\n\r\n<!-- ===== MODULES (optional) ===== -->\r\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.modules!.title }}</h2>\r\n <p>{{ config.modules!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-modules-grid\">\r\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\r\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\r\n <div class=\"lp-module-tiles\">\r\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\r\n <span class=\"material-icons\">{{ tile.icon }}</span>\r\n <span>{{ tile.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\r\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header lp-header-light\">\r\n <h2>{{ config.security!.title }}</h2>\r\n <p>{{ config.security!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-security-grid\">\r\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\r\n <div class=\"lp-security-icon\">\r\n <span class=\"material-icons\">{{ item.icon }}</span>\r\n </div>\r\n <h3>{{ item.title }}</h3>\r\n <p>{{ item.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== TESTIMONIALS (optional) ===== -->\r\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.testimonials!.title }}</h2>\r\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-testimonials-grid\">\r\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\r\n <div class=\"lp-stars\">\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n </div>\r\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\r\n <div class=\"lp-testimonial-author\">\r\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\r\n <div>\r\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\r\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== PRICING (optional) ===== -->\r\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.pricing!.title }}</h2>\r\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-pricing-grid\">\r\n <div *ngFor=\"let plan of config.pricing!.plans\"\r\n class=\"lp-pricing-card\"\r\n [class.lp-pricing-popular]=\"plan.popular\">\r\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\r\n <div class=\"lp-pricing-header\">\r\n <h3>{{ plan.name }}</h3>\r\n <div class=\"lp-pricing-price\">\r\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\r\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\r\n </div>\r\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\r\n </div>\r\n <ul class=\"lp-pricing-features\">\r\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\r\n </ul>\r\n <!-- Popular plans get primary button, others get outline -->\r\n <button *ngIf=\"plan.popular\"\r\n class=\"lp-btn lp-btn-primary lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n <button *ngIf=\"!plan.popular\"\r\n class=\"lp-btn lp-btn-outline lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\r\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\r\n <div class=\"lp-container lp-cta-content\">\r\n <h2>{{ config.finalCta!.headline }}</h2>\r\n <p>{{ config.finalCta!.subtitle }}</p>\r\n <div class=\"lp-cta-buttons\">\r\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\r\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\r\n {{ config.finalCta!.primaryCTA }}\r\n </button>\r\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\r\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\r\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\r\n {{ config.finalCta!.secondaryCTA }}\r\n </button>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FOOTER ===== -->\r\n<footer class=\"lp-footer\" id=\"lp-footer\">\r\n <div class=\"lp-container\">\r\n\r\n <!-- Rich multi-column footer when footerColumns provided -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\r\n <!-- Brand column -->\r\n <div class=\"lp-footer-brand-col\">\r\n <!-- Custom SVG footer brand OR default img+name -->\r\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\r\n <div class=\"lp-footer-brand-custom\">\r\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\r\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\r\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultFooterBrand>\r\n <div class=\"lp-footer-brand-default\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n </ng-template>\r\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\r\n </div>\r\n <!-- Link columns -->\r\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\r\n <h4>{{ col.title }}</h4>\r\n <a *ngFor=\"let link of col.links\"\r\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\r\n {{ link.label }}\r\n </a>\r\n </div>\r\n </div>\r\n\r\n <!-- Simple single-row footer (default) -->\r\n <ng-template #simpleFooter>\r\n <div class=\"lp-footer-inner\">\r\n <div class=\"lp-footer-brand\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-footer-links\">\r\n <a (click)=\"navigateTo('login')\">Log in</a>\r\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\r\n </div>\r\n <div class=\"lp-footer-copy\">\r\n © {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Footer bottom bar (shown for both layouts) -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\r\n <span>© {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\r\n </div>\r\n </div>\r\n</footer>\r\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"] }]
|
|
17249
17249
|
}], ctorParameters: () => [{ type: i1$2.Router }, { type: AuthService }, { type: LastRouteService }, { type: i0.ElementRef }, { type: i3$2.DomSanitizer }], propDecorators: { config: [{
|
|
17250
17250
|
type: Input
|
|
17251
17251
|
}], onWindowScroll: [{
|
|
@@ -19788,150 +19788,150 @@ class JournalEntryDialogComponent {
|
|
|
19788
19788
|
});
|
|
19789
19789
|
}
|
|
19790
19790
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: JournalEntryDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19791
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: JournalEntryDialogComponent, isStandalone: false, selector: "spa-journal-lines", ngImport: i0, template: `
|
|
19792
|
-
<h2 mat-dialog-title>New Journal Entry</h2>
|
|
19793
|
-
<mat-dialog-content>
|
|
19794
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
19795
|
-
<mat-form-field>
|
|
19796
|
-
<mat-label>Date</mat-label>
|
|
19797
|
-
<input matInput type="date" [(ngModel)]="date">
|
|
19798
|
-
</mat-form-field>
|
|
19799
|
-
<mat-form-field style="flex: 1; min-width: 260px;">
|
|
19800
|
-
<mat-label>Description</mat-label>
|
|
19801
|
-
<input matInput [(ngModel)]="description" placeholder="Enter journal description">
|
|
19802
|
-
</mat-form-field>
|
|
19803
|
-
<mat-form-field>
|
|
19804
|
-
<mat-label>Journal Type</mat-label>
|
|
19805
|
-
<mat-select [(ngModel)]="journalType">
|
|
19806
|
-
<mat-option value="Journal Entry">Journal Entry</mat-option>
|
|
19807
|
-
<mat-option value="Opening Balance">Opening Balance</mat-option>
|
|
19808
|
-
</mat-select>
|
|
19809
|
-
</mat-form-field>
|
|
19810
|
-
</div>
|
|
19811
|
-
|
|
19812
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
19813
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
19814
|
-
<th style="padding: 4px;">Account</th>
|
|
19815
|
-
<th style="padding: 4px; width: 140px;">Debit</th>
|
|
19816
|
-
<th style="padding: 4px; width: 140px;">Credit</th>
|
|
19817
|
-
<th style="width: 48px;"></th>
|
|
19818
|
-
</tr>
|
|
19819
|
-
<tr *ngFor="let line of lines; let i = index">
|
|
19820
|
-
<td style="padding: 4px;">
|
|
19821
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19822
|
-
<mat-select [(ngModel)]="line.accountID" placeholder="Select account">
|
|
19823
|
-
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
19824
|
-
</mat-select>
|
|
19825
|
-
</mat-form-field>
|
|
19826
|
-
</td>
|
|
19827
|
-
<td style="padding: 4px;">
|
|
19828
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19829
|
-
<input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
|
|
19830
|
-
</mat-form-field>
|
|
19831
|
-
</td>
|
|
19832
|
-
<td style="padding: 4px;">
|
|
19833
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19834
|
-
<input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
|
|
19835
|
-
</mat-form-field>
|
|
19836
|
-
</td>
|
|
19837
|
-
<td>
|
|
19838
|
-
<button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
|
|
19839
|
-
<mat-icon>delete</mat-icon>
|
|
19840
|
-
</button>
|
|
19841
|
-
</td>
|
|
19842
|
-
</tr>
|
|
19843
|
-
<tr style="border-top: 2px solid #333;">
|
|
19844
|
-
<td style="padding: 8px 4px;">
|
|
19845
|
-
<button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
|
|
19846
|
-
</td>
|
|
19847
|
-
<td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
|
|
19848
|
-
<td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
|
|
19849
|
-
<td></td>
|
|
19850
|
-
</tr>
|
|
19851
|
-
</table>
|
|
19852
|
-
<p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
|
|
19853
|
-
<em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
|
|
19854
|
-
</p>
|
|
19855
|
-
</mat-dialog-content>
|
|
19856
|
-
<mat-dialog-actions align="end">
|
|
19857
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
19858
|
-
<button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
|
|
19859
|
-
</mat-dialog-actions>
|
|
19791
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: JournalEntryDialogComponent, isStandalone: false, selector: "spa-journal-lines", ngImport: i0, template: `
|
|
19792
|
+
<h2 mat-dialog-title>New Journal Entry</h2>
|
|
19793
|
+
<mat-dialog-content>
|
|
19794
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
19795
|
+
<mat-form-field>
|
|
19796
|
+
<mat-label>Date</mat-label>
|
|
19797
|
+
<input matInput type="date" [(ngModel)]="date">
|
|
19798
|
+
</mat-form-field>
|
|
19799
|
+
<mat-form-field style="flex: 1; min-width: 260px;">
|
|
19800
|
+
<mat-label>Description</mat-label>
|
|
19801
|
+
<input matInput [(ngModel)]="description" placeholder="Enter journal description">
|
|
19802
|
+
</mat-form-field>
|
|
19803
|
+
<mat-form-field>
|
|
19804
|
+
<mat-label>Journal Type</mat-label>
|
|
19805
|
+
<mat-select [(ngModel)]="journalType">
|
|
19806
|
+
<mat-option value="Journal Entry">Journal Entry</mat-option>
|
|
19807
|
+
<mat-option value="Opening Balance">Opening Balance</mat-option>
|
|
19808
|
+
</mat-select>
|
|
19809
|
+
</mat-form-field>
|
|
19810
|
+
</div>
|
|
19811
|
+
|
|
19812
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
19813
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
19814
|
+
<th style="padding: 4px;">Account</th>
|
|
19815
|
+
<th style="padding: 4px; width: 140px;">Debit</th>
|
|
19816
|
+
<th style="padding: 4px; width: 140px;">Credit</th>
|
|
19817
|
+
<th style="width: 48px;"></th>
|
|
19818
|
+
</tr>
|
|
19819
|
+
<tr *ngFor="let line of lines; let i = index">
|
|
19820
|
+
<td style="padding: 4px;">
|
|
19821
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19822
|
+
<mat-select [(ngModel)]="line.accountID" placeholder="Select account">
|
|
19823
|
+
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
19824
|
+
</mat-select>
|
|
19825
|
+
</mat-form-field>
|
|
19826
|
+
</td>
|
|
19827
|
+
<td style="padding: 4px;">
|
|
19828
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19829
|
+
<input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
|
|
19830
|
+
</mat-form-field>
|
|
19831
|
+
</td>
|
|
19832
|
+
<td style="padding: 4px;">
|
|
19833
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19834
|
+
<input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
|
|
19835
|
+
</mat-form-field>
|
|
19836
|
+
</td>
|
|
19837
|
+
<td>
|
|
19838
|
+
<button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
|
|
19839
|
+
<mat-icon>delete</mat-icon>
|
|
19840
|
+
</button>
|
|
19841
|
+
</td>
|
|
19842
|
+
</tr>
|
|
19843
|
+
<tr style="border-top: 2px solid #333;">
|
|
19844
|
+
<td style="padding: 8px 4px;">
|
|
19845
|
+
<button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
|
|
19846
|
+
</td>
|
|
19847
|
+
<td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
|
|
19848
|
+
<td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
|
|
19849
|
+
<td></td>
|
|
19850
|
+
</tr>
|
|
19851
|
+
</table>
|
|
19852
|
+
<p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
|
|
19853
|
+
<em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
|
|
19854
|
+
</p>
|
|
19855
|
+
</mat-dialog-content>
|
|
19856
|
+
<mat-dialog-actions align="end">
|
|
19857
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
19858
|
+
<button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
|
|
19859
|
+
</mat-dialog-actions>
|
|
19860
19860
|
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
19861
19861
|
}
|
|
19862
19862
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: JournalEntryDialogComponent, decorators: [{
|
|
19863
19863
|
type: Component,
|
|
19864
19864
|
args: [{
|
|
19865
19865
|
selector: 'spa-journal-lines',
|
|
19866
|
-
template: `
|
|
19867
|
-
<h2 mat-dialog-title>New Journal Entry</h2>
|
|
19868
|
-
<mat-dialog-content>
|
|
19869
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
19870
|
-
<mat-form-field>
|
|
19871
|
-
<mat-label>Date</mat-label>
|
|
19872
|
-
<input matInput type="date" [(ngModel)]="date">
|
|
19873
|
-
</mat-form-field>
|
|
19874
|
-
<mat-form-field style="flex: 1; min-width: 260px;">
|
|
19875
|
-
<mat-label>Description</mat-label>
|
|
19876
|
-
<input matInput [(ngModel)]="description" placeholder="Enter journal description">
|
|
19877
|
-
</mat-form-field>
|
|
19878
|
-
<mat-form-field>
|
|
19879
|
-
<mat-label>Journal Type</mat-label>
|
|
19880
|
-
<mat-select [(ngModel)]="journalType">
|
|
19881
|
-
<mat-option value="Journal Entry">Journal Entry</mat-option>
|
|
19882
|
-
<mat-option value="Opening Balance">Opening Balance</mat-option>
|
|
19883
|
-
</mat-select>
|
|
19884
|
-
</mat-form-field>
|
|
19885
|
-
</div>
|
|
19886
|
-
|
|
19887
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
19888
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
19889
|
-
<th style="padding: 4px;">Account</th>
|
|
19890
|
-
<th style="padding: 4px; width: 140px;">Debit</th>
|
|
19891
|
-
<th style="padding: 4px; width: 140px;">Credit</th>
|
|
19892
|
-
<th style="width: 48px;"></th>
|
|
19893
|
-
</tr>
|
|
19894
|
-
<tr *ngFor="let line of lines; let i = index">
|
|
19895
|
-
<td style="padding: 4px;">
|
|
19896
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19897
|
-
<mat-select [(ngModel)]="line.accountID" placeholder="Select account">
|
|
19898
|
-
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
19899
|
-
</mat-select>
|
|
19900
|
-
</mat-form-field>
|
|
19901
|
-
</td>
|
|
19902
|
-
<td style="padding: 4px;">
|
|
19903
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19904
|
-
<input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
|
|
19905
|
-
</mat-form-field>
|
|
19906
|
-
</td>
|
|
19907
|
-
<td style="padding: 4px;">
|
|
19908
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19909
|
-
<input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
|
|
19910
|
-
</mat-form-field>
|
|
19911
|
-
</td>
|
|
19912
|
-
<td>
|
|
19913
|
-
<button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
|
|
19914
|
-
<mat-icon>delete</mat-icon>
|
|
19915
|
-
</button>
|
|
19916
|
-
</td>
|
|
19917
|
-
</tr>
|
|
19918
|
-
<tr style="border-top: 2px solid #333;">
|
|
19919
|
-
<td style="padding: 8px 4px;">
|
|
19920
|
-
<button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
|
|
19921
|
-
</td>
|
|
19922
|
-
<td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
|
|
19923
|
-
<td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
|
|
19924
|
-
<td></td>
|
|
19925
|
-
</tr>
|
|
19926
|
-
</table>
|
|
19927
|
-
<p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
|
|
19928
|
-
<em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
|
|
19929
|
-
</p>
|
|
19930
|
-
</mat-dialog-content>
|
|
19931
|
-
<mat-dialog-actions align="end">
|
|
19932
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
19933
|
-
<button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
|
|
19934
|
-
</mat-dialog-actions>
|
|
19866
|
+
template: `
|
|
19867
|
+
<h2 mat-dialog-title>New Journal Entry</h2>
|
|
19868
|
+
<mat-dialog-content>
|
|
19869
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
19870
|
+
<mat-form-field>
|
|
19871
|
+
<mat-label>Date</mat-label>
|
|
19872
|
+
<input matInput type="date" [(ngModel)]="date">
|
|
19873
|
+
</mat-form-field>
|
|
19874
|
+
<mat-form-field style="flex: 1; min-width: 260px;">
|
|
19875
|
+
<mat-label>Description</mat-label>
|
|
19876
|
+
<input matInput [(ngModel)]="description" placeholder="Enter journal description">
|
|
19877
|
+
</mat-form-field>
|
|
19878
|
+
<mat-form-field>
|
|
19879
|
+
<mat-label>Journal Type</mat-label>
|
|
19880
|
+
<mat-select [(ngModel)]="journalType">
|
|
19881
|
+
<mat-option value="Journal Entry">Journal Entry</mat-option>
|
|
19882
|
+
<mat-option value="Opening Balance">Opening Balance</mat-option>
|
|
19883
|
+
</mat-select>
|
|
19884
|
+
</mat-form-field>
|
|
19885
|
+
</div>
|
|
19886
|
+
|
|
19887
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
19888
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
19889
|
+
<th style="padding: 4px;">Account</th>
|
|
19890
|
+
<th style="padding: 4px; width: 140px;">Debit</th>
|
|
19891
|
+
<th style="padding: 4px; width: 140px;">Credit</th>
|
|
19892
|
+
<th style="width: 48px;"></th>
|
|
19893
|
+
</tr>
|
|
19894
|
+
<tr *ngFor="let line of lines; let i = index">
|
|
19895
|
+
<td style="padding: 4px;">
|
|
19896
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19897
|
+
<mat-select [(ngModel)]="line.accountID" placeholder="Select account">
|
|
19898
|
+
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
19899
|
+
</mat-select>
|
|
19900
|
+
</mat-form-field>
|
|
19901
|
+
</td>
|
|
19902
|
+
<td style="padding: 4px;">
|
|
19903
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19904
|
+
<input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
|
|
19905
|
+
</mat-form-field>
|
|
19906
|
+
</td>
|
|
19907
|
+
<td style="padding: 4px;">
|
|
19908
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
19909
|
+
<input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
|
|
19910
|
+
</mat-form-field>
|
|
19911
|
+
</td>
|
|
19912
|
+
<td>
|
|
19913
|
+
<button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
|
|
19914
|
+
<mat-icon>delete</mat-icon>
|
|
19915
|
+
</button>
|
|
19916
|
+
</td>
|
|
19917
|
+
</tr>
|
|
19918
|
+
<tr style="border-top: 2px solid #333;">
|
|
19919
|
+
<td style="padding: 8px 4px;">
|
|
19920
|
+
<button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
|
|
19921
|
+
</td>
|
|
19922
|
+
<td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
|
|
19923
|
+
<td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
|
|
19924
|
+
<td></td>
|
|
19925
|
+
</tr>
|
|
19926
|
+
</table>
|
|
19927
|
+
<p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
|
|
19928
|
+
<em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
|
|
19929
|
+
</p>
|
|
19930
|
+
</mat-dialog-content>
|
|
19931
|
+
<mat-dialog-actions align="end">
|
|
19932
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
19933
|
+
<button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
|
|
19934
|
+
</mat-dialog-actions>
|
|
19935
19935
|
`,
|
|
19936
19936
|
standalone: false
|
|
19937
19937
|
}]
|
|
@@ -19957,26 +19957,26 @@ class TransactionsComponent {
|
|
|
19957
19957
|
this.page?.tableReload.next(true); });
|
|
19958
19958
|
}
|
|
19959
19959
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TransactionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
19960
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TransactionsComponent, isStandalone: false, selector: "spa-transactions", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
19961
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
19962
|
-
<button mat-stroked-button (click)="newJournal()">
|
|
19963
|
-
<mat-icon>library_books</mat-icon> New Journal
|
|
19964
|
-
</button>
|
|
19965
|
-
</div>
|
|
19966
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
19960
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TransactionsComponent, isStandalone: false, selector: "spa-transactions", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
19961
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
19962
|
+
<button mat-stroked-button (click)="newJournal()">
|
|
19963
|
+
<mat-icon>library_books</mat-icon> New Journal
|
|
19964
|
+
</button>
|
|
19965
|
+
</div>
|
|
19966
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
19967
19967
|
`, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
|
|
19968
19968
|
}
|
|
19969
19969
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TransactionsComponent, decorators: [{
|
|
19970
19970
|
type: Component,
|
|
19971
19971
|
args: [{
|
|
19972
19972
|
selector: 'spa-transactions',
|
|
19973
|
-
template: `
|
|
19974
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
19975
|
-
<button mat-stroked-button (click)="newJournal()">
|
|
19976
|
-
<mat-icon>library_books</mat-icon> New Journal
|
|
19977
|
-
</button>
|
|
19978
|
-
</div>
|
|
19979
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
19973
|
+
template: `
|
|
19974
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
19975
|
+
<button mat-stroked-button (click)="newJournal()">
|
|
19976
|
+
<mat-icon>library_books</mat-icon> New Journal
|
|
19977
|
+
</button>
|
|
19978
|
+
</div>
|
|
19979
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
19980
19980
|
`,
|
|
19981
19981
|
standalone: false
|
|
19982
19982
|
}]
|
|
@@ -20099,166 +20099,166 @@ class StatementComponent {
|
|
|
20099
20099
|
});
|
|
20100
20100
|
}
|
|
20101
20101
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
20102
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementComponent, isStandalone: false, selector: "spa-statement", ngImport: i0, template: `
|
|
20103
|
-
<mat-tab-group>
|
|
20104
|
-
<mat-tab label="Invoice History">
|
|
20105
|
-
<div style="padding-top: 8px;">
|
|
20106
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
20107
|
-
</div>
|
|
20108
|
-
</mat-tab>
|
|
20109
|
-
|
|
20110
|
-
<mat-tab label="Customer Statement">
|
|
20111
|
-
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
|
|
20112
|
-
</mat-tab>
|
|
20113
|
-
|
|
20114
|
-
<mat-tab label="Supplier Statement">
|
|
20115
|
-
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
|
|
20116
|
-
</mat-tab>
|
|
20117
|
-
</mat-tab-group>
|
|
20118
|
-
|
|
20119
|
-
<ng-template #statementTab let-kind="kind">
|
|
20120
|
-
<div style="padding: 16px 0;">
|
|
20121
|
-
<div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
|
|
20122
|
-
<mat-form-field style="min-width: 240px;">
|
|
20123
|
-
<mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
|
|
20124
|
-
<mat-select [(ngModel)]="state[kind].entityId">
|
|
20125
|
-
<mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
|
|
20126
|
-
</mat-select>
|
|
20127
|
-
</mat-form-field>
|
|
20128
|
-
<mat-form-field>
|
|
20129
|
-
<mat-label>From</mat-label>
|
|
20130
|
-
<input matInput type="date" [(ngModel)]="state[kind].from">
|
|
20131
|
-
</mat-form-field>
|
|
20132
|
-
<mat-form-field>
|
|
20133
|
-
<mat-label>To</mat-label>
|
|
20134
|
-
<input matInput type="date" [(ngModel)]="state[kind].to">
|
|
20135
|
-
</mat-form-field>
|
|
20136
|
-
<button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
|
|
20137
|
-
<button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
|
|
20138
|
-
<mat-icon>download</mat-icon> Export to Excel
|
|
20139
|
-
</button>
|
|
20140
|
-
<button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
|
|
20141
|
-
<mat-icon>picture_as_pdf</mat-icon> Download PDF
|
|
20142
|
-
</button>
|
|
20143
|
-
</div>
|
|
20144
|
-
|
|
20145
|
-
<mat-card *ngIf="state[kind].data" style="padding: 16px;">
|
|
20146
|
-
<div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
|
|
20147
|
-
<div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
|
|
20148
|
-
<div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
|
|
20149
|
-
<div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
|
|
20150
|
-
<div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
|
|
20151
|
-
</div>
|
|
20152
|
-
|
|
20153
|
-
<table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
|
|
20154
|
-
<tr style="text-align: left; border-bottom: 2px solid #333;">
|
|
20155
|
-
<th style="padding: 6px;">Date</th>
|
|
20156
|
-
<th style="padding: 6px;">Reference</th>
|
|
20157
|
-
<th style="padding: 6px;">Type</th>
|
|
20158
|
-
<th style="padding: 6px; text-align: right;">Total</th>
|
|
20159
|
-
<th style="padding: 6px; text-align: right;">Paid</th>
|
|
20160
|
-
<th style="padding: 6px; text-align: right;">Outstanding</th>
|
|
20161
|
-
<th style="padding: 6px;">Status</th>
|
|
20162
|
-
<th style="padding: 6px;">Aging</th>
|
|
20163
|
-
</tr>
|
|
20164
|
-
<tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
|
|
20165
|
-
<td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
|
|
20166
|
-
<td style="padding: 6px;">{{ line.reference }}</td>
|
|
20167
|
-
<td style="padding: 6px;">{{ line.type }}</td>
|
|
20168
|
-
<td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
|
|
20169
|
-
<td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
|
|
20170
|
-
<td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
|
|
20171
|
-
<td style="padding: 6px;">{{ line.status }}</td>
|
|
20172
|
-
<td style="padding: 6px;">{{ line.agingBucket }}</td>
|
|
20173
|
-
</tr>
|
|
20174
|
-
</table>
|
|
20175
|
-
<ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
|
|
20176
|
-
</mat-card>
|
|
20177
|
-
</div>
|
|
20178
|
-
</ng-template>
|
|
20102
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementComponent, isStandalone: false, selector: "spa-statement", ngImport: i0, template: `
|
|
20103
|
+
<mat-tab-group>
|
|
20104
|
+
<mat-tab label="Invoice History">
|
|
20105
|
+
<div style="padding-top: 8px;">
|
|
20106
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
20107
|
+
</div>
|
|
20108
|
+
</mat-tab>
|
|
20109
|
+
|
|
20110
|
+
<mat-tab label="Customer Statement">
|
|
20111
|
+
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
|
|
20112
|
+
</mat-tab>
|
|
20113
|
+
|
|
20114
|
+
<mat-tab label="Supplier Statement">
|
|
20115
|
+
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
|
|
20116
|
+
</mat-tab>
|
|
20117
|
+
</mat-tab-group>
|
|
20118
|
+
|
|
20119
|
+
<ng-template #statementTab let-kind="kind">
|
|
20120
|
+
<div style="padding: 16px 0;">
|
|
20121
|
+
<div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
|
|
20122
|
+
<mat-form-field style="min-width: 240px;">
|
|
20123
|
+
<mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
|
|
20124
|
+
<mat-select [(ngModel)]="state[kind].entityId">
|
|
20125
|
+
<mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
|
|
20126
|
+
</mat-select>
|
|
20127
|
+
</mat-form-field>
|
|
20128
|
+
<mat-form-field>
|
|
20129
|
+
<mat-label>From</mat-label>
|
|
20130
|
+
<input matInput type="date" [(ngModel)]="state[kind].from">
|
|
20131
|
+
</mat-form-field>
|
|
20132
|
+
<mat-form-field>
|
|
20133
|
+
<mat-label>To</mat-label>
|
|
20134
|
+
<input matInput type="date" [(ngModel)]="state[kind].to">
|
|
20135
|
+
</mat-form-field>
|
|
20136
|
+
<button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
|
|
20137
|
+
<button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
|
|
20138
|
+
<mat-icon>download</mat-icon> Export to Excel
|
|
20139
|
+
</button>
|
|
20140
|
+
<button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
|
|
20141
|
+
<mat-icon>picture_as_pdf</mat-icon> Download PDF
|
|
20142
|
+
</button>
|
|
20143
|
+
</div>
|
|
20144
|
+
|
|
20145
|
+
<mat-card *ngIf="state[kind].data" style="padding: 16px;">
|
|
20146
|
+
<div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
|
|
20147
|
+
<div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
|
|
20148
|
+
<div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
|
|
20149
|
+
<div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
|
|
20150
|
+
<div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
|
|
20151
|
+
</div>
|
|
20152
|
+
|
|
20153
|
+
<table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
|
|
20154
|
+
<tr style="text-align: left; border-bottom: 2px solid #333;">
|
|
20155
|
+
<th style="padding: 6px;">Date</th>
|
|
20156
|
+
<th style="padding: 6px;">Reference</th>
|
|
20157
|
+
<th style="padding: 6px;">Type</th>
|
|
20158
|
+
<th style="padding: 6px; text-align: right;">Total</th>
|
|
20159
|
+
<th style="padding: 6px; text-align: right;">Paid</th>
|
|
20160
|
+
<th style="padding: 6px; text-align: right;">Outstanding</th>
|
|
20161
|
+
<th style="padding: 6px;">Status</th>
|
|
20162
|
+
<th style="padding: 6px;">Aging</th>
|
|
20163
|
+
</tr>
|
|
20164
|
+
<tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
|
|
20165
|
+
<td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
|
|
20166
|
+
<td style="padding: 6px;">{{ line.reference }}</td>
|
|
20167
|
+
<td style="padding: 6px;">{{ line.type }}</td>
|
|
20168
|
+
<td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
|
|
20169
|
+
<td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
|
|
20170
|
+
<td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
|
|
20171
|
+
<td style="padding: 6px;">{{ line.status }}</td>
|
|
20172
|
+
<td style="padding: 6px;">{{ line.agingBucket }}</td>
|
|
20173
|
+
</tr>
|
|
20174
|
+
</table>
|
|
20175
|
+
<ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
|
|
20176
|
+
</mat-card>
|
|
20177
|
+
</div>
|
|
20178
|
+
</ng-template>
|
|
20179
20179
|
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i5$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i5$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i18.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
|
|
20180
20180
|
}
|
|
20181
20181
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementComponent, decorators: [{
|
|
20182
20182
|
type: Component,
|
|
20183
20183
|
args: [{
|
|
20184
20184
|
selector: 'spa-statement',
|
|
20185
|
-
template: `
|
|
20186
|
-
<mat-tab-group>
|
|
20187
|
-
<mat-tab label="Invoice History">
|
|
20188
|
-
<div style="padding-top: 8px;">
|
|
20189
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
20190
|
-
</div>
|
|
20191
|
-
</mat-tab>
|
|
20192
|
-
|
|
20193
|
-
<mat-tab label="Customer Statement">
|
|
20194
|
-
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
|
|
20195
|
-
</mat-tab>
|
|
20196
|
-
|
|
20197
|
-
<mat-tab label="Supplier Statement">
|
|
20198
|
-
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
|
|
20199
|
-
</mat-tab>
|
|
20200
|
-
</mat-tab-group>
|
|
20201
|
-
|
|
20202
|
-
<ng-template #statementTab let-kind="kind">
|
|
20203
|
-
<div style="padding: 16px 0;">
|
|
20204
|
-
<div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
|
|
20205
|
-
<mat-form-field style="min-width: 240px;">
|
|
20206
|
-
<mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
|
|
20207
|
-
<mat-select [(ngModel)]="state[kind].entityId">
|
|
20208
|
-
<mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
|
|
20209
|
-
</mat-select>
|
|
20210
|
-
</mat-form-field>
|
|
20211
|
-
<mat-form-field>
|
|
20212
|
-
<mat-label>From</mat-label>
|
|
20213
|
-
<input matInput type="date" [(ngModel)]="state[kind].from">
|
|
20214
|
-
</mat-form-field>
|
|
20215
|
-
<mat-form-field>
|
|
20216
|
-
<mat-label>To</mat-label>
|
|
20217
|
-
<input matInput type="date" [(ngModel)]="state[kind].to">
|
|
20218
|
-
</mat-form-field>
|
|
20219
|
-
<button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
|
|
20220
|
-
<button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
|
|
20221
|
-
<mat-icon>download</mat-icon> Export to Excel
|
|
20222
|
-
</button>
|
|
20223
|
-
<button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
|
|
20224
|
-
<mat-icon>picture_as_pdf</mat-icon> Download PDF
|
|
20225
|
-
</button>
|
|
20226
|
-
</div>
|
|
20227
|
-
|
|
20228
|
-
<mat-card *ngIf="state[kind].data" style="padding: 16px;">
|
|
20229
|
-
<div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
|
|
20230
|
-
<div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
|
|
20231
|
-
<div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
|
|
20232
|
-
<div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
|
|
20233
|
-
<div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
|
|
20234
|
-
</div>
|
|
20235
|
-
|
|
20236
|
-
<table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
|
|
20237
|
-
<tr style="text-align: left; border-bottom: 2px solid #333;">
|
|
20238
|
-
<th style="padding: 6px;">Date</th>
|
|
20239
|
-
<th style="padding: 6px;">Reference</th>
|
|
20240
|
-
<th style="padding: 6px;">Type</th>
|
|
20241
|
-
<th style="padding: 6px; text-align: right;">Total</th>
|
|
20242
|
-
<th style="padding: 6px; text-align: right;">Paid</th>
|
|
20243
|
-
<th style="padding: 6px; text-align: right;">Outstanding</th>
|
|
20244
|
-
<th style="padding: 6px;">Status</th>
|
|
20245
|
-
<th style="padding: 6px;">Aging</th>
|
|
20246
|
-
</tr>
|
|
20247
|
-
<tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
|
|
20248
|
-
<td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
|
|
20249
|
-
<td style="padding: 6px;">{{ line.reference }}</td>
|
|
20250
|
-
<td style="padding: 6px;">{{ line.type }}</td>
|
|
20251
|
-
<td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
|
|
20252
|
-
<td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
|
|
20253
|
-
<td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
|
|
20254
|
-
<td style="padding: 6px;">{{ line.status }}</td>
|
|
20255
|
-
<td style="padding: 6px;">{{ line.agingBucket }}</td>
|
|
20256
|
-
</tr>
|
|
20257
|
-
</table>
|
|
20258
|
-
<ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
|
|
20259
|
-
</mat-card>
|
|
20260
|
-
</div>
|
|
20261
|
-
</ng-template>
|
|
20185
|
+
template: `
|
|
20186
|
+
<mat-tab-group>
|
|
20187
|
+
<mat-tab label="Invoice History">
|
|
20188
|
+
<div style="padding-top: 8px;">
|
|
20189
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
20190
|
+
</div>
|
|
20191
|
+
</mat-tab>
|
|
20192
|
+
|
|
20193
|
+
<mat-tab label="Customer Statement">
|
|
20194
|
+
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
|
|
20195
|
+
</mat-tab>
|
|
20196
|
+
|
|
20197
|
+
<mat-tab label="Supplier Statement">
|
|
20198
|
+
<ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
|
|
20199
|
+
</mat-tab>
|
|
20200
|
+
</mat-tab-group>
|
|
20201
|
+
|
|
20202
|
+
<ng-template #statementTab let-kind="kind">
|
|
20203
|
+
<div style="padding: 16px 0;">
|
|
20204
|
+
<div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
|
|
20205
|
+
<mat-form-field style="min-width: 240px;">
|
|
20206
|
+
<mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
|
|
20207
|
+
<mat-select [(ngModel)]="state[kind].entityId">
|
|
20208
|
+
<mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
|
|
20209
|
+
</mat-select>
|
|
20210
|
+
</mat-form-field>
|
|
20211
|
+
<mat-form-field>
|
|
20212
|
+
<mat-label>From</mat-label>
|
|
20213
|
+
<input matInput type="date" [(ngModel)]="state[kind].from">
|
|
20214
|
+
</mat-form-field>
|
|
20215
|
+
<mat-form-field>
|
|
20216
|
+
<mat-label>To</mat-label>
|
|
20217
|
+
<input matInput type="date" [(ngModel)]="state[kind].to">
|
|
20218
|
+
</mat-form-field>
|
|
20219
|
+
<button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
|
|
20220
|
+
<button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
|
|
20221
|
+
<mat-icon>download</mat-icon> Export to Excel
|
|
20222
|
+
</button>
|
|
20223
|
+
<button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
|
|
20224
|
+
<mat-icon>picture_as_pdf</mat-icon> Download PDF
|
|
20225
|
+
</button>
|
|
20226
|
+
</div>
|
|
20227
|
+
|
|
20228
|
+
<mat-card *ngIf="state[kind].data" style="padding: 16px;">
|
|
20229
|
+
<div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
|
|
20230
|
+
<div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
|
|
20231
|
+
<div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
|
|
20232
|
+
<div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
|
|
20233
|
+
<div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
|
|
20234
|
+
</div>
|
|
20235
|
+
|
|
20236
|
+
<table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
|
|
20237
|
+
<tr style="text-align: left; border-bottom: 2px solid #333;">
|
|
20238
|
+
<th style="padding: 6px;">Date</th>
|
|
20239
|
+
<th style="padding: 6px;">Reference</th>
|
|
20240
|
+
<th style="padding: 6px;">Type</th>
|
|
20241
|
+
<th style="padding: 6px; text-align: right;">Total</th>
|
|
20242
|
+
<th style="padding: 6px; text-align: right;">Paid</th>
|
|
20243
|
+
<th style="padding: 6px; text-align: right;">Outstanding</th>
|
|
20244
|
+
<th style="padding: 6px;">Status</th>
|
|
20245
|
+
<th style="padding: 6px;">Aging</th>
|
|
20246
|
+
</tr>
|
|
20247
|
+
<tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
|
|
20248
|
+
<td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
|
|
20249
|
+
<td style="padding: 6px;">{{ line.reference }}</td>
|
|
20250
|
+
<td style="padding: 6px;">{{ line.type }}</td>
|
|
20251
|
+
<td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
|
|
20252
|
+
<td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
|
|
20253
|
+
<td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
|
|
20254
|
+
<td style="padding: 6px;">{{ line.status }}</td>
|
|
20255
|
+
<td style="padding: 6px;">{{ line.agingBucket }}</td>
|
|
20256
|
+
</tr>
|
|
20257
|
+
</table>
|
|
20258
|
+
<ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
|
|
20259
|
+
</mat-card>
|
|
20260
|
+
</div>
|
|
20261
|
+
</ng-template>
|
|
20262
20262
|
`,
|
|
20263
20263
|
standalone: false
|
|
20264
20264
|
}]
|
|
@@ -21144,140 +21144,140 @@ class ReceiptDialogComponent {
|
|
|
21144
21144
|
});
|
|
21145
21145
|
}
|
|
21146
21146
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21147
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptDialogComponent, isStandalone: false, selector: "spa-receipt-dialog", ngImport: i0, template: `
|
|
21148
|
-
<h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
|
|
21149
|
-
<mat-dialog-content>
|
|
21150
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
|
|
21151
|
-
<mat-form-field style="min-width: 220px;">
|
|
21152
|
-
<mat-label>Customer</mat-label>
|
|
21153
|
-
<mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
|
|
21154
|
-
<mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
|
|
21155
|
-
</mat-select>
|
|
21156
|
-
</mat-form-field>
|
|
21157
|
-
<mat-form-field>
|
|
21158
|
-
<mat-label>Date</mat-label>
|
|
21159
|
-
<input matInput type="date" [(ngModel)]="receiptDate">
|
|
21160
|
-
</mat-form-field>
|
|
21161
|
-
<mat-form-field>
|
|
21162
|
-
<mat-label>Amount</mat-label>
|
|
21163
|
-
<input matInput type="number" min="0" [(ngModel)]="amount">
|
|
21164
|
-
</mat-form-field>
|
|
21165
|
-
<mat-form-field>
|
|
21166
|
-
<mat-label>Method</mat-label>
|
|
21167
|
-
<mat-select [(ngModel)]="method">
|
|
21168
|
-
<mat-option [value]="0">Cash</mat-option>
|
|
21169
|
-
<mat-option [value]="1">Bank</mat-option>
|
|
21170
|
-
</mat-select>
|
|
21171
|
-
</mat-form-field>
|
|
21172
|
-
<mat-form-field style="min-width: 200px;">
|
|
21173
|
-
<mat-label>Reference</mat-label>
|
|
21174
|
-
<input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
|
|
21175
|
-
</mat-form-field>
|
|
21176
|
-
</div>
|
|
21177
|
-
<p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
|
|
21178
|
-
|
|
21179
|
-
<table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
|
|
21180
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21181
|
-
<th style="padding: 4px;">Invoice #</th>
|
|
21182
|
-
<th style="padding: 4px;">Date</th>
|
|
21183
|
-
<th style="padding: 4px; text-align: right;">Outstanding</th>
|
|
21184
|
-
<th style="padding: 4px; width: 160px;">Allocate</th>
|
|
21185
|
-
</tr>
|
|
21186
|
-
<tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
|
|
21187
|
-
<td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
|
|
21188
|
-
<td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
|
|
21189
|
-
<td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
|
|
21190
|
-
<td style="padding: 4px;">
|
|
21191
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
21192
|
-
<input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
|
|
21193
|
-
</mat-form-field>
|
|
21194
|
-
</td>
|
|
21195
|
-
</tr>
|
|
21196
|
-
<tr style="border-top: 2px solid #333;">
|
|
21197
|
-
<td style="padding: 8px 4px;" colspan="2">
|
|
21198
|
-
<button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
|
|
21199
|
-
</td>
|
|
21200
|
-
<td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
|
|
21201
|
-
<td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
|
|
21202
|
-
</tr>
|
|
21203
|
-
</table>
|
|
21204
|
-
<ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
|
|
21205
|
-
<p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
|
|
21206
|
-
</mat-dialog-content>
|
|
21207
|
-
<mat-dialog-actions align="end">
|
|
21208
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21209
|
-
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
|
|
21210
|
-
</mat-dialog-actions>
|
|
21147
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptDialogComponent, isStandalone: false, selector: "spa-receipt-dialog", ngImport: i0, template: `
|
|
21148
|
+
<h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
|
|
21149
|
+
<mat-dialog-content>
|
|
21150
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
|
|
21151
|
+
<mat-form-field style="min-width: 220px;">
|
|
21152
|
+
<mat-label>Customer</mat-label>
|
|
21153
|
+
<mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
|
|
21154
|
+
<mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
|
|
21155
|
+
</mat-select>
|
|
21156
|
+
</mat-form-field>
|
|
21157
|
+
<mat-form-field>
|
|
21158
|
+
<mat-label>Date</mat-label>
|
|
21159
|
+
<input matInput type="date" [(ngModel)]="receiptDate">
|
|
21160
|
+
</mat-form-field>
|
|
21161
|
+
<mat-form-field>
|
|
21162
|
+
<mat-label>Amount</mat-label>
|
|
21163
|
+
<input matInput type="number" min="0" [(ngModel)]="amount">
|
|
21164
|
+
</mat-form-field>
|
|
21165
|
+
<mat-form-field>
|
|
21166
|
+
<mat-label>Method</mat-label>
|
|
21167
|
+
<mat-select [(ngModel)]="method">
|
|
21168
|
+
<mat-option [value]="0">Cash</mat-option>
|
|
21169
|
+
<mat-option [value]="1">Bank</mat-option>
|
|
21170
|
+
</mat-select>
|
|
21171
|
+
</mat-form-field>
|
|
21172
|
+
<mat-form-field style="min-width: 200px;">
|
|
21173
|
+
<mat-label>Reference</mat-label>
|
|
21174
|
+
<input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
|
|
21175
|
+
</mat-form-field>
|
|
21176
|
+
</div>
|
|
21177
|
+
<p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
|
|
21178
|
+
|
|
21179
|
+
<table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
|
|
21180
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21181
|
+
<th style="padding: 4px;">Invoice #</th>
|
|
21182
|
+
<th style="padding: 4px;">Date</th>
|
|
21183
|
+
<th style="padding: 4px; text-align: right;">Outstanding</th>
|
|
21184
|
+
<th style="padding: 4px; width: 160px;">Allocate</th>
|
|
21185
|
+
</tr>
|
|
21186
|
+
<tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
|
|
21187
|
+
<td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
|
|
21188
|
+
<td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
|
|
21189
|
+
<td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
|
|
21190
|
+
<td style="padding: 4px;">
|
|
21191
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
21192
|
+
<input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
|
|
21193
|
+
</mat-form-field>
|
|
21194
|
+
</td>
|
|
21195
|
+
</tr>
|
|
21196
|
+
<tr style="border-top: 2px solid #333;">
|
|
21197
|
+
<td style="padding: 8px 4px;" colspan="2">
|
|
21198
|
+
<button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
|
|
21199
|
+
</td>
|
|
21200
|
+
<td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
|
|
21201
|
+
<td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
|
|
21202
|
+
</tr>
|
|
21203
|
+
</table>
|
|
21204
|
+
<ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
|
|
21205
|
+
<p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
|
|
21206
|
+
</mat-dialog-content>
|
|
21207
|
+
<mat-dialog-actions align="end">
|
|
21208
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21209
|
+
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
|
|
21210
|
+
</mat-dialog-actions>
|
|
21211
21211
|
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2$2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
|
|
21212
21212
|
}
|
|
21213
21213
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptDialogComponent, decorators: [{
|
|
21214
21214
|
type: Component,
|
|
21215
21215
|
args: [{
|
|
21216
21216
|
selector: 'spa-receipt-dialog',
|
|
21217
|
-
template: `
|
|
21218
|
-
<h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
|
|
21219
|
-
<mat-dialog-content>
|
|
21220
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
|
|
21221
|
-
<mat-form-field style="min-width: 220px;">
|
|
21222
|
-
<mat-label>Customer</mat-label>
|
|
21223
|
-
<mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
|
|
21224
|
-
<mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
|
|
21225
|
-
</mat-select>
|
|
21226
|
-
</mat-form-field>
|
|
21227
|
-
<mat-form-field>
|
|
21228
|
-
<mat-label>Date</mat-label>
|
|
21229
|
-
<input matInput type="date" [(ngModel)]="receiptDate">
|
|
21230
|
-
</mat-form-field>
|
|
21231
|
-
<mat-form-field>
|
|
21232
|
-
<mat-label>Amount</mat-label>
|
|
21233
|
-
<input matInput type="number" min="0" [(ngModel)]="amount">
|
|
21234
|
-
</mat-form-field>
|
|
21235
|
-
<mat-form-field>
|
|
21236
|
-
<mat-label>Method</mat-label>
|
|
21237
|
-
<mat-select [(ngModel)]="method">
|
|
21238
|
-
<mat-option [value]="0">Cash</mat-option>
|
|
21239
|
-
<mat-option [value]="1">Bank</mat-option>
|
|
21240
|
-
</mat-select>
|
|
21241
|
-
</mat-form-field>
|
|
21242
|
-
<mat-form-field style="min-width: 200px;">
|
|
21243
|
-
<mat-label>Reference</mat-label>
|
|
21244
|
-
<input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
|
|
21245
|
-
</mat-form-field>
|
|
21246
|
-
</div>
|
|
21247
|
-
<p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
|
|
21248
|
-
|
|
21249
|
-
<table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
|
|
21250
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21251
|
-
<th style="padding: 4px;">Invoice #</th>
|
|
21252
|
-
<th style="padding: 4px;">Date</th>
|
|
21253
|
-
<th style="padding: 4px; text-align: right;">Outstanding</th>
|
|
21254
|
-
<th style="padding: 4px; width: 160px;">Allocate</th>
|
|
21255
|
-
</tr>
|
|
21256
|
-
<tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
|
|
21257
|
-
<td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
|
|
21258
|
-
<td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
|
|
21259
|
-
<td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
|
|
21260
|
-
<td style="padding: 4px;">
|
|
21261
|
-
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
21262
|
-
<input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
|
|
21263
|
-
</mat-form-field>
|
|
21264
|
-
</td>
|
|
21265
|
-
</tr>
|
|
21266
|
-
<tr style="border-top: 2px solid #333;">
|
|
21267
|
-
<td style="padding: 8px 4px;" colspan="2">
|
|
21268
|
-
<button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
|
|
21269
|
-
</td>
|
|
21270
|
-
<td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
|
|
21271
|
-
<td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
|
|
21272
|
-
</tr>
|
|
21273
|
-
</table>
|
|
21274
|
-
<ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
|
|
21275
|
-
<p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
|
|
21276
|
-
</mat-dialog-content>
|
|
21277
|
-
<mat-dialog-actions align="end">
|
|
21278
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21279
|
-
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
|
|
21280
|
-
</mat-dialog-actions>
|
|
21217
|
+
template: `
|
|
21218
|
+
<h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
|
|
21219
|
+
<mat-dialog-content>
|
|
21220
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
|
|
21221
|
+
<mat-form-field style="min-width: 220px;">
|
|
21222
|
+
<mat-label>Customer</mat-label>
|
|
21223
|
+
<mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
|
|
21224
|
+
<mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
|
|
21225
|
+
</mat-select>
|
|
21226
|
+
</mat-form-field>
|
|
21227
|
+
<mat-form-field>
|
|
21228
|
+
<mat-label>Date</mat-label>
|
|
21229
|
+
<input matInput type="date" [(ngModel)]="receiptDate">
|
|
21230
|
+
</mat-form-field>
|
|
21231
|
+
<mat-form-field>
|
|
21232
|
+
<mat-label>Amount</mat-label>
|
|
21233
|
+
<input matInput type="number" min="0" [(ngModel)]="amount">
|
|
21234
|
+
</mat-form-field>
|
|
21235
|
+
<mat-form-field>
|
|
21236
|
+
<mat-label>Method</mat-label>
|
|
21237
|
+
<mat-select [(ngModel)]="method">
|
|
21238
|
+
<mat-option [value]="0">Cash</mat-option>
|
|
21239
|
+
<mat-option [value]="1">Bank</mat-option>
|
|
21240
|
+
</mat-select>
|
|
21241
|
+
</mat-form-field>
|
|
21242
|
+
<mat-form-field style="min-width: 200px;">
|
|
21243
|
+
<mat-label>Reference</mat-label>
|
|
21244
|
+
<input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
|
|
21245
|
+
</mat-form-field>
|
|
21246
|
+
</div>
|
|
21247
|
+
<p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
|
|
21248
|
+
|
|
21249
|
+
<table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
|
|
21250
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21251
|
+
<th style="padding: 4px;">Invoice #</th>
|
|
21252
|
+
<th style="padding: 4px;">Date</th>
|
|
21253
|
+
<th style="padding: 4px; text-align: right;">Outstanding</th>
|
|
21254
|
+
<th style="padding: 4px; width: 160px;">Allocate</th>
|
|
21255
|
+
</tr>
|
|
21256
|
+
<tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
|
|
21257
|
+
<td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
|
|
21258
|
+
<td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
|
|
21259
|
+
<td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
|
|
21260
|
+
<td style="padding: 4px;">
|
|
21261
|
+
<mat-form-field style="width: 100%;" subscriptSizing="dynamic">
|
|
21262
|
+
<input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
|
|
21263
|
+
</mat-form-field>
|
|
21264
|
+
</td>
|
|
21265
|
+
</tr>
|
|
21266
|
+
<tr style="border-top: 2px solid #333;">
|
|
21267
|
+
<td style="padding: 8px 4px;" colspan="2">
|
|
21268
|
+
<button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
|
|
21269
|
+
</td>
|
|
21270
|
+
<td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
|
|
21271
|
+
<td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
|
|
21272
|
+
</tr>
|
|
21273
|
+
</table>
|
|
21274
|
+
<ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
|
|
21275
|
+
<p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
|
|
21276
|
+
</mat-dialog-content>
|
|
21277
|
+
<mat-dialog-actions align="end">
|
|
21278
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21279
|
+
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
|
|
21280
|
+
</mat-dialog-actions>
|
|
21281
21281
|
`,
|
|
21282
21282
|
standalone: false
|
|
21283
21283
|
}]
|
|
@@ -21321,26 +21321,26 @@ class ReceiptsComponent {
|
|
|
21321
21321
|
});
|
|
21322
21322
|
}
|
|
21323
21323
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21324
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptsComponent, isStandalone: false, selector: "spa-receipts", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
21325
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
21326
|
-
<button mat-stroked-button (click)="openDialog()">
|
|
21327
|
-
<mat-icon>receipt</mat-icon> New Receipt
|
|
21328
|
-
</button>
|
|
21329
|
-
</div>
|
|
21330
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
21324
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptsComponent, isStandalone: false, selector: "spa-receipts", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
21325
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
21326
|
+
<button mat-stroked-button (click)="openDialog()">
|
|
21327
|
+
<mat-icon>receipt</mat-icon> New Receipt
|
|
21328
|
+
</button>
|
|
21329
|
+
</div>
|
|
21330
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
21331
21331
|
`, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
|
|
21332
21332
|
}
|
|
21333
21333
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptsComponent, decorators: [{
|
|
21334
21334
|
type: Component,
|
|
21335
21335
|
args: [{
|
|
21336
21336
|
selector: 'spa-receipts',
|
|
21337
|
-
template: `
|
|
21338
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
21339
|
-
<button mat-stroked-button (click)="openDialog()">
|
|
21340
|
-
<mat-icon>receipt</mat-icon> New Receipt
|
|
21341
|
-
</button>
|
|
21342
|
-
</div>
|
|
21343
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
21337
|
+
template: `
|
|
21338
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
21339
|
+
<button mat-stroked-button (click)="openDialog()">
|
|
21340
|
+
<mat-icon>receipt</mat-icon> New Receipt
|
|
21341
|
+
</button>
|
|
21342
|
+
</div>
|
|
21343
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
21344
21344
|
`,
|
|
21345
21345
|
standalone: false
|
|
21346
21346
|
}]
|
|
@@ -21407,80 +21407,80 @@ class StatementImportDialogComponent {
|
|
|
21407
21407
|
});
|
|
21408
21408
|
}
|
|
21409
21409
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementImportDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21410
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementImportDialogComponent, isStandalone: false, selector: "spa-statement-import-dialog", ngImport: i0, template: `
|
|
21411
|
-
<h2 mat-dialog-title>Import Bank Statement</h2>
|
|
21412
|
-
<mat-dialog-content>
|
|
21413
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21414
|
-
<mat-form-field style="min-width: 220px;">
|
|
21415
|
-
<mat-label>Account</mat-label>
|
|
21416
|
-
<mat-select [(ngModel)]="accountID">
|
|
21417
|
-
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
21418
|
-
</mat-select>
|
|
21419
|
-
</mat-form-field>
|
|
21420
|
-
<mat-form-field>
|
|
21421
|
-
<mat-label>From</mat-label>
|
|
21422
|
-
<input matInput type="date" [(ngModel)]="statementFrom">
|
|
21423
|
-
</mat-form-field>
|
|
21424
|
-
<mat-form-field>
|
|
21425
|
-
<mat-label>To</mat-label>
|
|
21426
|
-
<input matInput type="date" [(ngModel)]="statementTo">
|
|
21427
|
-
</mat-form-field>
|
|
21428
|
-
<mat-form-field>
|
|
21429
|
-
<mat-label>Closing Balance</mat-label>
|
|
21430
|
-
<input matInput type="number" [(ngModel)]="closingBalance">
|
|
21431
|
-
</mat-form-field>
|
|
21432
|
-
</div>
|
|
21433
|
-
<mat-form-field style="width: 100%;">
|
|
21434
|
-
<mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
|
|
21435
|
-
<textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00 2026-07-03, Bank charges, , -25.50"></textarea>
|
|
21436
|
-
</mat-form-field>
|
|
21437
|
-
<p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
|
|
21438
|
-
<p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
|
|
21439
|
-
</mat-dialog-content>
|
|
21440
|
-
<mat-dialog-actions align="end">
|
|
21441
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21442
|
-
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
|
|
21443
|
-
</mat-dialog-actions>
|
|
21410
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementImportDialogComponent, isStandalone: false, selector: "spa-statement-import-dialog", ngImport: i0, template: `
|
|
21411
|
+
<h2 mat-dialog-title>Import Bank Statement</h2>
|
|
21412
|
+
<mat-dialog-content>
|
|
21413
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21414
|
+
<mat-form-field style="min-width: 220px;">
|
|
21415
|
+
<mat-label>Account</mat-label>
|
|
21416
|
+
<mat-select [(ngModel)]="accountID">
|
|
21417
|
+
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
21418
|
+
</mat-select>
|
|
21419
|
+
</mat-form-field>
|
|
21420
|
+
<mat-form-field>
|
|
21421
|
+
<mat-label>From</mat-label>
|
|
21422
|
+
<input matInput type="date" [(ngModel)]="statementFrom">
|
|
21423
|
+
</mat-form-field>
|
|
21424
|
+
<mat-form-field>
|
|
21425
|
+
<mat-label>To</mat-label>
|
|
21426
|
+
<input matInput type="date" [(ngModel)]="statementTo">
|
|
21427
|
+
</mat-form-field>
|
|
21428
|
+
<mat-form-field>
|
|
21429
|
+
<mat-label>Closing Balance</mat-label>
|
|
21430
|
+
<input matInput type="number" [(ngModel)]="closingBalance">
|
|
21431
|
+
</mat-form-field>
|
|
21432
|
+
</div>
|
|
21433
|
+
<mat-form-field style="width: 100%;">
|
|
21434
|
+
<mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
|
|
21435
|
+
<textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00 2026-07-03, Bank charges, , -25.50"></textarea>
|
|
21436
|
+
</mat-form-field>
|
|
21437
|
+
<p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
|
|
21438
|
+
<p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
|
|
21439
|
+
</mat-dialog-content>
|
|
21440
|
+
<mat-dialog-actions align="end">
|
|
21441
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21442
|
+
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
|
|
21443
|
+
</mat-dialog-actions>
|
|
21444
21444
|
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
21445
21445
|
}
|
|
21446
21446
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementImportDialogComponent, decorators: [{
|
|
21447
21447
|
type: Component,
|
|
21448
21448
|
args: [{
|
|
21449
21449
|
selector: 'spa-statement-import-dialog',
|
|
21450
|
-
template: `
|
|
21451
|
-
<h2 mat-dialog-title>Import Bank Statement</h2>
|
|
21452
|
-
<mat-dialog-content>
|
|
21453
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21454
|
-
<mat-form-field style="min-width: 220px;">
|
|
21455
|
-
<mat-label>Account</mat-label>
|
|
21456
|
-
<mat-select [(ngModel)]="accountID">
|
|
21457
|
-
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
21458
|
-
</mat-select>
|
|
21459
|
-
</mat-form-field>
|
|
21460
|
-
<mat-form-field>
|
|
21461
|
-
<mat-label>From</mat-label>
|
|
21462
|
-
<input matInput type="date" [(ngModel)]="statementFrom">
|
|
21463
|
-
</mat-form-field>
|
|
21464
|
-
<mat-form-field>
|
|
21465
|
-
<mat-label>To</mat-label>
|
|
21466
|
-
<input matInput type="date" [(ngModel)]="statementTo">
|
|
21467
|
-
</mat-form-field>
|
|
21468
|
-
<mat-form-field>
|
|
21469
|
-
<mat-label>Closing Balance</mat-label>
|
|
21470
|
-
<input matInput type="number" [(ngModel)]="closingBalance">
|
|
21471
|
-
</mat-form-field>
|
|
21472
|
-
</div>
|
|
21473
|
-
<mat-form-field style="width: 100%;">
|
|
21474
|
-
<mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
|
|
21475
|
-
<textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00 2026-07-03, Bank charges, , -25.50"></textarea>
|
|
21476
|
-
</mat-form-field>
|
|
21477
|
-
<p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
|
|
21478
|
-
<p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
|
|
21479
|
-
</mat-dialog-content>
|
|
21480
|
-
<mat-dialog-actions align="end">
|
|
21481
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21482
|
-
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
|
|
21483
|
-
</mat-dialog-actions>
|
|
21450
|
+
template: `
|
|
21451
|
+
<h2 mat-dialog-title>Import Bank Statement</h2>
|
|
21452
|
+
<mat-dialog-content>
|
|
21453
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21454
|
+
<mat-form-field style="min-width: 220px;">
|
|
21455
|
+
<mat-label>Account</mat-label>
|
|
21456
|
+
<mat-select [(ngModel)]="accountID">
|
|
21457
|
+
<mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
|
|
21458
|
+
</mat-select>
|
|
21459
|
+
</mat-form-field>
|
|
21460
|
+
<mat-form-field>
|
|
21461
|
+
<mat-label>From</mat-label>
|
|
21462
|
+
<input matInput type="date" [(ngModel)]="statementFrom">
|
|
21463
|
+
</mat-form-field>
|
|
21464
|
+
<mat-form-field>
|
|
21465
|
+
<mat-label>To</mat-label>
|
|
21466
|
+
<input matInput type="date" [(ngModel)]="statementTo">
|
|
21467
|
+
</mat-form-field>
|
|
21468
|
+
<mat-form-field>
|
|
21469
|
+
<mat-label>Closing Balance</mat-label>
|
|
21470
|
+
<input matInput type="number" [(ngModel)]="closingBalance">
|
|
21471
|
+
</mat-form-field>
|
|
21472
|
+
</div>
|
|
21473
|
+
<mat-form-field style="width: 100%;">
|
|
21474
|
+
<mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
|
|
21475
|
+
<textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00 2026-07-03, Bank charges, , -25.50"></textarea>
|
|
21476
|
+
</mat-form-field>
|
|
21477
|
+
<p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
|
|
21478
|
+
<p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
|
|
21479
|
+
</mat-dialog-content>
|
|
21480
|
+
<mat-dialog-actions align="end">
|
|
21481
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21482
|
+
<button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
|
|
21483
|
+
</mat-dialog-actions>
|
|
21484
21484
|
`,
|
|
21485
21485
|
standalone: false
|
|
21486
21486
|
}]
|
|
@@ -21528,132 +21528,132 @@ class ReconcileDialogComponent {
|
|
|
21528
21528
|
});
|
|
21529
21529
|
}
|
|
21530
21530
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReconcileDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21531
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReconcileDialogComponent, isStandalone: false, selector: "spa-reconcile-dialog", ngImport: i0, template: `
|
|
21532
|
-
<h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
|
|
21533
|
-
<mat-dialog-content>
|
|
21534
|
-
<div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
|
|
21535
|
-
<div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
|
|
21536
|
-
<div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
|
|
21537
|
-
<div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
|
|
21538
|
-
<div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
|
|
21539
|
-
</div>
|
|
21540
|
-
|
|
21541
|
-
<div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
|
|
21542
|
-
<div style="flex: 1; min-width: 380px;">
|
|
21543
|
-
<h4>Statement Lines</h4>
|
|
21544
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
21545
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21546
|
-
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21547
|
-
<th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
|
|
21548
|
-
</tr>
|
|
21549
|
-
<tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
|
|
21550
|
-
[style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
|
|
21551
|
-
<td style="padding: 4px;">
|
|
21552
|
-
<mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
|
|
21553
|
-
</td>
|
|
21554
|
-
<td style="padding: 4px;">{{ line.date }}</td>
|
|
21555
|
-
<td style="padding: 4px;">{{ line.description }}</td>
|
|
21556
|
-
<td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
|
|
21557
|
-
<td style="padding: 4px;">
|
|
21558
|
-
<span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
|
|
21559
|
-
<button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
|
|
21560
|
-
</td>
|
|
21561
|
-
</tr>
|
|
21562
|
-
</table>
|
|
21563
|
-
</div>
|
|
21564
|
-
|
|
21565
|
-
<div style="flex: 1; min-width: 380px;">
|
|
21566
|
-
<h4>Unreconciled Book Entries</h4>
|
|
21567
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
21568
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21569
|
-
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21570
|
-
<th style="padding: 4px; text-align: right;">Amount</th>
|
|
21571
|
-
</tr>
|
|
21572
|
-
<tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
|
|
21573
|
-
[style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
|
|
21574
|
-
<td style="padding: 4px;">
|
|
21575
|
-
<mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
|
|
21576
|
-
</td>
|
|
21577
|
-
<td style="padding: 4px;">{{ entry.date }}</td>
|
|
21578
|
-
<td style="padding: 4px;">{{ entry.description }}</td>
|
|
21579
|
-
<td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
|
|
21580
|
-
</tr>
|
|
21581
|
-
</table>
|
|
21582
|
-
</div>
|
|
21583
|
-
</div>
|
|
21584
|
-
</mat-dialog-content>
|
|
21585
|
-
<mat-dialog-actions align="end">
|
|
21586
|
-
<button mat-button (click)="dialogRef.close(changed)">Close</button>
|
|
21587
|
-
<button mat-stroked-button (click)="autoMatch()">Auto Match</button>
|
|
21588
|
-
<button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
|
|
21589
|
-
<button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
|
|
21590
|
-
</mat-dialog-actions>
|
|
21531
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReconcileDialogComponent, isStandalone: false, selector: "spa-reconcile-dialog", ngImport: i0, template: `
|
|
21532
|
+
<h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
|
|
21533
|
+
<mat-dialog-content>
|
|
21534
|
+
<div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
|
|
21535
|
+
<div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
|
|
21536
|
+
<div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
|
|
21537
|
+
<div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
|
|
21538
|
+
<div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
|
|
21539
|
+
</div>
|
|
21540
|
+
|
|
21541
|
+
<div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
|
|
21542
|
+
<div style="flex: 1; min-width: 380px;">
|
|
21543
|
+
<h4>Statement Lines</h4>
|
|
21544
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
21545
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21546
|
+
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21547
|
+
<th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
|
|
21548
|
+
</tr>
|
|
21549
|
+
<tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
|
|
21550
|
+
[style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
|
|
21551
|
+
<td style="padding: 4px;">
|
|
21552
|
+
<mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
|
|
21553
|
+
</td>
|
|
21554
|
+
<td style="padding: 4px;">{{ line.date }}</td>
|
|
21555
|
+
<td style="padding: 4px;">{{ line.description }}</td>
|
|
21556
|
+
<td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
|
|
21557
|
+
<td style="padding: 4px;">
|
|
21558
|
+
<span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
|
|
21559
|
+
<button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
|
|
21560
|
+
</td>
|
|
21561
|
+
</tr>
|
|
21562
|
+
</table>
|
|
21563
|
+
</div>
|
|
21564
|
+
|
|
21565
|
+
<div style="flex: 1; min-width: 380px;">
|
|
21566
|
+
<h4>Unreconciled Book Entries</h4>
|
|
21567
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
21568
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21569
|
+
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21570
|
+
<th style="padding: 4px; text-align: right;">Amount</th>
|
|
21571
|
+
</tr>
|
|
21572
|
+
<tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
|
|
21573
|
+
[style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
|
|
21574
|
+
<td style="padding: 4px;">
|
|
21575
|
+
<mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
|
|
21576
|
+
</td>
|
|
21577
|
+
<td style="padding: 4px;">{{ entry.date }}</td>
|
|
21578
|
+
<td style="padding: 4px;">{{ entry.description }}</td>
|
|
21579
|
+
<td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
|
|
21580
|
+
</tr>
|
|
21581
|
+
</table>
|
|
21582
|
+
</div>
|
|
21583
|
+
</div>
|
|
21584
|
+
</mat-dialog-content>
|
|
21585
|
+
<mat-dialog-actions align="end">
|
|
21586
|
+
<button mat-button (click)="dialogRef.close(changed)">Close</button>
|
|
21587
|
+
<button mat-stroked-button (click)="autoMatch()">Auto Match</button>
|
|
21588
|
+
<button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
|
|
21589
|
+
<button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
|
|
21590
|
+
</mat-dialog-actions>
|
|
21591
21591
|
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
21592
21592
|
}
|
|
21593
21593
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReconcileDialogComponent, decorators: [{
|
|
21594
21594
|
type: Component,
|
|
21595
21595
|
args: [{
|
|
21596
21596
|
selector: 'spa-reconcile-dialog',
|
|
21597
|
-
template: `
|
|
21598
|
-
<h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
|
|
21599
|
-
<mat-dialog-content>
|
|
21600
|
-
<div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
|
|
21601
|
-
<div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
|
|
21602
|
-
<div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
|
|
21603
|
-
<div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
|
|
21604
|
-
<div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
|
|
21605
|
-
</div>
|
|
21606
|
-
|
|
21607
|
-
<div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
|
|
21608
|
-
<div style="flex: 1; min-width: 380px;">
|
|
21609
|
-
<h4>Statement Lines</h4>
|
|
21610
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
21611
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21612
|
-
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21613
|
-
<th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
|
|
21614
|
-
</tr>
|
|
21615
|
-
<tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
|
|
21616
|
-
[style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
|
|
21617
|
-
<td style="padding: 4px;">
|
|
21618
|
-
<mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
|
|
21619
|
-
</td>
|
|
21620
|
-
<td style="padding: 4px;">{{ line.date }}</td>
|
|
21621
|
-
<td style="padding: 4px;">{{ line.description }}</td>
|
|
21622
|
-
<td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
|
|
21623
|
-
<td style="padding: 4px;">
|
|
21624
|
-
<span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
|
|
21625
|
-
<button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
|
|
21626
|
-
</td>
|
|
21627
|
-
</tr>
|
|
21628
|
-
</table>
|
|
21629
|
-
</div>
|
|
21630
|
-
|
|
21631
|
-
<div style="flex: 1; min-width: 380px;">
|
|
21632
|
-
<h4>Unreconciled Book Entries</h4>
|
|
21633
|
-
<table style="width: 100%; border-collapse: collapse;">
|
|
21634
|
-
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21635
|
-
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21636
|
-
<th style="padding: 4px; text-align: right;">Amount</th>
|
|
21637
|
-
</tr>
|
|
21638
|
-
<tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
|
|
21639
|
-
[style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
|
|
21640
|
-
<td style="padding: 4px;">
|
|
21641
|
-
<mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
|
|
21642
|
-
</td>
|
|
21643
|
-
<td style="padding: 4px;">{{ entry.date }}</td>
|
|
21644
|
-
<td style="padding: 4px;">{{ entry.description }}</td>
|
|
21645
|
-
<td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
|
|
21646
|
-
</tr>
|
|
21647
|
-
</table>
|
|
21648
|
-
</div>
|
|
21649
|
-
</div>
|
|
21650
|
-
</mat-dialog-content>
|
|
21651
|
-
<mat-dialog-actions align="end">
|
|
21652
|
-
<button mat-button (click)="dialogRef.close(changed)">Close</button>
|
|
21653
|
-
<button mat-stroked-button (click)="autoMatch()">Auto Match</button>
|
|
21654
|
-
<button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
|
|
21655
|
-
<button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
|
|
21656
|
-
</mat-dialog-actions>
|
|
21597
|
+
template: `
|
|
21598
|
+
<h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
|
|
21599
|
+
<mat-dialog-content>
|
|
21600
|
+
<div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
|
|
21601
|
+
<div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
|
|
21602
|
+
<div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
|
|
21603
|
+
<div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
|
|
21604
|
+
<div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
|
|
21605
|
+
</div>
|
|
21606
|
+
|
|
21607
|
+
<div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
|
|
21608
|
+
<div style="flex: 1; min-width: 380px;">
|
|
21609
|
+
<h4>Statement Lines</h4>
|
|
21610
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
21611
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21612
|
+
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21613
|
+
<th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
|
|
21614
|
+
</tr>
|
|
21615
|
+
<tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
|
|
21616
|
+
[style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
|
|
21617
|
+
<td style="padding: 4px;">
|
|
21618
|
+
<mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
|
|
21619
|
+
</td>
|
|
21620
|
+
<td style="padding: 4px;">{{ line.date }}</td>
|
|
21621
|
+
<td style="padding: 4px;">{{ line.description }}</td>
|
|
21622
|
+
<td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
|
|
21623
|
+
<td style="padding: 4px;">
|
|
21624
|
+
<span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
|
|
21625
|
+
<button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
|
|
21626
|
+
</td>
|
|
21627
|
+
</tr>
|
|
21628
|
+
</table>
|
|
21629
|
+
</div>
|
|
21630
|
+
|
|
21631
|
+
<div style="flex: 1; min-width: 380px;">
|
|
21632
|
+
<h4>Unreconciled Book Entries</h4>
|
|
21633
|
+
<table style="width: 100%; border-collapse: collapse;">
|
|
21634
|
+
<tr style="text-align: left; border-bottom: 1px solid #ccc;">
|
|
21635
|
+
<th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
|
|
21636
|
+
<th style="padding: 4px; text-align: right;">Amount</th>
|
|
21637
|
+
</tr>
|
|
21638
|
+
<tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
|
|
21639
|
+
[style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
|
|
21640
|
+
<td style="padding: 4px;">
|
|
21641
|
+
<mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
|
|
21642
|
+
</td>
|
|
21643
|
+
<td style="padding: 4px;">{{ entry.date }}</td>
|
|
21644
|
+
<td style="padding: 4px;">{{ entry.description }}</td>
|
|
21645
|
+
<td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
|
|
21646
|
+
</tr>
|
|
21647
|
+
</table>
|
|
21648
|
+
</div>
|
|
21649
|
+
</div>
|
|
21650
|
+
</mat-dialog-content>
|
|
21651
|
+
<mat-dialog-actions align="end">
|
|
21652
|
+
<button mat-button (click)="dialogRef.close(changed)">Close</button>
|
|
21653
|
+
<button mat-stroked-button (click)="autoMatch()">Auto Match</button>
|
|
21654
|
+
<button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
|
|
21655
|
+
<button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
|
|
21656
|
+
</mat-dialog-actions>
|
|
21657
21657
|
`,
|
|
21658
21658
|
standalone: false
|
|
21659
21659
|
}]
|
|
@@ -21681,26 +21681,26 @@ class BankReconciliationComponent {
|
|
|
21681
21681
|
this.page?.tableReload.next(true); });
|
|
21682
21682
|
}
|
|
21683
21683
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: BankReconciliationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21684
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: BankReconciliationComponent, isStandalone: false, selector: "spa-bank-reconciliation", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
21685
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
21686
|
-
<button mat-stroked-button (click)="newImport()">
|
|
21687
|
-
<mat-icon>upload_file</mat-icon> Import Statement
|
|
21688
|
-
</button>
|
|
21689
|
-
</div>
|
|
21690
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
21684
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: BankReconciliationComponent, isStandalone: false, selector: "spa-bank-reconciliation", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
21685
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
21686
|
+
<button mat-stroked-button (click)="newImport()">
|
|
21687
|
+
<mat-icon>upload_file</mat-icon> Import Statement
|
|
21688
|
+
</button>
|
|
21689
|
+
</div>
|
|
21690
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
21691
21691
|
`, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
|
|
21692
21692
|
}
|
|
21693
21693
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: BankReconciliationComponent, decorators: [{
|
|
21694
21694
|
type: Component,
|
|
21695
21695
|
args: [{
|
|
21696
21696
|
selector: 'spa-bank-reconciliation',
|
|
21697
|
-
template: `
|
|
21698
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
21699
|
-
<button mat-stroked-button (click)="newImport()">
|
|
21700
|
-
<mat-icon>upload_file</mat-icon> Import Statement
|
|
21701
|
-
</button>
|
|
21702
|
-
</div>
|
|
21703
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
21697
|
+
template: `
|
|
21698
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
21699
|
+
<button mat-stroked-button (click)="newImport()">
|
|
21700
|
+
<mat-icon>upload_file</mat-icon> Import Statement
|
|
21701
|
+
</button>
|
|
21702
|
+
</div>
|
|
21703
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
21704
21704
|
`,
|
|
21705
21705
|
standalone: false
|
|
21706
21706
|
}]
|
|
@@ -21735,62 +21735,62 @@ class RevenueScheduleDialogComponent {
|
|
|
21735
21735
|
});
|
|
21736
21736
|
}
|
|
21737
21737
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueScheduleDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21738
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueScheduleDialogComponent, isStandalone: false, selector: "spa-revenue-schedule-dialog", ngImport: i0, template: `
|
|
21739
|
-
<h2 mat-dialog-title>Defer Invoice Revenue</h2>
|
|
21740
|
-
<mat-dialog-content>
|
|
21741
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21742
|
-
<mat-form-field style="min-width: 280px;">
|
|
21743
|
-
<mat-label>Invoice</mat-label>
|
|
21744
|
-
<mat-select [(ngModel)]="invoiceID">
|
|
21745
|
-
<mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
|
|
21746
|
-
</mat-select>
|
|
21747
|
-
</mat-form-field>
|
|
21748
|
-
<mat-form-field>
|
|
21749
|
-
<mat-label>Service Period Start</mat-label>
|
|
21750
|
-
<input matInput type="date" [(ngModel)]="startDate">
|
|
21751
|
-
</mat-form-field>
|
|
21752
|
-
<mat-form-field>
|
|
21753
|
-
<mat-label>Service Period End</mat-label>
|
|
21754
|
-
<input matInput type="date" [(ngModel)]="endDate">
|
|
21755
|
-
</mat-form-field>
|
|
21756
|
-
</div>
|
|
21757
|
-
<p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
|
|
21758
|
-
</mat-dialog-content>
|
|
21759
|
-
<mat-dialog-actions align="end">
|
|
21760
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21761
|
-
<button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
|
|
21762
|
-
</mat-dialog-actions>
|
|
21738
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueScheduleDialogComponent, isStandalone: false, selector: "spa-revenue-schedule-dialog", ngImport: i0, template: `
|
|
21739
|
+
<h2 mat-dialog-title>Defer Invoice Revenue</h2>
|
|
21740
|
+
<mat-dialog-content>
|
|
21741
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21742
|
+
<mat-form-field style="min-width: 280px;">
|
|
21743
|
+
<mat-label>Invoice</mat-label>
|
|
21744
|
+
<mat-select [(ngModel)]="invoiceID">
|
|
21745
|
+
<mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
|
|
21746
|
+
</mat-select>
|
|
21747
|
+
</mat-form-field>
|
|
21748
|
+
<mat-form-field>
|
|
21749
|
+
<mat-label>Service Period Start</mat-label>
|
|
21750
|
+
<input matInput type="date" [(ngModel)]="startDate">
|
|
21751
|
+
</mat-form-field>
|
|
21752
|
+
<mat-form-field>
|
|
21753
|
+
<mat-label>Service Period End</mat-label>
|
|
21754
|
+
<input matInput type="date" [(ngModel)]="endDate">
|
|
21755
|
+
</mat-form-field>
|
|
21756
|
+
</div>
|
|
21757
|
+
<p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
|
|
21758
|
+
</mat-dialog-content>
|
|
21759
|
+
<mat-dialog-actions align="end">
|
|
21760
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21761
|
+
<button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
|
|
21762
|
+
</mat-dialog-actions>
|
|
21763
21763
|
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] }); }
|
|
21764
21764
|
}
|
|
21765
21765
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueScheduleDialogComponent, decorators: [{
|
|
21766
21766
|
type: Component,
|
|
21767
21767
|
args: [{
|
|
21768
21768
|
selector: 'spa-revenue-schedule-dialog',
|
|
21769
|
-
template: `
|
|
21770
|
-
<h2 mat-dialog-title>Defer Invoice Revenue</h2>
|
|
21771
|
-
<mat-dialog-content>
|
|
21772
|
-
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21773
|
-
<mat-form-field style="min-width: 280px;">
|
|
21774
|
-
<mat-label>Invoice</mat-label>
|
|
21775
|
-
<mat-select [(ngModel)]="invoiceID">
|
|
21776
|
-
<mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
|
|
21777
|
-
</mat-select>
|
|
21778
|
-
</mat-form-field>
|
|
21779
|
-
<mat-form-field>
|
|
21780
|
-
<mat-label>Service Period Start</mat-label>
|
|
21781
|
-
<input matInput type="date" [(ngModel)]="startDate">
|
|
21782
|
-
</mat-form-field>
|
|
21783
|
-
<mat-form-field>
|
|
21784
|
-
<mat-label>Service Period End</mat-label>
|
|
21785
|
-
<input matInput type="date" [(ngModel)]="endDate">
|
|
21786
|
-
</mat-form-field>
|
|
21787
|
-
</div>
|
|
21788
|
-
<p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
|
|
21789
|
-
</mat-dialog-content>
|
|
21790
|
-
<mat-dialog-actions align="end">
|
|
21791
|
-
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21792
|
-
<button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
|
|
21793
|
-
</mat-dialog-actions>
|
|
21769
|
+
template: `
|
|
21770
|
+
<h2 mat-dialog-title>Defer Invoice Revenue</h2>
|
|
21771
|
+
<mat-dialog-content>
|
|
21772
|
+
<div style="display: flex; gap: 16px; flex-wrap: wrap;">
|
|
21773
|
+
<mat-form-field style="min-width: 280px;">
|
|
21774
|
+
<mat-label>Invoice</mat-label>
|
|
21775
|
+
<mat-select [(ngModel)]="invoiceID">
|
|
21776
|
+
<mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
|
|
21777
|
+
</mat-select>
|
|
21778
|
+
</mat-form-field>
|
|
21779
|
+
<mat-form-field>
|
|
21780
|
+
<mat-label>Service Period Start</mat-label>
|
|
21781
|
+
<input matInput type="date" [(ngModel)]="startDate">
|
|
21782
|
+
</mat-form-field>
|
|
21783
|
+
<mat-form-field>
|
|
21784
|
+
<mat-label>Service Period End</mat-label>
|
|
21785
|
+
<input matInput type="date" [(ngModel)]="endDate">
|
|
21786
|
+
</mat-form-field>
|
|
21787
|
+
</div>
|
|
21788
|
+
<p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
|
|
21789
|
+
</mat-dialog-content>
|
|
21790
|
+
<mat-dialog-actions align="end">
|
|
21791
|
+
<button mat-button (click)="dialogRef.close(false)">Cancel</button>
|
|
21792
|
+
<button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
|
|
21793
|
+
</mat-dialog-actions>
|
|
21794
21794
|
`,
|
|
21795
21795
|
standalone: false
|
|
21796
21796
|
}]
|
|
@@ -21814,26 +21814,26 @@ class RevenueSchedulesComponent {
|
|
|
21814
21814
|
this.page?.tableReload.next(true); });
|
|
21815
21815
|
}
|
|
21816
21816
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueSchedulesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21817
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueSchedulesComponent, isStandalone: false, selector: "spa-revenue-schedules", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
21818
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
21819
|
-
<button mat-stroked-button (click)="newSchedule()">
|
|
21820
|
-
<mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
|
|
21821
|
-
</button>
|
|
21822
|
-
</div>
|
|
21823
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
21817
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueSchedulesComponent, isStandalone: false, selector: "spa-revenue-schedules", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
|
|
21818
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
21819
|
+
<button mat-stroked-button (click)="newSchedule()">
|
|
21820
|
+
<mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
|
|
21821
|
+
</button>
|
|
21822
|
+
</div>
|
|
21823
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
21824
21824
|
`, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
|
|
21825
21825
|
}
|
|
21826
21826
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueSchedulesComponent, decorators: [{
|
|
21827
21827
|
type: Component,
|
|
21828
21828
|
args: [{
|
|
21829
21829
|
selector: 'spa-revenue-schedules',
|
|
21830
|
-
template: `
|
|
21831
|
-
<div style="display: flex; justify-content: flex-end;">
|
|
21832
|
-
<button mat-stroked-button (click)="newSchedule()">
|
|
21833
|
-
<mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
|
|
21834
|
-
</button>
|
|
21835
|
-
</div>
|
|
21836
|
-
<spa-page [config]="pageConfig"></spa-page>
|
|
21830
|
+
template: `
|
|
21831
|
+
<div style="display: flex; justify-content: flex-end;">
|
|
21832
|
+
<button mat-stroked-button (click)="newSchedule()">
|
|
21833
|
+
<mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
|
|
21834
|
+
</button>
|
|
21835
|
+
</div>
|
|
21836
|
+
<spa-page [config]="pageConfig"></spa-page>
|
|
21837
21837
|
`,
|
|
21838
21838
|
standalone: false
|
|
21839
21839
|
}]
|
|
@@ -26295,26 +26295,26 @@ class FixedAssetsDashboardComponent {
|
|
|
26295
26295
|
};
|
|
26296
26296
|
}
|
|
26297
26297
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: FixedAssetsDashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
26298
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: FixedAssetsDashboardComponent, isStandalone: false, selector: "spa-fixed-assets-dashboard", ngImport: i0, template: `
|
|
26299
|
-
<div class="dashboard-container">
|
|
26300
|
-
<h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
|
|
26301
|
-
<spa-tiles [config]="summaryTiles"></spa-tiles>
|
|
26302
|
-
<div style="margin-top: 16px;"></div>
|
|
26303
|
-
<spa-tiles [config]="chartTiles"></spa-tiles>
|
|
26304
|
-
<spa-charts [config]="chartConfig"></spa-charts>
|
|
26305
|
-
</div>
|
|
26298
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: FixedAssetsDashboardComponent, isStandalone: false, selector: "spa-fixed-assets-dashboard", ngImport: i0, template: `
|
|
26299
|
+
<div class="dashboard-container">
|
|
26300
|
+
<h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
|
|
26301
|
+
<spa-tiles [config]="summaryTiles"></spa-tiles>
|
|
26302
|
+
<div style="margin-top: 16px;"></div>
|
|
26303
|
+
<spa-tiles [config]="chartTiles"></spa-tiles>
|
|
26304
|
+
<spa-charts [config]="chartConfig"></spa-charts>
|
|
26305
|
+
</div>
|
|
26306
26306
|
`, isInline: true, styles: [".dashboard-container{padding:16px}.dashboard-title{display:flex;align-items:center;gap:8px;margin-bottom:16px;color:#333;font-weight:500}\n"], dependencies: [{ kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: TilesComponent, selector: "spa-tiles", inputs: ["config", "lastSearch", "data", "reload"], outputs: ["tileActionSelected", "tileClick", "tileUnClick"] }, { kind: "component", type: ChartsComponent, selector: "spa-charts", inputs: ["config", "data", "reload"] }] }); }
|
|
26307
26307
|
}
|
|
26308
26308
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: FixedAssetsDashboardComponent, decorators: [{
|
|
26309
26309
|
type: Component,
|
|
26310
|
-
args: [{ selector: 'spa-fixed-assets-dashboard', template: `
|
|
26311
|
-
<div class="dashboard-container">
|
|
26312
|
-
<h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
|
|
26313
|
-
<spa-tiles [config]="summaryTiles"></spa-tiles>
|
|
26314
|
-
<div style="margin-top: 16px;"></div>
|
|
26315
|
-
<spa-tiles [config]="chartTiles"></spa-tiles>
|
|
26316
|
-
<spa-charts [config]="chartConfig"></spa-charts>
|
|
26317
|
-
</div>
|
|
26310
|
+
args: [{ selector: 'spa-fixed-assets-dashboard', template: `
|
|
26311
|
+
<div class="dashboard-container">
|
|
26312
|
+
<h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
|
|
26313
|
+
<spa-tiles [config]="summaryTiles"></spa-tiles>
|
|
26314
|
+
<div style="margin-top: 16px;"></div>
|
|
26315
|
+
<spa-tiles [config]="chartTiles"></spa-tiles>
|
|
26316
|
+
<spa-charts [config]="chartConfig"></spa-charts>
|
|
26317
|
+
</div>
|
|
26318
26318
|
`, standalone: false, styles: [".dashboard-container{padding:16px}.dashboard-title{display:flex;align-items:center;gap:8px;margin-bottom:16px;color:#333;font-weight:500}\n"] }]
|
|
26319
26319
|
}] });
|
|
26320
26320
|
|
|
@@ -26919,28 +26919,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
|
|
|
26919
26919
|
// Config interfaces for the spa-landing component
|
|
26920
26920
|
// Consumer apps provide this config to drive the entire landing page
|
|
26921
26921
|
// Alsquare brand SVG — dark variant (for navbar on light backgrounds)
|
|
26922
|
-
const ALSQUARE_SVG_DARK = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
26923
|
-
<path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round" fill="none"/>
|
|
26924
|
-
<circle cx="32" cy="36" r="10" fill="white" stroke="#1a3a7a" stroke-width="7"/>
|
|
26925
|
-
<circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
|
|
26926
|
-
<line x1="48" y1="22" x2="48" y2="62" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round"/>
|
|
26927
|
-
<rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
|
|
26928
|
-
<rect x="67" y="0" width="6" height="6" rx="1" fill="#1a3a7a"/>
|
|
26929
|
-
<rect x="67" y="9" width="6" height="6" rx="1" fill="#1a3a7a"/>
|
|
26930
|
-
<rect x="75" y="0" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.7"/>
|
|
26931
|
-
<rect x="75" y="8" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.5"/>
|
|
26922
|
+
const ALSQUARE_SVG_DARK = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
26923
|
+
<path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round" fill="none"/>
|
|
26924
|
+
<circle cx="32" cy="36" r="10" fill="white" stroke="#1a3a7a" stroke-width="7"/>
|
|
26925
|
+
<circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
|
|
26926
|
+
<line x1="48" y1="22" x2="48" y2="62" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round"/>
|
|
26927
|
+
<rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
|
|
26928
|
+
<rect x="67" y="0" width="6" height="6" rx="1" fill="#1a3a7a"/>
|
|
26929
|
+
<rect x="67" y="9" width="6" height="6" rx="1" fill="#1a3a7a"/>
|
|
26930
|
+
<rect x="75" y="0" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.7"/>
|
|
26931
|
+
<rect x="75" y="8" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.5"/>
|
|
26932
26932
|
</svg>`;
|
|
26933
26933
|
// Alsquare brand SVG — white variant (for footer on dark backgrounds)
|
|
26934
|
-
const ALSQUARE_SVG_WHITE = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
26935
|
-
<path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#ffffff" stroke-width="9" stroke-linecap="round" fill="none"/>
|
|
26936
|
-
<circle cx="32" cy="36" r="10" fill="#0f172a" stroke="#ffffff" stroke-width="7"/>
|
|
26937
|
-
<circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
|
|
26938
|
-
<line x1="48" y1="22" x2="48" y2="62" stroke="#ffffff" stroke-width="9" stroke-linecap="round"/>
|
|
26939
|
-
<rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
|
|
26940
|
-
<rect x="67" y="0" width="6" height="6" rx="1" fill="#ffffff"/>
|
|
26941
|
-
<rect x="67" y="9" width="6" height="6" rx="1" fill="#ffffff"/>
|
|
26942
|
-
<rect x="75" y="0" width="5" height="5" rx="1" fill="#ffffff" opacity="0.7"/>
|
|
26943
|
-
<rect x="75" y="8" width="5" height="5" rx="1" fill="#ffffff" opacity="0.5"/>
|
|
26934
|
+
const ALSQUARE_SVG_WHITE = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
26935
|
+
<path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#ffffff" stroke-width="9" stroke-linecap="round" fill="none"/>
|
|
26936
|
+
<circle cx="32" cy="36" r="10" fill="#0f172a" stroke="#ffffff" stroke-width="7"/>
|
|
26937
|
+
<circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
|
|
26938
|
+
<line x1="48" y1="22" x2="48" y2="62" stroke="#ffffff" stroke-width="9" stroke-linecap="round"/>
|
|
26939
|
+
<rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
|
|
26940
|
+
<rect x="67" y="0" width="6" height="6" rx="1" fill="#ffffff"/>
|
|
26941
|
+
<rect x="67" y="9" width="6" height="6" rx="1" fill="#ffffff"/>
|
|
26942
|
+
<rect x="75" y="0" width="5" height="5" rx="1" fill="#ffffff" opacity="0.7"/>
|
|
26943
|
+
<rect x="75" y="8" width="5" height="5" rx="1" fill="#ffffff" opacity="0.5"/>
|
|
26944
26944
|
</svg>`;
|
|
26945
26945
|
|
|
26946
26946
|
/*
|