@alimetry/edit-session-widget 0.0.0-stage → 1.0.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,24 @@
1
- # Temporary Holding Version
1
+ # EditSessionWidget
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.1.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Run `ng generate component component-name --project edit-session-widget` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project edit-session-widget`.
8
+ > Note: Don't forget to add `--project edit-session-widget` or else it will be added to the default project in your `angular.json` file.
9
+
10
+ ## Build
11
+
12
+ Run `ng build edit-session-widget` to build the project. The build artifacts will be stored in the `dist/` directory.
13
+
14
+ ## Publishing
15
+
16
+ After building your library with `ng build edit-session-widget`, go to the dist folder `cd dist/edit-session-widget` and run `npm publish`.
17
+
18
+ ## Running unit tests
19
+
20
+ Run `ng test edit-session-widget` to execute the unit tests via [Karma](https://karma-runner.github.io).
21
+
22
+ ## Further help
23
+
24
+ To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ export * from './public-api';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxpbWV0cnktZWRpdC1zZXNzaW9uLXdpZGdldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3dpZGdldHMvZWRpdC1zZXNzaW9uLXdpZGdldC9zcmMvYWxpbWV0cnktZWRpdC1zZXNzaW9uLXdpZGdldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILGNBQWMsY0FBYyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBHZW5lcmF0ZWQgYnVuZGxlIGluZGV4LiBEbyBub3QgZWRpdC5cbiAqL1xuXG5leHBvcnQgKiBmcm9tICcuL3B1YmxpYy1hcGknO1xuIl19
@@ -0,0 +1,161 @@
1
+ import { Component, Input, ViewChild } from '@angular/core';
2
+ import { ServicesConfigService, ServicesService, SessionSearchComponent, isReportGenerated, isReportGenerating, reportLinkOf, sessionNumberOf, statusOf, } from '@alimetry/widget-core';
3
+ import { catchError, EMPTY, exhaustMap, filter, interval, map, Subject, switchMap, take, takeUntil, timer, } from 'rxjs';
4
+ import { EDIT_SESSION_WIDGET_VERSION } from './version';
5
+ import * as i0 from "@angular/core";
6
+ import * as i1 from "@alimetry/widget-core";
7
+ import * as i2 from "@angular/common";
8
+ // how often the widget asks the cloud whether the report it is waiting on is ready
9
+ const REPORT_POLL_INTERVAL_MS = 30000;
10
+ // how often it then re-reads the report link and the file behind it, waiting for that file to be
11
+ // the newly generated one rather than the one it replaced
12
+ const REPORT_FILE_POLL_INTERVAL_MS = 5000;
13
+ export class EditSessionWidgetComponent {
14
+ constructor(servicesConfig, services) {
15
+ this.servicesConfig = servicesConfig;
16
+ this.services = services;
17
+ this.version = EDIT_SESSION_WIDGET_VERSION;
18
+ this.configError = null;
19
+ // set when a poll gave up because the record stopped short of a generated report
20
+ this.reportError = null;
21
+ this.reportBaseline = null;
22
+ // the generation of the report currently on screen, as the viewer last loaded it
23
+ this.reportLastModified = null;
24
+ // set while a poll is running that started before the viewer had read the report on screen, so
25
+ // the generation that poll is waiting to replace has still to be recorded
26
+ this.needsBaseline = false;
27
+ // ends the poll a save started: on the next one, on a new search, and on teardown
28
+ this.stopPolling$ = new Subject();
29
+ }
30
+ ngOnChanges() {
31
+ const deviceDataModelId = this.config?.deviceDataModelId?.trim() ?? '';
32
+ const apiUrl = this.config?.apiUrl?.trim() ?? '';
33
+ const appDataPropertySetId = this.config?.appDataPropertySetId?.trim() ?? '';
34
+ const devicePropertySetId = this.config?.devicePropertySetId?.trim() ?? '';
35
+ if (!deviceDataModelId || !apiUrl) {
36
+ this.configError =
37
+ 'This widget is missing required configuration values. Please check the widget configuration.';
38
+ this.sessionData = undefined;
39
+ return;
40
+ }
41
+ this.configError = null;
42
+ // hand the values the services layer needs to it through dependency injection
43
+ this.servicesConfig.setConfig({
44
+ deviceDataModelId,
45
+ apiUrl,
46
+ appDataPropertySetId,
47
+ devicePropertySetId,
48
+ });
49
+ }
50
+ onSessionDataLoaded(sessionData) {
51
+ // whatever the last save was waiting on, it is not this session
52
+ this.stopPolling$.next();
53
+ this.sessionData = sessionData;
54
+ this.reportError = null;
55
+ this.reportBaseline = null;
56
+ this.reportLastModified = null;
57
+ this.needsBaseline = false;
58
+ // a session that is already regenerating when it is searched for is waited on the same way one
59
+ // this widget just regenerated is, so reloading the page picks the wait back up
60
+ if (isReportGenerating(statusOf(sessionData))) {
61
+ this.onRegenerateRequested(true);
62
+ }
63
+ }
64
+ // clears the search box and drops the session, which empties the form and the report with it
65
+ onCancelled() {
66
+ this.sessionSearch?.clear();
67
+ this.onSessionDataLoaded(undefined);
68
+ }
69
+ onReportLoaded(lastModified) {
70
+ this.reportLastModified = lastModified;
71
+ // the poll a reload started has no generation to wait past until this first read arrives
72
+ if (this.needsBaseline) {
73
+ this.reportBaseline = lastModified;
74
+ this.needsBaseline = false;
75
+ }
76
+ }
77
+ onRegenerateRequested(requested) {
78
+ if (!requested) {
79
+ return;
80
+ }
81
+ this.reportError = null;
82
+ this.reportBaseline = this.reportLastModified;
83
+ this.needsBaseline = !this.reportBaseline;
84
+ this.pollForReport();
85
+ }
86
+ ngOnDestroy() {
87
+ this.stopPolling$.next();
88
+ this.stopPolling$.complete();
89
+ }
90
+ pollForReport() {
91
+ const sessionNumber = this.sessionNumber;
92
+ if (!sessionNumber) {
93
+ return;
94
+ }
95
+ // whichever poll is already running is waiting on a report this save has superseded
96
+ this.stopPolling$.next();
97
+ interval(REPORT_POLL_INTERVAL_MS)
98
+ .pipe(exhaustMap(() => this.services.getSessionProperty(sessionNumber, 'Status').pipe(map((sessionData) => statusOf(sessionData)), catchError(() => EMPTY))),
99
+ // anything other than "generating report" is as far as this report is going to get, so the
100
+ // poll ends on it either way rather than waiting out a status that will never change
101
+ filter((status) => !isReportGenerating(status)), take(1), takeUntil(this.stopPolling$))
102
+ .subscribe((status) => {
103
+ if (isReportGenerated(status)) {
104
+ this.pollForNewReportFile(sessionNumber);
105
+ }
106
+ else {
107
+ this.onReportStopped(sessionNumber);
108
+ }
109
+ });
110
+ }
111
+ pollForNewReportFile(sessionNumber) {
112
+ // with no generation to compare against there is nothing to wait for: whatever the link points
113
+ // at is the report to show, and reading it here only downloads a file the viewer downloads again
114
+ if (!this.reportBaseline) {
115
+ this.loadSession(sessionNumber);
116
+ return;
117
+ }
118
+ // poll to check if new report is ready
119
+ timer(0, REPORT_FILE_POLL_INTERVAL_MS)
120
+ .pipe(exhaustMap(() => this.services.getSessionProperty(sessionNumber, 'PdfReport').pipe(map((sessionData) => reportLinkOf(sessionData)), filter((link) => !!link), switchMap((link) => this.services.getReportLastModified(link)), catchError(() => EMPTY))), filter((lastModified) => this.isNewReport(lastModified)), take(1), takeUntil(this.stopPolling$))
121
+ .subscribe(() => this.loadSession(sessionNumber));
122
+ }
123
+ // the record settled on a status that is not a generated report: say so, and read the record back
124
+ // so the form unlocks rather than staying held by a regeneration that is over
125
+ onReportStopped(sessionNumber) {
126
+ this.reportError = 'Report regeneration failed. Please try again.';
127
+ this.loadSession(sessionNumber);
128
+ }
129
+ // the polls above read one property at a time, so the whole record is read back once, here, to
130
+ // show the session the regeneration has left behind
131
+ loadSession(sessionNumber) {
132
+ this.services
133
+ .getSessionData(sessionNumber)
134
+ .pipe(filter((sessionData) => !!sessionData), catchError(() => EMPTY), takeUntil(this.stopPolling$))
135
+ .subscribe((sessionData) => this.showSession(sessionData));
136
+ }
137
+ showSession(sessionData) {
138
+ this.needsBaseline = false;
139
+ this.sessionData = sessionData;
140
+ }
141
+ // check if report is new
142
+ isNewReport(lastModified) {
143
+ const baseline = this.reportBaseline;
144
+ return !!lastModified && !!baseline && lastModified.getTime() > baseline.getTime();
145
+ }
146
+ get sessionNumber() {
147
+ return sessionNumberOf(this.sessionData);
148
+ }
149
+ }
150
+ EditSessionWidgetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetComponent, deps: [{ token: i1.ServicesConfigService }, { token: i1.ServicesService }], target: i0.ɵɵFactoryTarget.Component });
151
+ EditSessionWidgetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: EditSessionWidgetComponent, selector: "lib-edit-session-widget", inputs: { config: "config" }, providers: [ServicesConfigService, ServicesService], viewQueries: [{ propertyName: "sessionSearch", first: true, predicate: SessionSearchComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n", styles: [".edit-session-widget__empty{margin:1rem 0 0;color:#6c757d;font-size:14px}.edit-session-widget__error{margin:0;color:#b02a37}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.SessionSearchComponent, selector: "lib-session-search", outputs: ["sessionDataLoaded"] }, { kind: "component", type: i1.ReportViewerComponent, selector: "lib-report-viewer", inputs: ["sessionData"], outputs: ["reportLoaded"] }, { kind: "component", type: i1.DetailEditComponent, selector: "lib-detail-edit", inputs: ["sessionData"], outputs: ["regenerateRequested", "cancelled"] }, { kind: "component", type: i1.VersionFooterComponent, selector: "lib-version-footer", inputs: ["version"] }] });
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetComponent, decorators: [{
153
+ type: Component,
154
+ args: [{ selector: 'lib-edit-session-widget', providers: [ServicesConfigService, ServicesService], template: "<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n", styles: [".edit-session-widget__empty{margin:1rem 0 0;color:#6c757d;font-size:14px}.edit-session-widget__error{margin:0;color:#b02a37}\n"] }]
155
+ }], ctorParameters: function () { return [{ type: i1.ServicesConfigService }, { type: i1.ServicesService }]; }, propDecorators: { config: [{
156
+ type: Input
157
+ }], sessionSearch: [{
158
+ type: ViewChild,
159
+ args: [SessionSearchComponent]
160
+ }] } });
161
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRpdC1zZXNzaW9uLXdpZGdldC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi93aWRnZXRzL2VkaXQtc2Vzc2lvbi13aWRnZXQvc3JjL2xpYi9lZGl0LXNlc3Npb24td2lkZ2V0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL3dpZGdldHMvZWRpdC1zZXNzaW9uLXdpZGdldC9zcmMvbGliL2VkaXQtc2Vzc2lvbi13aWRnZXQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQXdCLFNBQVMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNsRixPQUFPLEVBRUwscUJBQXFCLEVBQ3JCLGVBQWUsRUFDZixzQkFBc0IsRUFDdEIsaUJBQWlCLEVBQ2pCLGtCQUFrQixFQUNsQixZQUFZLEVBQ1osZUFBZSxFQUNmLFFBQVEsR0FDVCxNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFDTCxVQUFVLEVBQ1YsS0FBSyxFQUNMLFVBQVUsRUFDVixNQUFNLEVBQ04sUUFBUSxFQUNSLEdBQUcsRUFDSCxPQUFPLEVBQ1AsU0FBUyxFQUNULElBQUksRUFDSixTQUFTLEVBQ1QsS0FBSyxHQUNOLE1BQU0sTUFBTSxDQUFDO0FBQ2QsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sV0FBVyxDQUFDOzs7O0FBRXhELG1GQUFtRjtBQUNuRixNQUFNLHVCQUF1QixHQUFHLEtBQU0sQ0FBQztBQUV2QyxpR0FBaUc7QUFDakcsMERBQTBEO0FBQzFELE1BQU0sNEJBQTRCLEdBQUcsSUFBSyxDQUFDO0FBUzNDLE1BQU0sT0FBTywwQkFBMEI7SUEyQnJDLFlBQ1UsY0FBcUMsRUFDckMsUUFBeUI7UUFEekIsbUJBQWMsR0FBZCxjQUFjLENBQXVCO1FBQ3JDLGFBQVEsR0FBUixRQUFRLENBQWlCO1FBekIxQixZQUFPLEdBQUcsMkJBQTJCLENBQUM7UUFNL0MsZ0JBQVcs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dkQsU0FBUzt1QkFBQyxzQkFBc0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0LCBPbkNoYW5nZXMsIE9uRGVzdHJveSwgVmlld0NoaWxkIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBEZXZpY2VEYXRhLFxuICBTZXJ2aWNlc0NvbmZpZ1NlcnZpY2UsXG4gIFNlcnZpY2VzU2VydmljZSxcbiAgU2Vzc2lvblNlYXJjaENvbXBvbmVudCxcbiAgaXNSZXBvcnRHZW5lcmF0ZWQsXG4gIGlzUmVwb3J0R2VuZXJhdGluZyxcbiAgcmVwb3J0TGlua09mLFxuICBzZXNzaW9uTnVtYmVyT2YsXG4gIHN0YXR1c09mLFxufSBmcm9tICdAYWxpbWV0cnkvd2lkZ2V0LWNvcmUnO1xuaW1wb3J0IHtcbiAgY2F0Y2hFcnJvcixcbiAgRU1QVFksXG4gIGV4aGF1c3RNYXAsXG4gIGZpbHRlcixcbiAgaW50ZXJ2YWwsXG4gIG1hcCxcbiAgU3ViamVjdCxcbiAgc3dpdGNoTWFwLFxuICB0YWtlLFxuICB0YWtlVW50aWwsXG4gIHRpbWVyLFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEVESVRfU0VTU0lPTl9XSURHRVRfVkVSU0lPTiB9IGZyb20gJy4vdmVyc2lvbic7XG5cbi8vIGhvdyBvZnRlbiB0aGUgd2lkZ2V0IGFza3MgdGhlIGNsb3VkIHdoZXRoZXIgdGhlIHJlcG9ydCBpdCBpcyB3YWl0aW5nIG9uIGlzIHJlYWR5XG5jb25zdCBSRVBPUlRfUE9MTF9JTlRFUlZBTF9NUyA9IDMwXzAwMDtcblxuLy8gaG93IG9mdGVuIGl0IHRoZW4gcmUtcmVhZHMgdGhlIHJlcG9ydCBsaW5rIGFuZCB0aGUgZmlsZSBiZWhpbmQgaXQsIHdhaXRpbmcgZm9yIHRoYXQgZmlsZSB0byBiZVxuLy8gdGhlIG5ld2x5IGdlbmVyYXRlZCBvbmUgcmF0aGVyIHRoYW4gdGhlIG9uZSBpdCByZXBsYWNlZFxuY29uc3QgUkVQT1JUX0ZJTEVfUE9MTF9JTlRFUlZBTF9NUyA9IDVfMDAwO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdsaWItZWRpdC1zZXNzaW9uLXdpZGdldCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9lZGl0LXNlc3Npb24td2lkZ2V0LmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vZWRpdC1zZXNzaW9uLXdpZGdldC5jb21wb25lbnQuY3NzJ10sXG4gIC8vIHNjb3BlZCB0byB0aGlzIHdpZGdldCBzbyBlYWNoIGluc3RhbmNlIHJ1bnMgYWdhaW5zdCB0aGUgY29uZmlnIGl0IHdhcyBnaXZlblxuICBwcm92aWRlcnM6IFtTZXJ2aWNlc0NvbmZpZ1NlcnZpY2UsIFNlcnZpY2VzU2VydmljZV0sXG59KVxuZXhwb3J0IGNsYXNzIEVkaXRTZXNzaW9uV2lkZ2V0Q29tcG9uZW50IGltcGxlbWVudHMgT25DaGFuZ2VzLCBPbkRlc3Ryb3kge1xuICAvLyBUT0RPOiBjaGFuZ2UgdGhlIHR5cGUgb2YgdGhlIGlucHV0IHRvIG5vdCBiZSBhbnlcbiAgQElucHV0KCkgY29uZmlnOiBhbnk7XG5cbiAgcmVhZG9ubHkgdmVyc2lvbiA9IEVESVRfU0VTU0lPTl9XSURHRVRfVkVSU0lPTjtcblxuICAvLyBpbnNpZGUgdGhlIGNvbmZpZyBjaGVjaydzIHRlbXBsYXRlLCBzbyBpdCBpcyBvbmx5IHRoZXJlIG9uY2UgdGhlIGNvbmZpZyBpcyB2YWxpZFxuICBAVmlld0NoaWxkKFNlc3Npb25TZWFyY2hDb21wb25lbnQpIHByaXZhdGUgc2Vzc2lvblNlYXJjaD86IFNlc3Npb25TZWFyY2hDb21wb25lbnQ7XG5cbiAgc2Vzc2lvbkRhdGE/OiBEZXZpY2VEYXRhO1xuICBjb25maWdFcnJvcjogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG5cbiAgLy8gc2V0IHdoZW4gYSBwb2xsIGdhdmUgdXAgYmVjYXVzZSB0aGUgcmVjb3JkIHN0b3BwZWQgc2hvcnQgb2YgYSBnZW5lcmF0ZWQgcmVwb3J0XG4gIHJlcG9ydEVycm9yOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcblxuICByZXBvcnRCYXNlbGluZTogRGF0ZSB8IG51bGwgPSBudWxsO1xuXG4gIC8vIHRoZSBnZW5lcmF0aW9uIG9mIHRoZSByZXBvcnQgY3VycmVudGx5IG9uIHNjcmVlbiwgYXMgdGhlIHZpZXdlciBsYXN0IGxvYWRlZCBpdFxuICBwcml2YXRlIHJlcG9ydExhc3RNb2RpZmllZDogRGF0ZSB8IG51bGwgPSBudWxsO1xuXG4gIC8vIHNldCB3aGlsZSBhIHBvbGwgaXMgcnVubmluZyB0aGF0IHN0YXJ0ZWQgYmVmb3JlIHRoZSB2aWV3ZXIgaGFkIHJlYWQgdGhlIHJlcG9ydCBvbiBzY3JlZW4sIHNvXG4gIC8vIHRoZSBnZW5lcmF0aW9uIHRoYXQgcG9sbCBpcyB3YWl0aW5nIHRvIHJlcGxhY2UgaGFzIHN0aWxsIHRvIGJlIHJlY29yZGVkXG4gIHByaXZhdGUgbmVlZHNCYXNlbGluZSA9IGZhbHNlO1xuXG4gIC8vIGVuZHMgdGhlIHBvbGwgYSBzYXZlIHN0YXJ0ZWQ6IG9uIHRoZSBuZXh0IG9uZSwgb24gYSBuZXcgc2VhcmNoLCBhbmQgb24gdGVhcmRvd25cbiAgcHJpdmF0ZSByZWFkb25seSBzdG9wUG9sbGluZyQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgc2VydmljZXNDb25maWc6IFNlcnZpY2VzQ29uZmlnU2VydmljZSxcbiAgICBwcml2YXRlIHNlcnZpY2VzOiBTZXJ2aWNlc1NlcnZpY2UsXG4gICkge31cblxuICBuZ09uQ2hhbmdlcygpOiB2b2lkIHtcbiAgICBjb25zdCBkZXZpY2VEYXRhTW9kZWxJZDogc3RyaW5nID0gdGhpcy5jb25maWc/LmRldmljZURhdGFNb2RlbElkPy50cmltKCkgPz8gJyc7XG4gICAgY29uc3QgYXBpVXJsOiBzdHJpbmcgPSB0aGlzLmNvbmZpZz8uYXBpVXJsPy50cmltKCkgPz8gJyc7XG4gICAgY29uc3QgYXBwRGF0YVByb3BlcnR5U2V0SWQ6IHN0cmluZyA9IHRoaXMuY29uZmlnPy5hcHBEYXRhUHJvcGVydHlTZXRJZD8udHJpbSgpID8/ICcnO1xuICAgIGNvbnN0IGRldmljZVByb3BlcnR5U2V0SWQ6IHN0cmluZyA9IHRoaXMuY29uZmlnPy5kZXZpY2VQcm9wZXJ0eVNldElkPy50cmltKCkgPz8gJyc7XG5cbiAgICBpZiAoIWRldmljZURhdGFNb2RlbElkIHx8ICFhcGlVcmwpIHtcbiAgICAgIHRoaXMuY29uZmlnRXJyb3IgPVxuICAgICAgICAnVGhpcyB3aWRnZXQgaXMgbWlzc2luZyByZXF1aXJlZCBjb25maWd1cmF0aW9uIHZhbHVlcy4gUGxlYXNlIGNoZWNrIHRoZSB3aWRnZXQgY29uZmlndXJhdGlvbi4nO1xuICAgICAgdGhpcy5zZXNzaW9uRGF0YSA9IHVuZGVmaW5lZDtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmNvbmZpZ0Vycm9yID0gbnVsbDtcbiAgICAvLyBoYW5kIHRoZSB2YWx1ZXMgdGhlIHNlcnZpY2VzIGxheWVyIG5lZWRzIHRvIGl0IHRocm91Z2ggZGVwZW5kZW5jeSBpbmplY3Rpb25cbiAgICB0aGlzLnNlcnZpY2VzQ29uZmlnLnNldENvbmZpZyh7XG4gICAgICBkZXZpY2VEYXRhTW9kZWxJZCxcbiAgICAgIGFwaVVybCxcbiAgICAgIGFwcERhdGFQcm9wZXJ0eVNldElkLFxuICAgICAgZGV2aWNlUHJvcGVydHlTZXRJZCxcbiAgICB9KTtcbiAgfVxuXG4gIG9uU2Vzc2lvbkRhdGFMb2FkZWQoc2Vzc2lvbkRhdGE6IERldmljZURhdGEgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgICAvLyB3aGF0ZXZlciB0aGUgbGFzdCBzYXZlIHdhcyB3YWl0aW5nIG9uLCBpdCBpcyBub3QgdGhpcyBzZXNzaW9uXG4gICAgdGhpcy5zdG9wUG9sbGluZyQubmV4dCgpO1xuXG4gICAgdGhpcy5zZXNzaW9uRGF0YSA9IHNlc3Npb25EYXRhO1xuICAgIHRoaXMucmVwb3J0RXJyb3IgPSBudWxsO1xuICAgIHRoaXMucmVwb3J0QmFzZWxpbmUgPSBudWxsO1xuICAgIHRoaXMucmVwb3J0TGFzdE1vZGlmaWVkID0gbnVsbDtcbiAgICB0aGlzLm5lZWRzQmFzZWxpbmUgPSBmYWxzZTtcblxuICAgIC8vIGEgc2Vzc2lvbiB0aGF0IGlzIGFscmVhZHkgcmVnZW5lcmF0aW5nIHdoZW4gaXQgaXMgc2VhcmNoZWQgZm9yIGlzIHdhaXRlZCBvbiB0aGUgc2FtZSB3YXkgb25lXG4gICAgLy8gdGhpcyB3aWRnZXQganVzdCByZWdlbmVyYXRlZCBpcywgc28gcmVsb2FkaW5nIHRoZSBwYWdlIHBpY2tzIHRoZSB3YWl0IGJhY2sgdXBcbiAgICBpZiAoaXNSZXBvcnRHZW5lcmF0aW5nKHN0YXR1c09mKHNlc3Npb25EYXRhKSkpIHtcbiAgICAgIHRoaXMub25SZWdlbmVyYXRlUmVxdWVzdGVkKHRydWUpO1xuICAgIH1cbiAgfVxuXG4gIC8vIGNsZWFycyB0aGUgc2VhcmNoIGJveCBhbmQgZHJvcHMgdGhlIHNlc3Npb24sIHdoaWNoIGVtcHRpZXMgdGhlIGZvcm0gYW5kIHRoZSByZXBvcnQgd2l0aCBpdFxuICBvbkNhbmNlbGxlZCgpOiB2b2lkIHtcbiAgICB0aGlzLnNlc3Npb25TZWFyY2g/LmNsZWFyKCk7XG4gICAgdGhpcy5vblNlc3Npb25EYXRhTG9hZGVkKHVuZGVmaW5lZCk7XG4gIH1cblxuICBvblJlcG9ydExvYWRlZChsYXN0TW9kaWZpZWQ6IERhdGUgfCBudWxsKTogdm9pZCB7XG4gICAgdGhpcy5yZXBvcnRMYXN0TW9kaWZpZWQgPSBsYXN0TW9kaWZpZWQ7XG5cbiAgICAvLyB0aGUgcG9sbCBhIHJlbG9hZCBzdGFydGVkIGhhcyBubyBnZW5lcmF0aW9uIHRvIHdhaXQgcGFzdCB1bnRpbCB0aGlzIGZpcnN0IHJlYWQgYXJyaXZlc1xuICAgIGlmICh0aGlzLm5lZWRzQmFzZWxpbmUpIHtcbiAgICAgIHRoaXMucmVwb3J0QmFzZWxpbmUgPSBsYXN0TW9kaWZpZWQ7XG4gICAgICB0aGlzLm5lZWRzQmFzZWxpbmUgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBvblJlZ2VuZXJhdGVSZXF1ZXN0ZWQocmVxdWVzdGVkOiBib29sZWFuKTogdm9pZCB7XG4gICAgaWYgKCFyZXF1ZXN0ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnJlcG9ydEVycm9yID0gbnVsbDtcbiAgICB0aGlzLnJlcG9ydEJhc2VsaW5lID0gdGhpcy5yZXBvcnRMYXN0TW9kaWZpZWQ7XG4gICAgdGhpcy5uZWVkc0Jhc2VsaW5lID0gIXRoaXMucmVwb3J0QmFzZWxpbmU7XG4gICAgdGhpcy5wb2xsRm9yUmVwb3J0KCk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLnN0b3BQb2xsaW5nJC5uZXh0KCk7XG4gICAgdGhpcy5zdG9wUG9sbGluZyQuY29tcGxldGUoKTtcbiAgfVxuXG4gIHByaXZhdGUgcG9sbEZvclJlcG9ydCgpOiB2b2lkIHtcbiAgICBjb25zdCBzZXNzaW9uTnVtYmVyID0gdGhpcy5zZXNzaW9uTnVtYmVyO1xuICAgIGlmICghc2Vzc2lvbk51bWJlcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIHdoaWNoZXZlciBwb2xsIGlzIGFscmVhZHkgcnVubmluZyBpcyB3YWl0aW5nIG9uIGEgcmVwb3J0IHRoaXMgc2F2ZSBoYXMgc3VwZXJzZWRlZFxuICAgIHRoaXMuc3RvcFBvbGxpbmckLm5leHQoKTtcblxuICAgIGludGVydmFsKFJFUE9SVF9QT0xMX0lOVEVSVkFMX01TKVxuICAgICAgLnBpcGUoXG4gICAgICAgIGV4aGF1c3RNYXAoKCkgPT5cbiAgICAgICAgICB0aGlzLnNlcnZpY2VzLmdldFNlc3Npb25Qcm9wZXJ0eShzZXNzaW9uTnVtYmVyLCAnU3RhdHVzJykucGlwZShcbiAgICAgICAgICAgIG1hcCgoc2Vzc2lvbkRhdGEpID0+IHN0YXR1c09mKHNlc3Npb25EYXRhKSksXG4gICAgICAgICAgICBjYXRjaEVycm9yKCgpID0+IEVNUFRZKSxcbiAgICAgICAgICApLFxuICAgICAgICApLFxuICAgICAgICAvLyBhbnl0aGluZyBvdGhlciB0aGFuIFwiZ2VuZXJhdGluZyByZXBvcnRcIiBpcyBhcyBmYXIgYXMgdGhpcyByZXBvcnQgaXMgZ29pbmcgdG8gZ2V0LCBzbyB0aGVcbiAgICAgICAgLy8gcG9sbCBlbmRzIG9uIGl0IGVpdGhlciB3YXkgcmF0aGVyIHRoYW4gd2FpdGluZyBvdXQgYSBzdGF0dXMgdGhhdCB3aWxsIG5ldmVyIGNoYW5nZVxuICAgICAgICBmaWx0ZXIoKHN0YXR1cykgPT4gIWlzUmVwb3J0R2VuZXJhdGluZyhzdGF0dXMpKSxcbiAgICAgICAgdGFrZSgxKSxcbiAgICAgICAgdGFrZVVudGlsKHRoaXMuc3RvcFBvbGxpbmckKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKHN0YXR1cykgPT4ge1xuICAgICAgICBpZiAoaXNSZXBvcnRHZW5lcmF0ZWQoc3RhdHVzKSkge1xuICAgICAgICAgIHRoaXMucG9sbEZvck5ld1JlcG9ydEZpbGUoc2Vzc2lvbk51bWJlcik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5vblJlcG9ydFN0b3BwZWQoc2Vzc2lvbk51bWJlcik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9XG5cbiAgcHJpdmF0ZSBwb2xsRm9yTmV3UmVwb3J0RmlsZShzZXNzaW9uTnVtYmVyOiBzdHJpbmcpOiB2b2lkIHtcbiAgICAvLyB3aXRoIG5vIGdlbmVyYXRpb24gdG8gY29tcGFyZSBhZ2FpbnN0IHRoZXJlIGlzIG5vdGhpbmcgdG8gd2FpdCBmb3I6IHdoYXRldmVyIHRoZSBsaW5rIHBvaW50c1xuICAgIC8vIGF0IGlzIHRoZSByZXBvcnQgdG8gc2hvdywgYW5kIHJlYWRpbmcgaXQgaGVyZSBvbmx5IGRvd25sb2FkcyBhIGZpbGUgdGhlIHZpZXdlciBkb3dubG9hZHMgYWdhaW5cbiAgICBpZiAoIXRoaXMucmVwb3J0QmFzZWxpbmUpIHtcbiAgICAgIHRoaXMubG9hZFNlc3Npb24oc2Vzc2lvbk51bWJlcik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gcG9sbCB0byBjaGVjayBpZiBuZXcgcmVwb3J0IGlzIHJlYWR5XG4gICAgdGltZXIoMCwgUkVQT1JUX0ZJTEVfUE9MTF9JTlRFUlZBTF9NUylcbiAgICAgIC5waXBlKFxuICAgICAgICBleGhhdXN0TWFwKCgpID0+XG4gICAgICAgICAgdGhpcy5zZXJ2aWNlcy5nZXRTZXNzaW9uUHJvcGVydHkoc2Vzc2lvbk51bWJlciwgJ1BkZlJlcG9ydCcpLnBpcGUoXG4gICAgICAgICAgICBtYXAoKHNlc3Npb25EYXRhKSA9PiByZXBvcnRMaW5rT2Yoc2Vzc2lvbkRhdGEpKSxcbiAgICAgICAgICAgIGZpbHRlcigobGluaykgPT4gISFsaW5rKSxcbiAgICAgICAgICAgIHN3aXRjaE1hcCgobGluaykgPT4gdGhpcy5zZXJ2aWNlcy5nZXRSZXBvcnRMYXN0TW9kaWZpZWQobGluaykpLFxuICAgICAgICAgICAgY2F0Y2hFcnJvcigoKSA9PiBFTVBUWSksXG4gICAgICAgICAgKSxcbiAgICAgICAgKSxcbiAgICAgICAgZmlsdGVyKChsYXN0TW9kaWZpZWQpID0+IHRoaXMuaXNOZXdSZXBvcnQobGFzdE1vZGlmaWVkKSksXG4gICAgICAgIHRha2UoMSksXG4gICAgICAgIHRha2VVbnRpbCh0aGlzLnN0b3BQb2xsaW5nJCksXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKCgpID0+IHRoaXMubG9hZFNlc3Npb24oc2Vzc2lvbk51bWJlcikpO1xuICB9XG5cbiAgLy8gdGhlIHJlY29yZCBzZXR0bGVkIG9uIGEgc3RhdHVzIHRoYXQgaXMgbm90IGEgZ2VuZXJhdGVkIHJlcG9ydDogc2F5IHNvLCBhbmQgcmVhZCB0aGUgcmVjb3JkIGJhY2tcbiAgLy8gc28gdGhlIGZvcm0gdW5sb2NrcyByYXRoZXIgdGhhbiBzdGF5aW5nIGhlbGQgYnkgYSByZWdlbmVyYXRpb24gdGhhdCBpcyBvdmVyXG4gIHByaXZhdGUgb25SZXBvcnRTdG9wcGVkKHNlc3Npb25OdW1iZXI6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMucmVwb3J0RXJyb3IgPSAnUmVwb3J0IHJlZ2VuZXJhdGlvbiBmYWlsZWQuIFBsZWFzZSB0cnkgYWdhaW4uJztcbiAgICB0aGlzLmxvYWRTZXNzaW9uKHNlc3Npb25OdW1iZXIpO1xuICB9XG5cbiAgLy8gdGhlIHBvbGxzIGFib3ZlIHJlYWQgb25lIHByb3BlcnR5IGF0IGEgdGltZSwgc28gdGhlIHdob2xlIHJlY29yZCBpcyByZWFkIGJhY2sgb25jZSwgaGVyZSwgdG9cbiAgLy8gc2hvdyB0aGUgc2Vzc2lvbiB0aGUgcmVnZW5lcmF0aW9uIGhhcyBsZWZ0IGJlaGluZFxuICBwcml2YXRlIGxvYWRTZXNzaW9uKHNlc3Npb25OdW1iZXI6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuc2VydmljZXNcbiAgICAgIC5nZXRTZXNzaW9uRGF0YShzZXNzaW9uTnVtYmVyKVxuICAgICAgLnBpcGUoXG4gICAgICAgIGZpbHRlcigoc2Vzc2lvbkRhdGEpOiBzZXNzaW9uRGF0YSBpcyBEZXZpY2VEYXRhID0+ICEhc2Vzc2lvbkRhdGEpLFxuICAgICAgICBjYXRjaEVycm9yKCgpID0+IEVNUFRZKSxcbiAgICAgICAgdGFrZVVudGlsKHRoaXMuc3RvcFBvbGxpbmckKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKHNlc3Npb25EYXRhKSA9PiB0aGlzLnNob3dTZXNzaW9uKHNlc3Npb25EYXRhKSk7XG4gIH1cblxuICBwcml2YXRlIHNob3dTZXNzaW9uKHNlc3Npb25EYXRhOiBEZXZpY2VEYXRhKTogdm9pZCB7XG4gICAgdGhpcy5uZWVkc0Jhc2VsaW5lID0gZmFsc2U7XG4gICAgdGhpcy5zZXNzaW9uRGF0YSA9IHNlc3Npb25EYXRhO1xuICB9XG5cbiAgLy8gY2hlY2sgaWYgcmVwb3J0IGlzIG5ld1xuICBwcml2YXRlIGlzTmV3UmVwb3J0KGxhc3RNb2RpZmllZDogRGF0ZSB8IG51bGwpOiBib29sZWFuIHtcbiAgICBjb25zdCBiYXNlbGluZSA9IHRoaXMucmVwb3J0QmFzZWxpbmU7XG4gICAgcmV0dXJuICEhbGFzdE1vZGlmaWVkICYmICEhYmFzZWxpbmUgJiYgbGFzdE1vZGlmaWVkLmdldFRpbWUoKSA+IGJhc2VsaW5lLmdldFRpbWUoKTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0IHNlc3Npb25OdW1iZXIoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gc2Vzc2lvbk51bWJlck9mKHRoaXMuc2Vzc2lvbkRhdGEpO1xuICB9XG59XG4iLCI8cCAqbmdJZj1cImNvbmZpZ0Vycm9yOyBlbHNlIHNlc3Npb25TZWFyY2hcIiBjbGFzcz1cImVkaXQtc2Vzc2lvbi13aWRnZXRfX2Vycm9yXCIgcm9sZT1cImFsZXJ0XCI+XG4gIHt7IGNvbmZpZ0Vycm9yIH19XG48L3A+XG48bmctdGVtcGxhdGUgI3Nlc3Npb25TZWFyY2g+XG4gIDxsaWItc2Vzc2lvbi1zZWFyY2ggKHNlc3Npb25EYXRhTG9hZGVkKT1cIm9uU2Vzc2lvbkRhdGFMb2FkZWQoJGV2ZW50KVwiPjwvbGliLXNlc3Npb24tc2VhcmNoPlxuICA8cCAqbmdJZj1cIiFzZXNzaW9uRGF0YVwiIGNsYXNzPVwiZWRpdC1zZXNzaW9uLXdpZGdldF9fZW1wdHlcIj5cbiAgICBTZWFyY2ggZm9yIGEgc2Vzc2lvbiB0byB2aWV3IGl0cyByZXBvcnQuXG4gIDwvcD5cbiAgPGxpYi1kZXRhaWwtZWRpdFxuICAgIFtzZXNzaW9uRGF0YV09XCJzZXNzaW9uRGF0YVwiXG4gICAgKHJlZ2VuZXJhdGVSZXF1ZXN0ZWQpPVwib25SZWdlbmVyYXRlUmVxdWVzdGVkKCRldmVudClcIlxuICAgIChjYW5jZWxsZWQpPVwib25DYW5jZWxsZWQoKVwiXG4gID48L2xpYi1kZXRhaWwtZWRpdD5cbiAgPHAgKm5nSWY9XCJyZXBvcnRFcnJvclwiIGNsYXNzPVwiZWRpdC1zZXNzaW9uLXdpZGdldF9fZXJyb3JcIiByb2xlPVwiYWxlcnRcIj5cbiAgICB7eyByZXBvcnRFcnJvciB9fVxuICA8L3A+XG4gIDxsaWItcmVwb3J0LXZpZXdlclxuICAgIFtzZXNzaW9uRGF0YV09XCJzZXNzaW9uRGF0YVwiXG4gICAgKHJlcG9ydExvYWRlZCk9XCJvblJlcG9ydExvYWRlZCgkZXZlbnQpXCJcbiAgPjwvbGliLXJlcG9ydC12aWV3ZXI+XG48L25nLXRlbXBsYXRlPlxuPGxpYi12ZXJzaW9uLWZvb3RlciBbdmVyc2lvbl09XCJ2ZXJzaW9uXCI+PC9saWItdmVyc2lvbi1mb290ZXI+XG4iXX0=
@@ -0,0 +1,19 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { ComponentsModule } from '@alimetry/widget-core';
4
+ import { EditSessionWidgetComponent } from './edit-session-widget.component';
5
+ import * as i0 from "@angular/core";
6
+ export class EditSessionWidgetModule {
7
+ }
8
+ EditSessionWidgetModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
9
+ EditSessionWidgetModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, declarations: [EditSessionWidgetComponent], imports: [CommonModule, ComponentsModule], exports: [EditSessionWidgetComponent] });
10
+ EditSessionWidgetModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, imports: [CommonModule, ComponentsModule] });
11
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, decorators: [{
12
+ type: NgModule,
13
+ args: [{
14
+ declarations: [EditSessionWidgetComponent],
15
+ imports: [CommonModule, ComponentsModule],
16
+ exports: [EditSessionWidgetComponent],
17
+ }]
18
+ }] });
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRpdC1zZXNzaW9uLXdpZGdldC5tb2R1bGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi93aWRnZXRzL2VkaXQtc2Vzc2lvbi13aWRnZXQvc3JjL2xpYi9lZGl0LXNlc3Npb24td2lkZ2V0Lm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQzs7QUFPN0UsTUFBTSxPQUFPLHVCQUF1Qjs7cUhBQXZCLHVCQUF1QjtzSEFBdkIsdUJBQXVCLGlCQUpuQiwwQkFBMEIsYUFDL0IsWUFBWSxFQUFFLGdCQUFnQixhQUM5QiwwQkFBMEI7c0hBRXpCLHVCQUF1QixZQUh4QixZQUFZLEVBQUUsZ0JBQWdCOzRGQUc3Qix1QkFBdUI7a0JBTG5DLFFBQVE7bUJBQUM7b0JBQ1IsWUFBWSxFQUFFLENBQUMsMEJBQTBCLENBQUM7b0JBQzFDLE9BQU8sRUFBRSxDQUFDLFlBQVksRUFBRSxnQkFBZ0IsQ0FBQztvQkFDekMsT0FBTyxFQUFFLENBQUMsMEJBQTBCLENBQUM7aUJBQ3RDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTmdNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBDb21wb25lbnRzTW9kdWxlIH0gZnJvbSAnQGFsaW1ldHJ5L3dpZGdldC1jb3JlJztcbmltcG9ydCB7IEVkaXRTZXNzaW9uV2lkZ2V0Q29tcG9uZW50IH0gZnJvbSAnLi9lZGl0LXNlc3Npb24td2lkZ2V0LmNvbXBvbmVudCc7XG5cbkBOZ01vZHVsZSh7XG4gIGRlY2xhcmF0aW9uczogW0VkaXRTZXNzaW9uV2lkZ2V0Q29tcG9uZW50XSxcbiAgaW1wb3J0czogW0NvbW1vbk1vZHVsZSwgQ29tcG9uZW50c01vZHVsZV0sXG4gIGV4cG9ydHM6IFtFZGl0U2Vzc2lvbldpZGdldENvbXBvbmVudF0sXG59KVxuZXhwb3J0IGNsYXNzIEVkaXRTZXNzaW9uV2lkZ2V0TW9kdWxlIHt9XG4iXX0=
@@ -0,0 +1,4 @@
1
+ // keep in step with "version" in widgets/edit-session-widget/package.json, which ng-packagr won't
2
+ // let src import
3
+ export const EDIT_SESSION_WIDGET_VERSION = '1.0.0-dev.0';
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3dpZGdldHMvZWRpdC1zZXNzaW9uLXdpZGdldC9zcmMvbGliL3ZlcnNpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsa0dBQWtHO0FBQ2xHLGlCQUFpQjtBQUNqQixNQUFNLENBQUMsTUFBTSwyQkFBMkIsR0FBRyxhQUFhLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBrZWVwIGluIHN0ZXAgd2l0aCBcInZlcnNpb25cIiBpbiB3aWRnZXRzL2VkaXQtc2Vzc2lvbi13aWRnZXQvcGFja2FnZS5qc29uLCB3aGljaCBuZy1wYWNrYWdyIHdvbid0XG4vLyBsZXQgc3JjIGltcG9ydFxuZXhwb3J0IGNvbnN0IEVESVRfU0VTU0lPTl9XSURHRVRfVkVSU0lPTiA9ICcxLjAuMC1kZXYuMCc7XG4iXX0=
@@ -0,0 +1,7 @@
1
+ /*
2
+ * Public API Surface of edit-session-widget
3
+ */
4
+ export * from './lib/edit-session-widget.component';
5
+ export * from './lib/edit-session-widget.module';
6
+ export * from './lib/version';
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljLWFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3dpZGdldHMvZWRpdC1zZXNzaW9uLXdpZGdldC9zcmMvcHVibGljLWFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILGNBQWMscUNBQXFDLENBQUM7QUFDcEQsY0FBYyxrQ0FBa0MsQ0FBQztBQUNqRCxjQUFjLGVBQWUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qXG4gKiBQdWJsaWMgQVBJIFN1cmZhY2Ugb2YgZWRpdC1zZXNzaW9uLXdpZGdldFxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vbGliL2VkaXQtc2Vzc2lvbi13aWRnZXQuY29tcG9uZW50JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2VkaXQtc2Vzc2lvbi13aWRnZXQubW9kdWxlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3ZlcnNpb24nO1xuIl19
@@ -0,0 +1,192 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, Input, ViewChild, NgModule } from '@angular/core';
3
+ import * as i1 from '@alimetry/widget-core';
4
+ import { isReportGenerating, statusOf, isReportGenerated, reportLinkOf, sessionNumberOf, ServicesConfigService, ServicesService, SessionSearchComponent, ComponentsModule } from '@alimetry/widget-core';
5
+ import { Subject, interval, exhaustMap, map, catchError, EMPTY, filter, take, takeUntil, timer, switchMap } from 'rxjs';
6
+ import * as i2 from '@angular/common';
7
+ import { CommonModule } from '@angular/common';
8
+
9
+ // keep in step with "version" in widgets/edit-session-widget/package.json, which ng-packagr won't
10
+ // let src import
11
+ const EDIT_SESSION_WIDGET_VERSION = '1.0.0-dev.0';
12
+
13
+ // how often the widget asks the cloud whether the report it is waiting on is ready
14
+ const REPORT_POLL_INTERVAL_MS = 30000;
15
+ // how often it then re-reads the report link and the file behind it, waiting for that file to be
16
+ // the newly generated one rather than the one it replaced
17
+ const REPORT_FILE_POLL_INTERVAL_MS = 5000;
18
+ class EditSessionWidgetComponent {
19
+ constructor(servicesConfig, services) {
20
+ this.servicesConfig = servicesConfig;
21
+ this.services = services;
22
+ this.version = EDIT_SESSION_WIDGET_VERSION;
23
+ this.configError = null;
24
+ // set when a poll gave up because the record stopped short of a generated report
25
+ this.reportError = null;
26
+ this.reportBaseline = null;
27
+ // the generation of the report currently on screen, as the viewer last loaded it
28
+ this.reportLastModified = null;
29
+ // set while a poll is running that started before the viewer had read the report on screen, so
30
+ // the generation that poll is waiting to replace has still to be recorded
31
+ this.needsBaseline = false;
32
+ // ends the poll a save started: on the next one, on a new search, and on teardown
33
+ this.stopPolling$ = new Subject();
34
+ }
35
+ ngOnChanges() {
36
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
37
+ const deviceDataModelId = (_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.deviceDataModelId) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : '';
38
+ const apiUrl = (_f = (_e = (_d = this.config) === null || _d === void 0 ? void 0 : _d.apiUrl) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : '';
39
+ const appDataPropertySetId = (_j = (_h = (_g = this.config) === null || _g === void 0 ? void 0 : _g.appDataPropertySetId) === null || _h === void 0 ? void 0 : _h.trim()) !== null && _j !== void 0 ? _j : '';
40
+ const devicePropertySetId = (_m = (_l = (_k = this.config) === null || _k === void 0 ? void 0 : _k.devicePropertySetId) === null || _l === void 0 ? void 0 : _l.trim()) !== null && _m !== void 0 ? _m : '';
41
+ if (!deviceDataModelId || !apiUrl) {
42
+ this.configError =
43
+ 'This widget is missing required configuration values. Please check the widget configuration.';
44
+ this.sessionData = undefined;
45
+ return;
46
+ }
47
+ this.configError = null;
48
+ // hand the values the services layer needs to it through dependency injection
49
+ this.servicesConfig.setConfig({
50
+ deviceDataModelId,
51
+ apiUrl,
52
+ appDataPropertySetId,
53
+ devicePropertySetId,
54
+ });
55
+ }
56
+ onSessionDataLoaded(sessionData) {
57
+ // whatever the last save was waiting on, it is not this session
58
+ this.stopPolling$.next();
59
+ this.sessionData = sessionData;
60
+ this.reportError = null;
61
+ this.reportBaseline = null;
62
+ this.reportLastModified = null;
63
+ this.needsBaseline = false;
64
+ // a session that is already regenerating when it is searched for is waited on the same way one
65
+ // this widget just regenerated is, so reloading the page picks the wait back up
66
+ if (isReportGenerating(statusOf(sessionData))) {
67
+ this.onRegenerateRequested(true);
68
+ }
69
+ }
70
+ // clears the search box and drops the session, which empties the form and the report with it
71
+ onCancelled() {
72
+ var _a;
73
+ (_a = this.sessionSearch) === null || _a === void 0 ? void 0 : _a.clear();
74
+ this.onSessionDataLoaded(undefined);
75
+ }
76
+ onReportLoaded(lastModified) {
77
+ this.reportLastModified = lastModified;
78
+ // the poll a reload started has no generation to wait past until this first read arrives
79
+ if (this.needsBaseline) {
80
+ this.reportBaseline = lastModified;
81
+ this.needsBaseline = false;
82
+ }
83
+ }
84
+ onRegenerateRequested(requested) {
85
+ if (!requested) {
86
+ return;
87
+ }
88
+ this.reportError = null;
89
+ this.reportBaseline = this.reportLastModified;
90
+ this.needsBaseline = !this.reportBaseline;
91
+ this.pollForReport();
92
+ }
93
+ ngOnDestroy() {
94
+ this.stopPolling$.next();
95
+ this.stopPolling$.complete();
96
+ }
97
+ pollForReport() {
98
+ const sessionNumber = this.sessionNumber;
99
+ if (!sessionNumber) {
100
+ return;
101
+ }
102
+ // whichever poll is already running is waiting on a report this save has superseded
103
+ this.stopPolling$.next();
104
+ interval(REPORT_POLL_INTERVAL_MS)
105
+ .pipe(exhaustMap(() => this.services.getSessionProperty(sessionNumber, 'Status').pipe(map((sessionData) => statusOf(sessionData)), catchError(() => EMPTY))),
106
+ // anything other than "generating report" is as far as this report is going to get, so the
107
+ // poll ends on it either way rather than waiting out a status that will never change
108
+ filter((status) => !isReportGenerating(status)), take(1), takeUntil(this.stopPolling$))
109
+ .subscribe((status) => {
110
+ if (isReportGenerated(status)) {
111
+ this.pollForNewReportFile(sessionNumber);
112
+ }
113
+ else {
114
+ this.onReportStopped(sessionNumber);
115
+ }
116
+ });
117
+ }
118
+ pollForNewReportFile(sessionNumber) {
119
+ // with no generation to compare against there is nothing to wait for: whatever the link points
120
+ // at is the report to show, and reading it here only downloads a file the viewer downloads again
121
+ if (!this.reportBaseline) {
122
+ this.loadSession(sessionNumber);
123
+ return;
124
+ }
125
+ // poll to check if new report is ready
126
+ timer(0, REPORT_FILE_POLL_INTERVAL_MS)
127
+ .pipe(exhaustMap(() => this.services.getSessionProperty(sessionNumber, 'PdfReport').pipe(map((sessionData) => reportLinkOf(sessionData)), filter((link) => !!link), switchMap((link) => this.services.getReportLastModified(link)), catchError(() => EMPTY))), filter((lastModified) => this.isNewReport(lastModified)), take(1), takeUntil(this.stopPolling$))
128
+ .subscribe(() => this.loadSession(sessionNumber));
129
+ }
130
+ // the record settled on a status that is not a generated report: say so, and read the record back
131
+ // so the form unlocks rather than staying held by a regeneration that is over
132
+ onReportStopped(sessionNumber) {
133
+ this.reportError = 'Report regeneration failed. Please try again.';
134
+ this.loadSession(sessionNumber);
135
+ }
136
+ // the polls above read one property at a time, so the whole record is read back once, here, to
137
+ // show the session the regeneration has left behind
138
+ loadSession(sessionNumber) {
139
+ this.services
140
+ .getSessionData(sessionNumber)
141
+ .pipe(filter((sessionData) => !!sessionData), catchError(() => EMPTY), takeUntil(this.stopPolling$))
142
+ .subscribe((sessionData) => this.showSession(sessionData));
143
+ }
144
+ showSession(sessionData) {
145
+ this.needsBaseline = false;
146
+ this.sessionData = sessionData;
147
+ }
148
+ // check if report is new
149
+ isNewReport(lastModified) {
150
+ const baseline = this.reportBaseline;
151
+ return !!lastModified && !!baseline && lastModified.getTime() > baseline.getTime();
152
+ }
153
+ get sessionNumber() {
154
+ return sessionNumberOf(this.sessionData);
155
+ }
156
+ }
157
+ EditSessionWidgetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetComponent, deps: [{ token: i1.ServicesConfigService }, { token: i1.ServicesService }], target: i0.ɵɵFactoryTarget.Component });
158
+ EditSessionWidgetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: EditSessionWidgetComponent, selector: "lib-edit-session-widget", inputs: { config: "config" }, providers: [ServicesConfigService, ServicesService], viewQueries: [{ propertyName: "sessionSearch", first: true, predicate: SessionSearchComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n", styles: [".edit-session-widget__empty{margin:1rem 0 0;color:#6c757d;font-size:14px}.edit-session-widget__error{margin:0;color:#b02a37}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.SessionSearchComponent, selector: "lib-session-search", outputs: ["sessionDataLoaded"] }, { kind: "component", type: i1.ReportViewerComponent, selector: "lib-report-viewer", inputs: ["sessionData"], outputs: ["reportLoaded"] }, { kind: "component", type: i1.DetailEditComponent, selector: "lib-detail-edit", inputs: ["sessionData"], outputs: ["regenerateRequested", "cancelled"] }, { kind: "component", type: i1.VersionFooterComponent, selector: "lib-version-footer", inputs: ["version"] }] });
159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetComponent, decorators: [{
160
+ type: Component,
161
+ args: [{ selector: 'lib-edit-session-widget', providers: [ServicesConfigService, ServicesService], template: "<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n", styles: [".edit-session-widget__empty{margin:1rem 0 0;color:#6c757d;font-size:14px}.edit-session-widget__error{margin:0;color:#b02a37}\n"] }]
162
+ }], ctorParameters: function () { return [{ type: i1.ServicesConfigService }, { type: i1.ServicesService }]; }, propDecorators: { config: [{
163
+ type: Input
164
+ }], sessionSearch: [{
165
+ type: ViewChild,
166
+ args: [SessionSearchComponent]
167
+ }] } });
168
+
169
+ class EditSessionWidgetModule {
170
+ }
171
+ EditSessionWidgetModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
172
+ EditSessionWidgetModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, declarations: [EditSessionWidgetComponent], imports: [CommonModule, ComponentsModule], exports: [EditSessionWidgetComponent] });
173
+ EditSessionWidgetModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, imports: [CommonModule, ComponentsModule] });
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, decorators: [{
175
+ type: NgModule,
176
+ args: [{
177
+ declarations: [EditSessionWidgetComponent],
178
+ imports: [CommonModule, ComponentsModule],
179
+ exports: [EditSessionWidgetComponent],
180
+ }]
181
+ }] });
182
+
183
+ /*
184
+ * Public API Surface of edit-session-widget
185
+ */
186
+
187
+ /**
188
+ * Generated bundle index. Do not edit.
189
+ */
190
+
191
+ export { EDIT_SESSION_WIDGET_VERSION, EditSessionWidgetComponent, EditSessionWidgetModule };
192
+ //# sourceMappingURL=alimetry-edit-session-widget.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alimetry-edit-session-widget.mjs","sources":["../../../widgets/edit-session-widget/src/lib/version.ts","../../../widgets/edit-session-widget/src/lib/edit-session-widget.component.ts","../../../widgets/edit-session-widget/src/lib/edit-session-widget.component.html","../../../widgets/edit-session-widget/src/lib/edit-session-widget.module.ts","../../../widgets/edit-session-widget/src/public-api.ts","../../../widgets/edit-session-widget/src/alimetry-edit-session-widget.ts"],"sourcesContent":["// keep in step with \"version\" in widgets/edit-session-widget/package.json, which ng-packagr won't\n// let src import\nexport const EDIT_SESSION_WIDGET_VERSION = '1.0.0-dev.0';\n","import { Component, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core';\nimport {\n DeviceData,\n ServicesConfigService,\n ServicesService,\n SessionSearchComponent,\n isReportGenerated,\n isReportGenerating,\n reportLinkOf,\n sessionNumberOf,\n statusOf,\n} from '@alimetry/widget-core';\nimport {\n catchError,\n EMPTY,\n exhaustMap,\n filter,\n interval,\n map,\n Subject,\n switchMap,\n take,\n takeUntil,\n timer,\n} from 'rxjs';\nimport { EDIT_SESSION_WIDGET_VERSION } from './version';\n\n// how often the widget asks the cloud whether the report it is waiting on is ready\nconst REPORT_POLL_INTERVAL_MS = 30_000;\n\n// how often it then re-reads the report link and the file behind it, waiting for that file to be\n// the newly generated one rather than the one it replaced\nconst REPORT_FILE_POLL_INTERVAL_MS = 5_000;\n\n@Component({\n selector: 'lib-edit-session-widget',\n templateUrl: './edit-session-widget.component.html',\n styleUrls: ['./edit-session-widget.component.css'],\n // scoped to this widget so each instance runs against the config it was given\n providers: [ServicesConfigService, ServicesService],\n})\nexport class EditSessionWidgetComponent implements OnChanges, OnDestroy {\n // TODO: change the type of the input to not be any\n @Input() config: any;\n\n readonly version = EDIT_SESSION_WIDGET_VERSION;\n\n // inside the config check's template, so it is only there once the config is valid\n @ViewChild(SessionSearchComponent) private sessionSearch?: SessionSearchComponent;\n\n sessionData?: DeviceData;\n configError: string | null = null;\n\n // set when a poll gave up because the record stopped short of a generated report\n reportError: string | null = null;\n\n reportBaseline: Date | null = null;\n\n // the generation of the report currently on screen, as the viewer last loaded it\n private reportLastModified: Date | null = null;\n\n // set while a poll is running that started before the viewer had read the report on screen, so\n // the generation that poll is waiting to replace has still to be recorded\n private needsBaseline = false;\n\n // ends the poll a save started: on the next one, on a new search, and on teardown\n private readonly stopPolling$ = new Subject<void>();\n\n constructor(\n private servicesConfig: ServicesConfigService,\n private services: ServicesService,\n ) {}\n\n ngOnChanges(): void {\n const deviceDataModelId: string = this.config?.deviceDataModelId?.trim() ?? '';\n const apiUrl: string = this.config?.apiUrl?.trim() ?? '';\n const appDataPropertySetId: string = this.config?.appDataPropertySetId?.trim() ?? '';\n const devicePropertySetId: string = this.config?.devicePropertySetId?.trim() ?? '';\n\n if (!deviceDataModelId || !apiUrl) {\n this.configError =\n 'This widget is missing required configuration values. Please check the widget configuration.';\n this.sessionData = undefined;\n return;\n }\n\n this.configError = null;\n // hand the values the services layer needs to it through dependency injection\n this.servicesConfig.setConfig({\n deviceDataModelId,\n apiUrl,\n appDataPropertySetId,\n devicePropertySetId,\n });\n }\n\n onSessionDataLoaded(sessionData: DeviceData | undefined): void {\n // whatever the last save was waiting on, it is not this session\n this.stopPolling$.next();\n\n this.sessionData = sessionData;\n this.reportError = null;\n this.reportBaseline = null;\n this.reportLastModified = null;\n this.needsBaseline = false;\n\n // a session that is already regenerating when it is searched for is waited on the same way one\n // this widget just regenerated is, so reloading the page picks the wait back up\n if (isReportGenerating(statusOf(sessionData))) {\n this.onRegenerateRequested(true);\n }\n }\n\n // clears the search box and drops the session, which empties the form and the report with it\n onCancelled(): void {\n this.sessionSearch?.clear();\n this.onSessionDataLoaded(undefined);\n }\n\n onReportLoaded(lastModified: Date | null): void {\n this.reportLastModified = lastModified;\n\n // the poll a reload started has no generation to wait past until this first read arrives\n if (this.needsBaseline) {\n this.reportBaseline = lastModified;\n this.needsBaseline = false;\n }\n }\n\n onRegenerateRequested(requested: boolean): void {\n if (!requested) {\n return;\n }\n\n this.reportError = null;\n this.reportBaseline = this.reportLastModified;\n this.needsBaseline = !this.reportBaseline;\n this.pollForReport();\n }\n\n ngOnDestroy(): void {\n this.stopPolling$.next();\n this.stopPolling$.complete();\n }\n\n private pollForReport(): void {\n const sessionNumber = this.sessionNumber;\n if (!sessionNumber) {\n return;\n }\n\n // whichever poll is already running is waiting on a report this save has superseded\n this.stopPolling$.next();\n\n interval(REPORT_POLL_INTERVAL_MS)\n .pipe(\n exhaustMap(() =>\n this.services.getSessionProperty(sessionNumber, 'Status').pipe(\n map((sessionData) => statusOf(sessionData)),\n catchError(() => EMPTY),\n ),\n ),\n // anything other than \"generating report\" is as far as this report is going to get, so the\n // poll ends on it either way rather than waiting out a status that will never change\n filter((status) => !isReportGenerating(status)),\n take(1),\n takeUntil(this.stopPolling$),\n )\n .subscribe((status) => {\n if (isReportGenerated(status)) {\n this.pollForNewReportFile(sessionNumber);\n } else {\n this.onReportStopped(sessionNumber);\n }\n });\n }\n\n private pollForNewReportFile(sessionNumber: string): void {\n // with no generation to compare against there is nothing to wait for: whatever the link points\n // at is the report to show, and reading it here only downloads a file the viewer downloads again\n if (!this.reportBaseline) {\n this.loadSession(sessionNumber);\n return;\n }\n\n // poll to check if new report is ready\n timer(0, REPORT_FILE_POLL_INTERVAL_MS)\n .pipe(\n exhaustMap(() =>\n this.services.getSessionProperty(sessionNumber, 'PdfReport').pipe(\n map((sessionData) => reportLinkOf(sessionData)),\n filter((link) => !!link),\n switchMap((link) => this.services.getReportLastModified(link)),\n catchError(() => EMPTY),\n ),\n ),\n filter((lastModified) => this.isNewReport(lastModified)),\n take(1),\n takeUntil(this.stopPolling$),\n )\n .subscribe(() => this.loadSession(sessionNumber));\n }\n\n // the record settled on a status that is not a generated report: say so, and read the record back\n // so the form unlocks rather than staying held by a regeneration that is over\n private onReportStopped(sessionNumber: string): void {\n this.reportError = 'Report regeneration failed. Please try again.';\n this.loadSession(sessionNumber);\n }\n\n // the polls above read one property at a time, so the whole record is read back once, here, to\n // show the session the regeneration has left behind\n private loadSession(sessionNumber: string): void {\n this.services\n .getSessionData(sessionNumber)\n .pipe(\n filter((sessionData): sessionData is DeviceData => !!sessionData),\n catchError(() => EMPTY),\n takeUntil(this.stopPolling$),\n )\n .subscribe((sessionData) => this.showSession(sessionData));\n }\n\n private showSession(sessionData: DeviceData): void {\n this.needsBaseline = false;\n this.sessionData = sessionData;\n }\n\n // check if report is new\n private isNewReport(lastModified: Date | null): boolean {\n const baseline = this.reportBaseline;\n return !!lastModified && !!baseline && lastModified.getTime() > baseline.getTime();\n }\n\n private get sessionNumber(): string {\n return sessionNumberOf(this.sessionData);\n }\n}\n","<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n","import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { ComponentsModule } from '@alimetry/widget-core';\nimport { EditSessionWidgetComponent } from './edit-session-widget.component';\n\n@NgModule({\n declarations: [EditSessionWidgetComponent],\n imports: [CommonModule, ComponentsModule],\n exports: [EditSessionWidgetComponent],\n})\nexport class EditSessionWidgetModule {}\n","/*\n * Public API Surface of edit-session-widget\n */\n\nexport * from './lib/edit-session-widget.component';\nexport * from './lib/edit-session-widget.module';\nexport * from './lib/version';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,190 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Component, Input, ViewChild, NgModule } from '@angular/core';
3
+ import * as i1 from '@alimetry/widget-core';
4
+ import { isReportGenerating, statusOf, isReportGenerated, reportLinkOf, sessionNumberOf, ServicesConfigService, ServicesService, SessionSearchComponent, ComponentsModule } from '@alimetry/widget-core';
5
+ import { Subject, interval, exhaustMap, map, catchError, EMPTY, filter, take, takeUntil, timer, switchMap } from 'rxjs';
6
+ import * as i2 from '@angular/common';
7
+ import { CommonModule } from '@angular/common';
8
+
9
+ // keep in step with "version" in widgets/edit-session-widget/package.json, which ng-packagr won't
10
+ // let src import
11
+ const EDIT_SESSION_WIDGET_VERSION = '1.0.0-dev.0';
12
+
13
+ // how often the widget asks the cloud whether the report it is waiting on is ready
14
+ const REPORT_POLL_INTERVAL_MS = 30000;
15
+ // how often it then re-reads the report link and the file behind it, waiting for that file to be
16
+ // the newly generated one rather than the one it replaced
17
+ const REPORT_FILE_POLL_INTERVAL_MS = 5000;
18
+ class EditSessionWidgetComponent {
19
+ constructor(servicesConfig, services) {
20
+ this.servicesConfig = servicesConfig;
21
+ this.services = services;
22
+ this.version = EDIT_SESSION_WIDGET_VERSION;
23
+ this.configError = null;
24
+ // set when a poll gave up because the record stopped short of a generated report
25
+ this.reportError = null;
26
+ this.reportBaseline = null;
27
+ // the generation of the report currently on screen, as the viewer last loaded it
28
+ this.reportLastModified = null;
29
+ // set while a poll is running that started before the viewer had read the report on screen, so
30
+ // the generation that poll is waiting to replace has still to be recorded
31
+ this.needsBaseline = false;
32
+ // ends the poll a save started: on the next one, on a new search, and on teardown
33
+ this.stopPolling$ = new Subject();
34
+ }
35
+ ngOnChanges() {
36
+ const deviceDataModelId = this.config?.deviceDataModelId?.trim() ?? '';
37
+ const apiUrl = this.config?.apiUrl?.trim() ?? '';
38
+ const appDataPropertySetId = this.config?.appDataPropertySetId?.trim() ?? '';
39
+ const devicePropertySetId = this.config?.devicePropertySetId?.trim() ?? '';
40
+ if (!deviceDataModelId || !apiUrl) {
41
+ this.configError =
42
+ 'This widget is missing required configuration values. Please check the widget configuration.';
43
+ this.sessionData = undefined;
44
+ return;
45
+ }
46
+ this.configError = null;
47
+ // hand the values the services layer needs to it through dependency injection
48
+ this.servicesConfig.setConfig({
49
+ deviceDataModelId,
50
+ apiUrl,
51
+ appDataPropertySetId,
52
+ devicePropertySetId,
53
+ });
54
+ }
55
+ onSessionDataLoaded(sessionData) {
56
+ // whatever the last save was waiting on, it is not this session
57
+ this.stopPolling$.next();
58
+ this.sessionData = sessionData;
59
+ this.reportError = null;
60
+ this.reportBaseline = null;
61
+ this.reportLastModified = null;
62
+ this.needsBaseline = false;
63
+ // a session that is already regenerating when it is searched for is waited on the same way one
64
+ // this widget just regenerated is, so reloading the page picks the wait back up
65
+ if (isReportGenerating(statusOf(sessionData))) {
66
+ this.onRegenerateRequested(true);
67
+ }
68
+ }
69
+ // clears the search box and drops the session, which empties the form and the report with it
70
+ onCancelled() {
71
+ this.sessionSearch?.clear();
72
+ this.onSessionDataLoaded(undefined);
73
+ }
74
+ onReportLoaded(lastModified) {
75
+ this.reportLastModified = lastModified;
76
+ // the poll a reload started has no generation to wait past until this first read arrives
77
+ if (this.needsBaseline) {
78
+ this.reportBaseline = lastModified;
79
+ this.needsBaseline = false;
80
+ }
81
+ }
82
+ onRegenerateRequested(requested) {
83
+ if (!requested) {
84
+ return;
85
+ }
86
+ this.reportError = null;
87
+ this.reportBaseline = this.reportLastModified;
88
+ this.needsBaseline = !this.reportBaseline;
89
+ this.pollForReport();
90
+ }
91
+ ngOnDestroy() {
92
+ this.stopPolling$.next();
93
+ this.stopPolling$.complete();
94
+ }
95
+ pollForReport() {
96
+ const sessionNumber = this.sessionNumber;
97
+ if (!sessionNumber) {
98
+ return;
99
+ }
100
+ // whichever poll is already running is waiting on a report this save has superseded
101
+ this.stopPolling$.next();
102
+ interval(REPORT_POLL_INTERVAL_MS)
103
+ .pipe(exhaustMap(() => this.services.getSessionProperty(sessionNumber, 'Status').pipe(map((sessionData) => statusOf(sessionData)), catchError(() => EMPTY))),
104
+ // anything other than "generating report" is as far as this report is going to get, so the
105
+ // poll ends on it either way rather than waiting out a status that will never change
106
+ filter((status) => !isReportGenerating(status)), take(1), takeUntil(this.stopPolling$))
107
+ .subscribe((status) => {
108
+ if (isReportGenerated(status)) {
109
+ this.pollForNewReportFile(sessionNumber);
110
+ }
111
+ else {
112
+ this.onReportStopped(sessionNumber);
113
+ }
114
+ });
115
+ }
116
+ pollForNewReportFile(sessionNumber) {
117
+ // with no generation to compare against there is nothing to wait for: whatever the link points
118
+ // at is the report to show, and reading it here only downloads a file the viewer downloads again
119
+ if (!this.reportBaseline) {
120
+ this.loadSession(sessionNumber);
121
+ return;
122
+ }
123
+ // poll to check if new report is ready
124
+ timer(0, REPORT_FILE_POLL_INTERVAL_MS)
125
+ .pipe(exhaustMap(() => this.services.getSessionProperty(sessionNumber, 'PdfReport').pipe(map((sessionData) => reportLinkOf(sessionData)), filter((link) => !!link), switchMap((link) => this.services.getReportLastModified(link)), catchError(() => EMPTY))), filter((lastModified) => this.isNewReport(lastModified)), take(1), takeUntil(this.stopPolling$))
126
+ .subscribe(() => this.loadSession(sessionNumber));
127
+ }
128
+ // the record settled on a status that is not a generated report: say so, and read the record back
129
+ // so the form unlocks rather than staying held by a regeneration that is over
130
+ onReportStopped(sessionNumber) {
131
+ this.reportError = 'Report regeneration failed. Please try again.';
132
+ this.loadSession(sessionNumber);
133
+ }
134
+ // the polls above read one property at a time, so the whole record is read back once, here, to
135
+ // show the session the regeneration has left behind
136
+ loadSession(sessionNumber) {
137
+ this.services
138
+ .getSessionData(sessionNumber)
139
+ .pipe(filter((sessionData) => !!sessionData), catchError(() => EMPTY), takeUntil(this.stopPolling$))
140
+ .subscribe((sessionData) => this.showSession(sessionData));
141
+ }
142
+ showSession(sessionData) {
143
+ this.needsBaseline = false;
144
+ this.sessionData = sessionData;
145
+ }
146
+ // check if report is new
147
+ isNewReport(lastModified) {
148
+ const baseline = this.reportBaseline;
149
+ return !!lastModified && !!baseline && lastModified.getTime() > baseline.getTime();
150
+ }
151
+ get sessionNumber() {
152
+ return sessionNumberOf(this.sessionData);
153
+ }
154
+ }
155
+ EditSessionWidgetComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetComponent, deps: [{ token: i1.ServicesConfigService }, { token: i1.ServicesService }], target: i0.ɵɵFactoryTarget.Component });
156
+ EditSessionWidgetComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: EditSessionWidgetComponent, selector: "lib-edit-session-widget", inputs: { config: "config" }, providers: [ServicesConfigService, ServicesService], viewQueries: [{ propertyName: "sessionSearch", first: true, predicate: SessionSearchComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n", styles: [".edit-session-widget__empty{margin:1rem 0 0;color:#6c757d;font-size:14px}.edit-session-widget__error{margin:0;color:#b02a37}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.SessionSearchComponent, selector: "lib-session-search", outputs: ["sessionDataLoaded"] }, { kind: "component", type: i1.ReportViewerComponent, selector: "lib-report-viewer", inputs: ["sessionData"], outputs: ["reportLoaded"] }, { kind: "component", type: i1.DetailEditComponent, selector: "lib-detail-edit", inputs: ["sessionData"], outputs: ["regenerateRequested", "cancelled"] }, { kind: "component", type: i1.VersionFooterComponent, selector: "lib-version-footer", inputs: ["version"] }] });
157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetComponent, decorators: [{
158
+ type: Component,
159
+ args: [{ selector: 'lib-edit-session-widget', providers: [ServicesConfigService, ServicesService], template: "<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n", styles: [".edit-session-widget__empty{margin:1rem 0 0;color:#6c757d;font-size:14px}.edit-session-widget__error{margin:0;color:#b02a37}\n"] }]
160
+ }], ctorParameters: function () { return [{ type: i1.ServicesConfigService }, { type: i1.ServicesService }]; }, propDecorators: { config: [{
161
+ type: Input
162
+ }], sessionSearch: [{
163
+ type: ViewChild,
164
+ args: [SessionSearchComponent]
165
+ }] } });
166
+
167
+ class EditSessionWidgetModule {
168
+ }
169
+ EditSessionWidgetModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
170
+ EditSessionWidgetModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, declarations: [EditSessionWidgetComponent], imports: [CommonModule, ComponentsModule], exports: [EditSessionWidgetComponent] });
171
+ EditSessionWidgetModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, imports: [CommonModule, ComponentsModule] });
172
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditSessionWidgetModule, decorators: [{
173
+ type: NgModule,
174
+ args: [{
175
+ declarations: [EditSessionWidgetComponent],
176
+ imports: [CommonModule, ComponentsModule],
177
+ exports: [EditSessionWidgetComponent],
178
+ }]
179
+ }] });
180
+
181
+ /*
182
+ * Public API Surface of edit-session-widget
183
+ */
184
+
185
+ /**
186
+ * Generated bundle index. Do not edit.
187
+ */
188
+
189
+ export { EDIT_SESSION_WIDGET_VERSION, EditSessionWidgetComponent, EditSessionWidgetModule };
190
+ //# sourceMappingURL=alimetry-edit-session-widget.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alimetry-edit-session-widget.mjs","sources":["../../../widgets/edit-session-widget/src/lib/version.ts","../../../widgets/edit-session-widget/src/lib/edit-session-widget.component.ts","../../../widgets/edit-session-widget/src/lib/edit-session-widget.component.html","../../../widgets/edit-session-widget/src/lib/edit-session-widget.module.ts","../../../widgets/edit-session-widget/src/public-api.ts","../../../widgets/edit-session-widget/src/alimetry-edit-session-widget.ts"],"sourcesContent":["// keep in step with \"version\" in widgets/edit-session-widget/package.json, which ng-packagr won't\n// let src import\nexport const EDIT_SESSION_WIDGET_VERSION = '1.0.0-dev.0';\n","import { Component, Input, OnChanges, OnDestroy, ViewChild } from '@angular/core';\nimport {\n DeviceData,\n ServicesConfigService,\n ServicesService,\n SessionSearchComponent,\n isReportGenerated,\n isReportGenerating,\n reportLinkOf,\n sessionNumberOf,\n statusOf,\n} from '@alimetry/widget-core';\nimport {\n catchError,\n EMPTY,\n exhaustMap,\n filter,\n interval,\n map,\n Subject,\n switchMap,\n take,\n takeUntil,\n timer,\n} from 'rxjs';\nimport { EDIT_SESSION_WIDGET_VERSION } from './version';\n\n// how often the widget asks the cloud whether the report it is waiting on is ready\nconst REPORT_POLL_INTERVAL_MS = 30_000;\n\n// how often it then re-reads the report link and the file behind it, waiting for that file to be\n// the newly generated one rather than the one it replaced\nconst REPORT_FILE_POLL_INTERVAL_MS = 5_000;\n\n@Component({\n selector: 'lib-edit-session-widget',\n templateUrl: './edit-session-widget.component.html',\n styleUrls: ['./edit-session-widget.component.css'],\n // scoped to this widget so each instance runs against the config it was given\n providers: [ServicesConfigService, ServicesService],\n})\nexport class EditSessionWidgetComponent implements OnChanges, OnDestroy {\n // TODO: change the type of the input to not be any\n @Input() config: any;\n\n readonly version = EDIT_SESSION_WIDGET_VERSION;\n\n // inside the config check's template, so it is only there once the config is valid\n @ViewChild(SessionSearchComponent) private sessionSearch?: SessionSearchComponent;\n\n sessionData?: DeviceData;\n configError: string | null = null;\n\n // set when a poll gave up because the record stopped short of a generated report\n reportError: string | null = null;\n\n reportBaseline: Date | null = null;\n\n // the generation of the report currently on screen, as the viewer last loaded it\n private reportLastModified: Date | null = null;\n\n // set while a poll is running that started before the viewer had read the report on screen, so\n // the generation that poll is waiting to replace has still to be recorded\n private needsBaseline = false;\n\n // ends the poll a save started: on the next one, on a new search, and on teardown\n private readonly stopPolling$ = new Subject<void>();\n\n constructor(\n private servicesConfig: ServicesConfigService,\n private services: ServicesService,\n ) {}\n\n ngOnChanges(): void {\n const deviceDataModelId: string = this.config?.deviceDataModelId?.trim() ?? '';\n const apiUrl: string = this.config?.apiUrl?.trim() ?? '';\n const appDataPropertySetId: string = this.config?.appDataPropertySetId?.trim() ?? '';\n const devicePropertySetId: string = this.config?.devicePropertySetId?.trim() ?? '';\n\n if (!deviceDataModelId || !apiUrl) {\n this.configError =\n 'This widget is missing required configuration values. Please check the widget configuration.';\n this.sessionData = undefined;\n return;\n }\n\n this.configError = null;\n // hand the values the services layer needs to it through dependency injection\n this.servicesConfig.setConfig({\n deviceDataModelId,\n apiUrl,\n appDataPropertySetId,\n devicePropertySetId,\n });\n }\n\n onSessionDataLoaded(sessionData: DeviceData | undefined): void {\n // whatever the last save was waiting on, it is not this session\n this.stopPolling$.next();\n\n this.sessionData = sessionData;\n this.reportError = null;\n this.reportBaseline = null;\n this.reportLastModified = null;\n this.needsBaseline = false;\n\n // a session that is already regenerating when it is searched for is waited on the same way one\n // this widget just regenerated is, so reloading the page picks the wait back up\n if (isReportGenerating(statusOf(sessionData))) {\n this.onRegenerateRequested(true);\n }\n }\n\n // clears the search box and drops the session, which empties the form and the report with it\n onCancelled(): void {\n this.sessionSearch?.clear();\n this.onSessionDataLoaded(undefined);\n }\n\n onReportLoaded(lastModified: Date | null): void {\n this.reportLastModified = lastModified;\n\n // the poll a reload started has no generation to wait past until this first read arrives\n if (this.needsBaseline) {\n this.reportBaseline = lastModified;\n this.needsBaseline = false;\n }\n }\n\n onRegenerateRequested(requested: boolean): void {\n if (!requested) {\n return;\n }\n\n this.reportError = null;\n this.reportBaseline = this.reportLastModified;\n this.needsBaseline = !this.reportBaseline;\n this.pollForReport();\n }\n\n ngOnDestroy(): void {\n this.stopPolling$.next();\n this.stopPolling$.complete();\n }\n\n private pollForReport(): void {\n const sessionNumber = this.sessionNumber;\n if (!sessionNumber) {\n return;\n }\n\n // whichever poll is already running is waiting on a report this save has superseded\n this.stopPolling$.next();\n\n interval(REPORT_POLL_INTERVAL_MS)\n .pipe(\n exhaustMap(() =>\n this.services.getSessionProperty(sessionNumber, 'Status').pipe(\n map((sessionData) => statusOf(sessionData)),\n catchError(() => EMPTY),\n ),\n ),\n // anything other than \"generating report\" is as far as this report is going to get, so the\n // poll ends on it either way rather than waiting out a status that will never change\n filter((status) => !isReportGenerating(status)),\n take(1),\n takeUntil(this.stopPolling$),\n )\n .subscribe((status) => {\n if (isReportGenerated(status)) {\n this.pollForNewReportFile(sessionNumber);\n } else {\n this.onReportStopped(sessionNumber);\n }\n });\n }\n\n private pollForNewReportFile(sessionNumber: string): void {\n // with no generation to compare against there is nothing to wait for: whatever the link points\n // at is the report to show, and reading it here only downloads a file the viewer downloads again\n if (!this.reportBaseline) {\n this.loadSession(sessionNumber);\n return;\n }\n\n // poll to check if new report is ready\n timer(0, REPORT_FILE_POLL_INTERVAL_MS)\n .pipe(\n exhaustMap(() =>\n this.services.getSessionProperty(sessionNumber, 'PdfReport').pipe(\n map((sessionData) => reportLinkOf(sessionData)),\n filter((link) => !!link),\n switchMap((link) => this.services.getReportLastModified(link)),\n catchError(() => EMPTY),\n ),\n ),\n filter((lastModified) => this.isNewReport(lastModified)),\n take(1),\n takeUntil(this.stopPolling$),\n )\n .subscribe(() => this.loadSession(sessionNumber));\n }\n\n // the record settled on a status that is not a generated report: say so, and read the record back\n // so the form unlocks rather than staying held by a regeneration that is over\n private onReportStopped(sessionNumber: string): void {\n this.reportError = 'Report regeneration failed. Please try again.';\n this.loadSession(sessionNumber);\n }\n\n // the polls above read one property at a time, so the whole record is read back once, here, to\n // show the session the regeneration has left behind\n private loadSession(sessionNumber: string): void {\n this.services\n .getSessionData(sessionNumber)\n .pipe(\n filter((sessionData): sessionData is DeviceData => !!sessionData),\n catchError(() => EMPTY),\n takeUntil(this.stopPolling$),\n )\n .subscribe((sessionData) => this.showSession(sessionData));\n }\n\n private showSession(sessionData: DeviceData): void {\n this.needsBaseline = false;\n this.sessionData = sessionData;\n }\n\n // check if report is new\n private isNewReport(lastModified: Date | null): boolean {\n const baseline = this.reportBaseline;\n return !!lastModified && !!baseline && lastModified.getTime() > baseline.getTime();\n }\n\n private get sessionNumber(): string {\n return sessionNumberOf(this.sessionData);\n }\n}\n","<p *ngIf=\"configError; else sessionSearch\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ configError }}\n</p>\n<ng-template #sessionSearch>\n <lib-session-search (sessionDataLoaded)=\"onSessionDataLoaded($event)\"></lib-session-search>\n <p *ngIf=\"!sessionData\" class=\"edit-session-widget__empty\">\n Search for a session to view its report.\n </p>\n <lib-detail-edit\n [sessionData]=\"sessionData\"\n (regenerateRequested)=\"onRegenerateRequested($event)\"\n (cancelled)=\"onCancelled()\"\n ></lib-detail-edit>\n <p *ngIf=\"reportError\" class=\"edit-session-widget__error\" role=\"alert\">\n {{ reportError }}\n </p>\n <lib-report-viewer\n [sessionData]=\"sessionData\"\n (reportLoaded)=\"onReportLoaded($event)\"\n ></lib-report-viewer>\n</ng-template>\n<lib-version-footer [version]=\"version\"></lib-version-footer>\n","import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { ComponentsModule } from '@alimetry/widget-core';\nimport { EditSessionWidgetComponent } from './edit-session-widget.component';\n\n@NgModule({\n declarations: [EditSessionWidgetComponent],\n imports: [CommonModule, ComponentsModule],\n exports: [EditSessionWidgetComponent],\n})\nexport class EditSessionWidgetModule {}\n","/*\n * Public API Surface of edit-session-widget\n */\n\nexport * from './lib/edit-session-widget.component';\nexport * from './lib/edit-session-widget.module';\nexport * from './lib/version';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ /// <amd-module name="@alimetry/edit-session-widget" />
5
+ export * from './public-api';
@@ -0,0 +1,33 @@
1
+ import { OnChanges, OnDestroy } from '@angular/core';
2
+ import { DeviceData, ServicesConfigService, ServicesService } from '@alimetry/widget-core';
3
+ import * as i0 from "@angular/core";
4
+ export declare class EditSessionWidgetComponent implements OnChanges, OnDestroy {
5
+ private servicesConfig;
6
+ private services;
7
+ config: any;
8
+ readonly version = "1.0.0-dev.0";
9
+ private sessionSearch?;
10
+ sessionData?: DeviceData;
11
+ configError: string | null;
12
+ reportError: string | null;
13
+ reportBaseline: Date | null;
14
+ private reportLastModified;
15
+ private needsBaseline;
16
+ private readonly stopPolling$;
17
+ constructor(servicesConfig: ServicesConfigService, services: ServicesService);
18
+ ngOnChanges(): void;
19
+ onSessionDataLoaded(sessionData: DeviceData | undefined): void;
20
+ onCancelled(): void;
21
+ onReportLoaded(lastModified: Date | null): void;
22
+ onRegenerateRequested(requested: boolean): void;
23
+ ngOnDestroy(): void;
24
+ private pollForReport;
25
+ private pollForNewReportFile;
26
+ private onReportStopped;
27
+ private loadSession;
28
+ private showSession;
29
+ private isNewReport;
30
+ private get sessionNumber();
31
+ static ɵfac: i0.ɵɵFactoryDeclaration<EditSessionWidgetComponent, never>;
32
+ static ɵcmp: i0.ɵɵComponentDeclaration<EditSessionWidgetComponent, "lib-edit-session-widget", never, { "config": "config"; }, {}, never, never, false, never>;
33
+ }
@@ -0,0 +1,9 @@
1
+ import * as i0 from "@angular/core";
2
+ import * as i1 from "./edit-session-widget.component";
3
+ import * as i2 from "@angular/common";
4
+ import * as i3 from "@alimetry/widget-core";
5
+ export declare class EditSessionWidgetModule {
6
+ static ɵfac: i0.ɵɵFactoryDeclaration<EditSessionWidgetModule, never>;
7
+ static ɵmod: i0.ɵɵNgModuleDeclaration<EditSessionWidgetModule, [typeof i1.EditSessionWidgetComponent], [typeof i2.CommonModule, typeof i3.ComponentsModule], [typeof i1.EditSessionWidgetComponent]>;
8
+ static ɵinj: i0.ɵɵInjectorDeclaration<EditSessionWidgetModule>;
9
+ }
@@ -0,0 +1 @@
1
+ export declare const EDIT_SESSION_WIDGET_VERSION = "1.0.0-dev.0";
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "@alimetry/edit-session-widget",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0-dev.0",
4
+ "peerDependencies": {
5
+ "@angular/common": "^15.1.0",
6
+ "@angular/core": "^15.1.0"
7
+ },
8
+ "dependencies": {
9
+ "tslib": "^2.3.0",
10
+ "@alimetry/widget-core": "1.0.0-dev.0"
11
+ },
12
+ "sideEffects": false,
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "module": "fesm2015/alimetry-edit-session-widget.mjs",
17
+ "es2020": "fesm2020/alimetry-edit-session-widget.mjs",
18
+ "esm2020": "esm2020/alimetry-edit-session-widget.mjs",
19
+ "fesm2020": "fesm2020/alimetry-edit-session-widget.mjs",
20
+ "fesm2015": "fesm2015/alimetry-edit-session-widget.mjs",
21
+ "typings": "index.d.ts",
22
+ "exports": {
23
+ "./package.json": {
24
+ "default": "./package.json"
25
+ },
26
+ ".": {
27
+ "types": "./index.d.ts",
28
+ "esm2020": "./esm2020/alimetry-edit-session-widget.mjs",
29
+ "es2020": "./fesm2020/alimetry-edit-session-widget.mjs",
30
+ "es2015": "./fesm2015/alimetry-edit-session-widget.mjs",
31
+ "node": "./fesm2015/alimetry-edit-session-widget.mjs",
32
+ "default": "./fesm2020/alimetry-edit-session-widget.mjs"
33
+ }
34
+ }
6
35
  }
@@ -0,0 +1,3 @@
1
+ export * from './lib/edit-session-widget.component';
2
+ export * from './lib/edit-session-widget.module';
3
+ export * from './lib/version';