@firestitch/report 18.0.10 → 18.0.11
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/app/reports/data/report.data.d.ts +4 -0
- package/app/reports/dialogs/global-settings/components/general/general.component.d.ts +8 -0
- package/app/reports/dialogs/global-settings/components/semantic-database/semantic-database.component.d.ts +15 -0
- package/app/reports/dialogs/global-settings/global-settings.component.d.ts +6 -2
- package/app/reports/interfaces/report.interface.d.ts +5 -0
- package/esm2022/app/reports/data/report.data.mjs +15 -1
- package/esm2022/app/reports/dialogs/global-settings/components/general/general.component.mjs +34 -0
- package/esm2022/app/reports/dialogs/global-settings/components/semantic-database/semantic-database.component.mjs +51 -8
- package/esm2022/app/reports/dialogs/global-settings/global-settings.component.mjs +33 -8
- package/esm2022/app/reports/interfaces/report.interface.mjs +1 -1
- package/fesm2022/{firestitch-report-echarts-Cel1iHUg.mjs → firestitch-report-echarts-DaBOoR9Z.mjs} +2 -2
- package/fesm2022/{firestitch-report-echarts-Cel1iHUg.mjs.map → firestitch-report-echarts-DaBOoR9Z.mjs.map} +1 -1
- package/fesm2022/{firestitch-report-firestitch-report-Y1XG_Gms.mjs → firestitch-report-firestitch-report-Cwsww3Jz.mjs} +125 -18
- package/fesm2022/firestitch-report-firestitch-report-Cwsww3Jz.mjs.map +1 -0
- package/fesm2022/firestitch-report.mjs +1 -1
- package/package.json +1 -1
- package/fesm2022/firestitch-report-firestitch-report-Y1XG_Gms.mjs.map +0 -1
|
@@ -245,6 +245,20 @@ class ReportData {
|
|
|
245
245
|
...config,
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
|
+
// The report module's General settings (semantic-build concurrency + its bounds).
|
|
249
|
+
settings(config = {}) {
|
|
250
|
+
return this._api.get(this._path('settings'), {}, {
|
|
251
|
+
key: 'settings',
|
|
252
|
+
...config,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
// Save the General settings; resolves to the same { settings } shape.
|
|
256
|
+
saveSettings(settings, config = {}) {
|
|
257
|
+
return this._api.put(this._path('settings'), settings, {
|
|
258
|
+
key: 'settings',
|
|
259
|
+
...config,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
248
262
|
// Update a filter group — its exposure level ('component' | 'report' | 'both')
|
|
249
263
|
// and/or its default value (the value seeding the report bar on open). Send
|
|
250
264
|
// `default: null` (or an empty default) to clear it. Only the keys provided
|
|
@@ -2527,7 +2541,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
2527
2541
|
// complete synchronously after setOption.
|
|
2528
2542
|
async function renderChartImage(component, data, width, height, pixelRatio = 3) {
|
|
2529
2543
|
// The same tree-shaken echarts build + 'report' theme the live canvas uses.
|
|
2530
|
-
const echarts = (await import('./firestitch-report-echarts-
|
|
2544
|
+
const echarts = (await import('./firestitch-report-echarts-DaBOoR9Z.mjs')).default;
|
|
2531
2545
|
const host = document.createElement('div');
|
|
2532
2546
|
host.style.width = `${width}px`;
|
|
2533
2547
|
host.style.height = `${height}px`;
|
|
@@ -3271,7 +3285,7 @@ class ReportComponent {
|
|
|
3271
3285
|
ReportPdfService,
|
|
3272
3286
|
// Tree-shaken ECharts core + the 'report' house theme, loaded lazily with
|
|
3273
3287
|
// this route's chunk.
|
|
3274
|
-
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-
|
|
3288
|
+
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
|
|
3275
3289
|
], viewQueries: [{ propertyName: "_split", first: true, predicate: ["split"], descendants: true, static: true }, { propertyName: "_chatPanel", first: true, predicate: ["chatPanel"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<div\n #split\n class=\"report fs-row.align-start\">\n <fs-ai-chat\n #chatPanel\n class=\"chat\"\n style=\"min-height: 500px;\"\n basePath=\"reports\"\n [requestData]=\"{ reportId: selected?.id ?? null }\"\n [introMessage]=\"introMessage\"\n (response)=\"onChatResponse($event)\">\n </fs-ai-chat>\n <div\n class=\"resizer\"\n (pointerdown)=\"onResizeStart($event)\">\n </div>\n <div class=\"viewer fs-flex fs-column\">\n <div class=\"fs-row.align-center.gap-sm\">\n <fs-autocomplete-chips\n class=\"fs-flex\"\n [fetch]=\"fetchReports\"\n [(ngModel)]=\"selected\"\n [disabled]=\"loadingReports\"\n [padless]=\"true\"\n [multiple]=\"false\"\n [fetchOnFocus]=\"true\"\n (ngModelChange)=\"reportChange($event)\"\n placeholder=\"Report\"\n name=\"report\">\n <ng-template\n fsAutocompleteChipsTemplate\n let-object=\"object\">\n {{ object.name }}\n </ng-template>\n <ng-template\n fsAutocompleteChipsStatic\n (click)=\"createReport()\">\n Create Report\n </ng-template>\n </fs-autocomplete-chips>\n @if (report) {\n <fs-menu>\n <ng-template\n fs-menu-item\n (click)=\"reportSettings()\">\n <mat-icon>\n tune\n </mat-icon>\n Report settings\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"toggleEditMode()\"\n [disabled]=\"editMode\">\n <mat-icon>\n dashboard_2_edit\n </mat-icon>\n {{ editMode ? 'Done editing layout' : 'Edit layout' }}\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPowerpoint()\">\n <mat-icon>\n slideshow\n </mat-icon>\n Export PowerPoint\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPdf()\">\n <mat-icon>\n picture_as_pdf\n </mat-icon>\n Export PDF\n </ng-template>\n </fs-menu>\n }\n </div>\n @if (report) {\n <!-- Report-level filters only (the report's actions live in the menu\n above). fs-filter reads its config once at init, so it's keyed on the\n filter signature: when the report-level filter set changes the block\n is recreated and re-reads the rebuilt config. -->\n @if (reportHasFilters) {\n @for (key of [reportFilterKey]; track key) {\n <fs-filter [config]=\"reportFilterConfig\"></fs-filter>\n }\n }\n <app-report-canvas\n [report]=\"report\"\n [editMode]=\"editMode\"\n (componentSettings)=\"componentSettings($event)\"\n (reportChanged)=\"onCanvasReportChanged()\"\n (editDone)=\"toggleEditMode()\">\n </app-report-canvas>\n }\n </div>\n</div>", styles: [".report{height:100%;min-height:500px}.report .chat{display:block;flex:0 0 25%;width:100%;height:100%;min-height:500px;min-width:0;border:none}.report .viewer{display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}.report .viewer fs-filter{flex:0 0 auto;margin-top:10px;margin-bottom:0}.report .viewer ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.report .resizer{flex:0 0 11px;align-self:stretch;display:flex;justify-content:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}.report .resizer:before{content:\"\";width:0px;background:#0000001f;transition:width .12s ease,background-color .12s ease}.report .resizer:hover:before{width:3px;background:var(--brand-primary-color)}.report.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.report.resizing .resizer:before{width:3px;background:var(--brand-primary-color)}.report.resizing .chat,.report.resizing .viewer{pointer-events:none}::ng-deep body.body-report-reports .mat-mdc-card-content{display:flex;flex-direction:column;box-sizing:border-box}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel{flex:1;min-height:0}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel router-outlet+ng-component{height:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsAutocompleteChipsModule }, { kind: "component", type: i2$2.FsAutocompleteChipsComponent, selector: "fs-autocomplete-chips", inputs: ["fetch", "appearance", "floatLabel", "readonly", "size", "label", "placeholder", "chipImage", "chipBackground", "chipColor", "chipIcon", "chipIconColor", "chipClass", "chipPadding", "shape", "hint", "allowText", "allowObject", "delay", "minPanelWidth", "maxPanelHeight", "validateText", "removable", "allowClear", "color", "background", "orderable", "padless", "initOnClick", "fetchOnFocus", "multiple", "multipleAdd", "confirm", "disabled", "groupBy", "panelWidth", "panelClass", "compareWith"], outputs: ["selected", "removed", "reordered", "clear", "panelOpened", "panelClosed"] }, { kind: "directive", type: i2$2.FsAutocompleteObjectDirective, selector: "[fsAutocompleteObject],[fsAutocompleteChipsTemplate]" }, { kind: "directive", type: i2$2.FsAutocompleteChipsStaticDirective, selector: "[fsAutocompleteChipsStatic]", inputs: ["show", "disable"], outputs: ["click", "selected"] }, { kind: "ngmodule", type: FsFilterModule }, { kind: "component", type: i2.FilterComponent, selector: "fs-filter", inputs: ["config"], outputs: ["closed", "opened", "ready"] }, { kind: "ngmodule", type: FsMenuModule }, { kind: "component", type: i2$3.FsMenuComponent, selector: "fs-menu", inputs: ["class", "buttonClass", "buttonType", "buttonColor"], outputs: ["opened", "closed"] }, { kind: "directive", type: i2$3.FsMenuItemDirective, selector: "fs-menu-group,[fs-menu-item]" }, { kind: "component", type: FsAiChatComponent, selector: "fs-ai-chat", inputs: ["basePath", "requestData", "introMessage"], outputs: ["response"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ReportCanvasComponent, selector: "app-report-canvas", inputs: ["report", "editMode"], outputs: ["componentSettings", "reportChanged", "editDone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3276
3290
|
}
|
|
3277
3291
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReportComponent, decorators: [{
|
|
@@ -3285,7 +3299,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
3285
3299
|
ReportPdfService,
|
|
3286
3300
|
// Tree-shaken ECharts core + the 'report' house theme, loaded lazily with
|
|
3287
3301
|
// this route's chunk.
|
|
3288
|
-
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-
|
|
3302
|
+
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
|
|
3289
3303
|
], imports: [
|
|
3290
3304
|
FormsModule,
|
|
3291
3305
|
FsAutocompleteChipsModule,
|
|
@@ -3327,6 +3341,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
3327
3341
|
], template: "<div class=\"fs-column tab-body\">\n <p class=\"hint\">\n This document is given to the report AI on every chat message. Use it for\n terminology, business rules and special cases the AI can't work out from the\n data on its own \u2014 it takes effect immediately, no deploy needed.\n </p>\n <fs-markdown-editor\n [ngModel]=\"content()\"\n (ngModelChange)=\"content.set($event)\"\n [config]=\"config()\"\n name=\"content\">\n </fs-markdown-editor>\n</div>\n", styles: [".tab-body{padding-top:16px}.hint{margin:0 0 12px;color:#0009;font-size:13px;line-height:1.5}fs-markdown-editor{display:block;min-height:320px}\n"] }]
|
|
3328
3342
|
}] });
|
|
3329
3343
|
|
|
3344
|
+
// The General tab: report-module settings that aren't the AI knowledge document.
|
|
3345
|
+
// Currently just the semantic-build concurrency — how many summary calls the
|
|
3346
|
+
// build runs at once. A field-level child of the settings dialog form (its input
|
|
3347
|
+
// registers with the parent <form fsForm> via the ControlContainer view
|
|
3348
|
+
// provider), so the dialog's Save button submits and validates it. The value
|
|
3349
|
+
// itself is held on the shell (bound through `concurrency`), and the safe band
|
|
3350
|
+
// (`min`/`max`) comes from the backend so the field can't be set out of range.
|
|
3351
|
+
class GeneralComponent {
|
|
3352
|
+
concurrency = model(0);
|
|
3353
|
+
min = input.required();
|
|
3354
|
+
max = input.required();
|
|
3355
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GeneralComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3356
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: GeneralComponent, isStandalone: true, selector: "fs-ai-report-global-settings-general", inputs: { concurrency: { classPropertyName: "concurrency", publicName: "concurrency", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: true, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { concurrency: "concurrencyChange" }, ngImport: i0, template: "<div class=\"fs-column tab-body\">\n <mat-form-field>\n <mat-label>Semantic build concurrency</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"concurrency()\"\n (ngModelChange)=\"concurrency.set($event)\"\n name=\"semanticBuildConcurrency\"\n [fsFormRequired]=\"true\"\n [fsFormInteger]=\"true\"\n [fsFormMin]=\"min()\"\n [fsFormMax]=\"max()\">\n <mat-hint>\n How many AI calls the semantic-database build runs at once ({{ min() }}\u2013{{ max() }}).\n Higher is faster but more likely to hit the AI provider's rate limit. Default is 8.\n </mat-hint>\n </mat-form-field>\n</div>\n", styles: [".tab-body{padding-top:16px}mat-form-field{display:block;max-width:340px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i2$1.FsFormRequiredDirective, selector: "[fsFormRequired],[ngModel][required]", inputs: ["fsFormRequired", "required", "fsFormRequiredMessage"] }, { kind: "directive", type: i2$1.FsFormMinDirective, selector: "[fsFormMin]", inputs: ["fsFormMin", "fsFormMinMessage"] }, { kind: "directive", type: i2$1.FsFormMaxDirective, selector: "[fsFormMax]", inputs: ["fsFormMax", "fsFormMaxMessage"] }, { kind: "directive", type: i2$1.FsFormIntegerDirective, selector: "[fsFormInteger]", inputs: ["fsFormInteger", "fsFormIntegerMessage"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3357
|
+
}
|
|
3358
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GeneralComponent, decorators: [{
|
|
3359
|
+
type: Component,
|
|
3360
|
+
args: [{ selector: 'fs-ai-report-global-settings-general', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
|
|
3361
|
+
FormsModule,
|
|
3362
|
+
FsFormModule,
|
|
3363
|
+
MatFormField,
|
|
3364
|
+
MatLabel,
|
|
3365
|
+
MatHint,
|
|
3366
|
+
MatInput,
|
|
3367
|
+
], template: "<div class=\"fs-column tab-body\">\n <mat-form-field>\n <mat-label>Semantic build concurrency</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"concurrency()\"\n (ngModelChange)=\"concurrency.set($event)\"\n name=\"semanticBuildConcurrency\"\n [fsFormRequired]=\"true\"\n [fsFormInteger]=\"true\"\n [fsFormMin]=\"min()\"\n [fsFormMax]=\"max()\">\n <mat-hint>\n How many AI calls the semantic-database build runs at once ({{ min() }}\u2013{{ max() }}).\n Higher is faster but more likely to hit the AI provider's rate limit. Default is 8.\n </mat-hint>\n </mat-form-field>\n</div>\n", styles: [".tab-body{padding-top:16px}mat-form-field{display:block;max-width:340px}\n"] }]
|
|
3368
|
+
}] });
|
|
3369
|
+
|
|
3330
3370
|
// The History tab: every saved version of the AI Knowledge document
|
|
3331
3371
|
// (append-only), each previewed and restorable. Restoring mutates the current
|
|
3332
3372
|
// version and reseeds the editor — a parent concern — so this stays
|
|
@@ -3401,6 +3441,9 @@ class SemanticDatabaseComponent {
|
|
|
3401
3441
|
// Per-entity function caches, loaded on first expand. undefined = not
|
|
3402
3442
|
// requested yet, null = request in flight (the skeleton window), object = loaded.
|
|
3403
3443
|
_entityFunctions = signal({});
|
|
3444
|
+
// Guards the outcome message to fire exactly once per run — whether the stream
|
|
3445
|
+
// completes (build finished, possibly failed) or errors outright.
|
|
3446
|
+
_buildFinalized = false;
|
|
3404
3447
|
_reportData = inject(ReportData);
|
|
3405
3448
|
_process = inject(FsProcess);
|
|
3406
3449
|
_prompt = inject(FsPrompt);
|
|
@@ -3441,22 +3484,62 @@ class SemanticDatabaseComponent {
|
|
|
3441
3484
|
// dismissing the dialog never interrupts a running build.
|
|
3442
3485
|
_runBuild() {
|
|
3443
3486
|
this.building.set(true);
|
|
3487
|
+
this._buildFinalized = false;
|
|
3444
3488
|
const target$ = this._reportData.semanticBuild()
|
|
3445
|
-
.pipe(
|
|
3489
|
+
.pipe(
|
|
3490
|
+
// A hard stream/transport failure (the build couldn't even start)
|
|
3491
|
+
// finalizes here — completed$ won't fire in that case.
|
|
3492
|
+
tap({ error: (error) => this._finalizeBuild(error) }), filter((event) => event?.type === StreamEventType.Data
|
|
3446
3493
|
&& event.data?.kind === 'log'
|
|
3447
3494
|
&& typeof event.data.line === 'string'), map((event) => event.data.line));
|
|
3448
3495
|
this._process
|
|
3449
3496
|
.run('Semantic database build', target$)
|
|
3450
3497
|
.completed$
|
|
3451
3498
|
.pipe(take(1), takeUntilDestroyed(this._destroyRef))
|
|
3452
|
-
.subscribe(() =>
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3499
|
+
.subscribe(() => this._finalizeBuild(null));
|
|
3500
|
+
}
|
|
3501
|
+
/**
|
|
3502
|
+
* Wrap up one build run exactly once. On a hard stream error there is no
|
|
3503
|
+
* status to read, so say so directly; otherwise refresh the status and
|
|
3504
|
+
* announce how the build actually ended — a build that failed (e.g. the AI
|
|
3505
|
+
* couldn't be reached) reports 'Failed' with a reason, and that must surface
|
|
3506
|
+
* instead of the run silently doing nothing.
|
|
3507
|
+
*/
|
|
3508
|
+
_finalizeBuild(streamError) {
|
|
3509
|
+
if (this._buildFinalized) {
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
this._buildFinalized = true;
|
|
3513
|
+
this.building.set(false);
|
|
3514
|
+
// The build rewrote every entity's function cache — drop the loaded copies
|
|
3515
|
+
// so the next expand refetches fresh data.
|
|
3516
|
+
this._entityFunctions.set({});
|
|
3517
|
+
if (streamError) {
|
|
3518
|
+
this._message.error('The semantic database build could not run — check the AI connection and try again.');
|
|
3457
3519
|
this._refreshStatus();
|
|
3520
|
+
return;
|
|
3521
|
+
}
|
|
3522
|
+
this._reportData.semanticBuildStatus()
|
|
3523
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3524
|
+
.subscribe((status) => {
|
|
3525
|
+
this.status.set(status);
|
|
3526
|
+
this._announceOutcome(status);
|
|
3458
3527
|
});
|
|
3459
3528
|
}
|
|
3529
|
+
/**
|
|
3530
|
+
* Tell the operator how the build ended: a failed/stopped build surfaces its
|
|
3531
|
+
* reason (so an AI connection problem is never silent), a completed one
|
|
3532
|
+
* confirms success.
|
|
3533
|
+
*/
|
|
3534
|
+
_announceOutcome(status) {
|
|
3535
|
+
const build = status?.build;
|
|
3536
|
+
if (build?.state === 'F' || build?.state === 'K') {
|
|
3537
|
+
this._message.error(build.message || 'The semantic database build did not finish.');
|
|
3538
|
+
}
|
|
3539
|
+
else if (build?.state === 'C') {
|
|
3540
|
+
this._message.success('Semantic database build complete.');
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3460
3543
|
stageColor(stage) {
|
|
3461
3544
|
return stage === 'done'
|
|
3462
3545
|
? SemanticDatabaseComponent.DONE_COLOR
|
|
@@ -3563,8 +3646,10 @@ const SCAFFOLD = [
|
|
|
3563
3646
|
// History — every saved version (append-only), any of which can be restored.
|
|
3564
3647
|
// Semantic Database — the build scoreboard the AI is built from, with a
|
|
3565
3648
|
// self-contained Build action.
|
|
3566
|
-
//
|
|
3567
|
-
//
|
|
3649
|
+
// General — report-module settings (the semantic-build concurrency).
|
|
3650
|
+
// The shell owns the shared data (the instructions document for the first two
|
|
3651
|
+
// tabs, the settings for General) plus the tab-aware save; everything else lives
|
|
3652
|
+
// in the children.
|
|
3568
3653
|
class GlobalSettingsComponent {
|
|
3569
3654
|
selectedTab = 'knowledge';
|
|
3570
3655
|
// The Semantic Database tab component, so the shared footer can drive its
|
|
@@ -3577,8 +3662,13 @@ class GlobalSettingsComponent {
|
|
|
3577
3662
|
maxLength: MAX_LENGTH,
|
|
3578
3663
|
placeholder: 'Terminology, business rules and special cases the report AI should know…',
|
|
3579
3664
|
};
|
|
3580
|
-
// null until loaded —
|
|
3665
|
+
// Both null until loaded — the form waits behind the skeleton until each is in.
|
|
3581
3666
|
instructions = signal(null);
|
|
3667
|
+
settings = signal(null);
|
|
3668
|
+
// The General tab's concurrency draft (bound to its field), seeded from settings.
|
|
3669
|
+
concurrency = signal(0);
|
|
3670
|
+
// Gate the whole form on both loads, so every tab's fields are ready to render.
|
|
3671
|
+
loaded = computed(() => !!this.instructions() && !!this.settings());
|
|
3582
3672
|
_dialogRef = inject(MatDialogRef);
|
|
3583
3673
|
_reportData = inject(ReportData);
|
|
3584
3674
|
_message = inject(FsMessage);
|
|
@@ -3587,8 +3677,17 @@ class GlobalSettingsComponent {
|
|
|
3587
3677
|
constructor() {
|
|
3588
3678
|
this._init();
|
|
3589
3679
|
}
|
|
3590
|
-
// Save
|
|
3680
|
+
// The footer Save is shared, so it saves whatever the current tab owns: the
|
|
3681
|
+
// General tab persists its settings (and stays open); AI Knowledge saves a new
|
|
3682
|
+
// document version and closes.
|
|
3591
3683
|
save = () => {
|
|
3684
|
+
if (this.selectedTab === 'general') {
|
|
3685
|
+
return this._reportData.saveSettings({ semanticBuildConcurrency: this.concurrency() })
|
|
3686
|
+
.pipe(tap((settings) => {
|
|
3687
|
+
this._applySettings(settings);
|
|
3688
|
+
this._message.success('Settings saved');
|
|
3689
|
+
}));
|
|
3690
|
+
}
|
|
3592
3691
|
return this._reportData.saveInstructions(this.content())
|
|
3593
3692
|
.pipe(tap((instructions) => {
|
|
3594
3693
|
this._message.success('AI knowledge saved');
|
|
@@ -3612,13 +3711,20 @@ class GlobalSettingsComponent {
|
|
|
3612
3711
|
this._reportData.instructions()
|
|
3613
3712
|
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3614
3713
|
.subscribe((instructions) => this._applyInstructions(instructions));
|
|
3714
|
+
this._reportData.settings()
|
|
3715
|
+
.pipe(takeUntilDestroyed(this._destroyRef))
|
|
3716
|
+
.subscribe((settings) => this._applySettings(settings));
|
|
3615
3717
|
}
|
|
3616
3718
|
_applyInstructions(instructions) {
|
|
3617
3719
|
this.instructions.set(instructions);
|
|
3618
3720
|
this.content.set(instructions.instructions?.content ?? SCAFFOLD);
|
|
3619
3721
|
}
|
|
3722
|
+
_applySettings(settings) {
|
|
3723
|
+
this.settings.set(settings);
|
|
3724
|
+
this.concurrency.set(settings.semanticBuildConcurrency);
|
|
3725
|
+
}
|
|
3620
3726
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GlobalSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3621
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: GlobalSettingsComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "semanticDatabase", first: true, predicate: SemanticDatabaseComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<form\n *fsSkeletonForm=\"
|
|
3727
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: GlobalSettingsComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "semanticDatabase", first: true, predicate: SemanticDatabaseComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<form\n *fsSkeletonForm=\"loaded()\"\n fsForm\n [submit]=\"save\">\n <fs-dialog>\n <fs-dialog-title>\n Report Settings\n </fs-dialog-title>\n <mat-dialog-content>\n <mat-tab-group [(selected)]=\"selectedTab\">\n <mat-tab\n label=\"AI Knowledge\"\n name=\"knowledge\">\n <fs-ai-report-global-settings-ai-knowledge\n [content]=\"content()\"\n (contentChange)=\"content.set($event)\"\n [config]=\"editorConfig\">\n </fs-ai-report-global-settings-ai-knowledge>\n </mat-tab>\n <mat-tab\n label=\"History\"\n name=\"history\">\n <fs-ai-report-global-settings-history\n [versions]=\"instructions()?.versions ?? []\"\n (restore)=\"restore($event)\">\n </fs-ai-report-global-settings-history>\n </mat-tab>\n <mat-tab\n label=\"Semantic Database\"\n name=\"build\">\n <fs-ai-report-global-settings-semantic-database>\n </fs-ai-report-global-settings-semantic-database>\n </mat-tab>\n <mat-tab\n label=\"General\"\n name=\"general\">\n <fs-ai-report-global-settings-general\n [concurrency]=\"concurrency()\"\n (concurrencyChange)=\"concurrency.set($event)\"\n [min]=\"settings()!.semanticBuildConcurrencyMin\"\n [max]=\"settings()!.semanticBuildConcurrencyMax\">\n </fs-ai-report-global-settings-general>\n </mat-tab>\n </mat-tab-group>\n </mat-dialog-content>\n <mat-dialog-actions>\n <!-- The footer follows the selected tab. AI Knowledge and General are the\n editable tabs, so they get Save/Cancel; History and Semantic Database\n are read/run surfaces, so they get a primary Close. The Semantic\n Database tab also carries its Build/Rebuild/Stop action beside Close. -->\n <fs-form-dialog-actions [save]=\"selectedTab === 'knowledge' || selectedTab === 'general'\">\n @if (selectedTab !== 'knowledge' && selectedTab !== 'general') {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n [mat-dialog-close]=\"undefined\">\n Close\n </button>\n }\n @if (selectedTab === 'build' && semanticDatabase(); as db) {\n @if (db.buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"db.stopBuild()\">\n Stop Build\n </button>\n } @else {\n @if (db.status(); as buildStatus) {\n <button\n type=\"button\"\n mat-button\n (click)=\"db.build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n }\n }\n </fs-form-dialog-actions>\n </mat-dialog-actions>\n </fs-dialog>\n</form>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FsDialogModule }, { kind: "component", type: i3.FsDialogComponent, selector: "fs-dialog", inputs: ["mobileMode", "mobileButtonPlacement", "mobileWidth", "mode", "buttonLayout"] }, { kind: "component", type: i3.FsDialogTitleComponent, selector: "fs-dialog-title", inputs: ["close", "dockToggle", "back"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i2$1.FsFormDirective, selector: "[fsForm]", inputs: ["wrapperSelector", "messageSelector", "hintSelector", "labelSelector", "autocomplete", "shortcuts", "confirm", "confirmDialog", "confirmDrawer", "confirmBrowser", "dirtySubmitButton", "submit", "successDelay", "errorDelay", "deactivationGuard"], outputs: ["fsForm", "invalid", "valid", "submitted", "reseted", "cleared"], exportAs: ["fsForm"] }, { kind: "component", type: i2$1.FsFormDialogActionsComponent, selector: "fs-form-dialog-actions", inputs: ["save", "create", "close", "done", "closeData", "name"] }, { kind: "directive", type: i2$1.FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit"] }, { kind: "ngmodule", type: FsSkeletonModule }, { kind: "directive", type: i6$1.FsSkeletonFormDirective, selector: "[fsSkeletonForm]", inputs: ["fsSkeletonForm", "fsSkeletonFormLines"] }, { kind: "ngmodule", type: FsTabsModule }, { kind: "directive", type: i5.FsTabsHeaderTabGroupDirective, selector: "mat-tab-group, matTabGroup, [matTabGroup]", inputs: ["orientation", "selected", "selectedData"], outputs: ["selectedChange", "selectedDataChange"], exportAs: ["fsTabsHeaderTabGroup"] }, { kind: "directive", type: i5.FsTabsTabDirective, selector: "mat-tab,matTab", inputs: ["name", "data"], exportAs: ["fsTabsTab"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i5$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i5$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: AiKnowledgeComponent, selector: "fs-ai-report-global-settings-ai-knowledge", inputs: ["content", "config"], outputs: ["contentChange"] }, { kind: "component", type: GeneralComponent, selector: "fs-ai-report-global-settings-general", inputs: ["concurrency", "min", "max"], outputs: ["concurrencyChange"] }, { kind: "component", type: HistoryComponent, selector: "fs-ai-report-global-settings-history", inputs: ["versions"], outputs: ["restore"] }, { kind: "component", type: SemanticDatabaseComponent, selector: "fs-ai-report-global-settings-semantic-database" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3622
3728
|
}
|
|
3623
3729
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GlobalSettingsComponent, decorators: [{
|
|
3624
3730
|
type: Component,
|
|
@@ -3634,9 +3740,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
3634
3740
|
MatDialogContent,
|
|
3635
3741
|
MatTabsModule,
|
|
3636
3742
|
AiKnowledgeComponent,
|
|
3743
|
+
GeneralComponent,
|
|
3637
3744
|
HistoryComponent,
|
|
3638
3745
|
SemanticDatabaseComponent,
|
|
3639
|
-
], template: "<form\n *fsSkeletonForm=\"
|
|
3746
|
+
], template: "<form\n *fsSkeletonForm=\"loaded()\"\n fsForm\n [submit]=\"save\">\n <fs-dialog>\n <fs-dialog-title>\n Report Settings\n </fs-dialog-title>\n <mat-dialog-content>\n <mat-tab-group [(selected)]=\"selectedTab\">\n <mat-tab\n label=\"AI Knowledge\"\n name=\"knowledge\">\n <fs-ai-report-global-settings-ai-knowledge\n [content]=\"content()\"\n (contentChange)=\"content.set($event)\"\n [config]=\"editorConfig\">\n </fs-ai-report-global-settings-ai-knowledge>\n </mat-tab>\n <mat-tab\n label=\"History\"\n name=\"history\">\n <fs-ai-report-global-settings-history\n [versions]=\"instructions()?.versions ?? []\"\n (restore)=\"restore($event)\">\n </fs-ai-report-global-settings-history>\n </mat-tab>\n <mat-tab\n label=\"Semantic Database\"\n name=\"build\">\n <fs-ai-report-global-settings-semantic-database>\n </fs-ai-report-global-settings-semantic-database>\n </mat-tab>\n <mat-tab\n label=\"General\"\n name=\"general\">\n <fs-ai-report-global-settings-general\n [concurrency]=\"concurrency()\"\n (concurrencyChange)=\"concurrency.set($event)\"\n [min]=\"settings()!.semanticBuildConcurrencyMin\"\n [max]=\"settings()!.semanticBuildConcurrencyMax\">\n </fs-ai-report-global-settings-general>\n </mat-tab>\n </mat-tab-group>\n </mat-dialog-content>\n <mat-dialog-actions>\n <!-- The footer follows the selected tab. AI Knowledge and General are the\n editable tabs, so they get Save/Cancel; History and Semantic Database\n are read/run surfaces, so they get a primary Close. The Semantic\n Database tab also carries its Build/Rebuild/Stop action beside Close. -->\n <fs-form-dialog-actions [save]=\"selectedTab === 'knowledge' || selectedTab === 'general'\">\n @if (selectedTab !== 'knowledge' && selectedTab !== 'general') {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n [mat-dialog-close]=\"undefined\">\n Close\n </button>\n }\n @if (selectedTab === 'build' && semanticDatabase(); as db) {\n @if (db.buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"db.stopBuild()\">\n Stop Build\n </button>\n } @else {\n @if (db.status(); as buildStatus) {\n <button\n type=\"button\"\n mat-button\n (click)=\"db.build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n }\n }\n </fs-form-dialog-actions>\n </mat-dialog-actions>\n </fs-dialog>\n</form>\n", styles: [":host{display:block}\n"] }]
|
|
3640
3747
|
}], ctorParameters: () => [] });
|
|
3641
3748
|
|
|
3642
3749
|
// The reports listing — an fs-list of reports. Rows are links built from a URL
|
|
@@ -4002,7 +4109,7 @@ class FsAiReportComponent {
|
|
|
4002
4109
|
ReportPptxService,
|
|
4003
4110
|
ReportPdfService,
|
|
4004
4111
|
// Tree-shaken ECharts core + the 'report' house theme, loaded lazily.
|
|
4005
|
-
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-
|
|
4112
|
+
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
|
|
4006
4113
|
], viewQueries: [{ propertyName: "_split", first: true, predicate: ["split"], descendants: true, static: true }, { propertyName: "_chatPanel", first: true, predicate: ["chatPanel"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<div class=\"fs-ai-report fs-column\">\n\n <div class=\"fs-ai-report__header fs-row.align-center\">\n <h2 class=\"fs-ai-report__title fs-flex\">{{ report?.name }}</h2>\n\n @if (report) {\n <!-- Report-level filters, inline with the toolbar actions. fs-filter\n reads its config once at init, so it's keyed on the filter\n signature: when the report-level filter set changes the block is\n recreated and re-reads the rebuilt config. -->\n @if (reportHasFilters) {\n @for (key of [reportFilterKey]; track key) {\n <fs-filter\n class=\"fs-ai-report__filters\"\n [config]=\"reportFilterConfig\">\n </fs-filter>\n }\n }\n\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"editMode ? 'Done' : 'Edit layout'\"\n (click)=\"toggleEditMode()\">\n <mat-icon>{{ editMode ? 'lock' : 'open_with' }}</mat-icon>\n </button>\n\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Export\"\n [fsMenuTriggerFor]=\"exportMenu\">\n <mat-icon>download</mat-icon>\n </button>\n <fs-menu #exportMenu>\n <ng-template\n fs-menu-item\n (click)=\"exportPowerpoint()\">\n Export PowerPoint\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPdf()\">\n Export PDF\n </ng-template>\n </fs-menu>\n\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Report settings\"\n (click)=\"reportSettings()\">\n <mat-icon>settings</mat-icon>\n </button>\n }\n </div>\n\n <div\n #split\n class=\"fs-ai-report__split fs-row.align-start\">\n <fs-ai-chat\n #chatPanel\n class=\"chat\"\n [basePath]=\"basePath()\"\n [requestData]=\"{ reportId: reportId() }\"\n (response)=\"onChatResponse($event)\">\n </fs-ai-chat>\n\n <div\n class=\"resizer\"\n (pointerdown)=\"onResizeStart($event)\">\n </div>\n\n <div class=\"viewer fs-flex fs-column\">\n @if (report) {\n <app-report-canvas\n [report]=\"report\"\n [editMode]=\"editMode\"\n (componentSettings)=\"componentSettings($event)\"\n (reportChanged)=\"onCanvasReportChanged()\"\n (editDone)=\"toggleEditMode()\">\n </app-report-canvas>\n }\n </div>\n </div>\n</div>\n", styles: [".fs-ai-report{height:100%;min-height:500px;display:flex;flex-direction:column}.fs-ai-report__header{flex:0 0 auto;margin-bottom:10px}.fs-ai-report__title{margin:0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fs-ai-report__filters{flex:0 0 auto;display:block;margin:0 5px 0 0}.fs-ai-report__filters ::ng-deep .filter-bar-container{align-items:center!important}.fs-ai-report__filters ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.fs-ai-report__split{flex:1 1 auto;min-height:0}.fs-ai-report .chat{display:block;flex:0 0 25%;width:100%;height:100%;min-height:400px;min-width:0;border:none}.fs-ai-report .viewer{display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}.fs-ai-report .resizer{flex:0 0 11px;align-self:stretch;display:flex;justify-content:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}.fs-ai-report .resizer:before{content:\"\";width:3px;border-radius:3px;background:transparent;transition:background-color .12s ease}.fs-ai-report .resizer:hover:before{background:var(--brand-primary-color)}.fs-ai-report__split.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fs-ai-report__split.resizing .resizer:before{background:var(--brand-primary-color)}.fs-ai-report__split.resizing .chat,.fs-ai-report__split.resizing .viewer{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: FsFilterModule }, { kind: "component", type: i2.FilterComponent, selector: "fs-filter", inputs: ["config"], outputs: ["closed", "opened", "ready"] }, { kind: "ngmodule", type: FsMenuModule }, { kind: "component", type: i2$3.FsMenuComponent, selector: "fs-menu", inputs: ["class", "buttonClass", "buttonType", "buttonColor"], outputs: ["opened", "closed"] }, { kind: "directive", type: i2$3.FsMenuItemDirective, selector: "fs-menu-group,[fs-menu-item]" }, { kind: "directive", type: i2$3.FsMenuTriggerDirective, selector: "[fsMenuTriggerFor]", inputs: ["fsMenuTriggerFor"] }, { kind: "component", type: FsAiChatComponent, selector: "fs-ai-chat", inputs: ["basePath", "requestData", "introMessage"], outputs: ["response"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ReportCanvasComponent, selector: "app-report-canvas", inputs: ["report", "editMode"], outputs: ["componentSettings", "reportChanged", "editDone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4007
4114
|
}
|
|
4008
4115
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FsAiReportComponent, decorators: [{
|
|
@@ -4015,7 +4122,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4015
4122
|
ReportPptxService,
|
|
4016
4123
|
ReportPdfService,
|
|
4017
4124
|
// Tree-shaken ECharts core + the 'report' house theme, loaded lazily.
|
|
4018
|
-
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-
|
|
4125
|
+
provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
|
|
4019
4126
|
], imports: [
|
|
4020
4127
|
FsFilterModule,
|
|
4021
4128
|
FsMenuModule,
|
|
@@ -4046,4 +4153,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4046
4153
|
*/
|
|
4047
4154
|
|
|
4048
4155
|
export { FsAiReportsComponent as F, REPORT_CHART_COLORS_CSS as R, ReportComponent as a, FsAiReportComponent as b, ReportData as c, ReportService as d, ReportFilterStateService as e, FREQUENCY_OPTIONS as f };
|
|
4049
|
-
//# sourceMappingURL=firestitch-report-firestitch-report-
|
|
4156
|
+
//# sourceMappingURL=firestitch-report-firestitch-report-Cwsww3Jz.mjs.map
|