alimetry-detail-edit 0.0.1
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 +15 -0
- package/esm2020/alimetry-detail-edit.mjs +5 -0
- package/esm2020/lib/detail-edit.component.mjs +150 -0
- package/esm2020/lib/detail-edit.module.mjs +33 -0
- package/esm2020/lib/detail-edit.service.mjs +14 -0
- package/esm2020/lib/entries.pipe.mjs +16 -0
- package/esm2020/lib/utils.mjs +48 -0
- package/esm2020/lib/validators.mjs +99 -0
- package/esm2020/public-api.mjs +7 -0
- package/fesm2015/alimetry-detail-edit.mjs +359 -0
- package/fesm2015/alimetry-detail-edit.mjs.map +1 -0
- package/fesm2020/alimetry-detail-edit.mjs +357 -0
- package/fesm2020/alimetry-detail-edit.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/detail-edit.component.d.ts +35 -0
- package/lib/detail-edit.module.d.ts +10 -0
- package/lib/detail-edit.service.d.ts +6 -0
- package/lib/entries.pipe.d.ts +7 -0
- package/lib/utils.d.ts +5 -0
- package/lib/validators.d.ts +5 -0
- package/package.json +31 -0
- package/public-api.d.ts +3 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, Pipe, Component, Input, NgModule } from '@angular/core';
|
|
3
|
+
import * as i2 from '@angular/forms';
|
|
4
|
+
import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
5
|
+
import { forkJoin, tap } from 'rxjs';
|
|
6
|
+
import * as i1 from 'shared';
|
|
7
|
+
import * as i3 from '@angular/common';
|
|
8
|
+
import { JsonPipe, CommonModule } from '@angular/common';
|
|
9
|
+
|
|
10
|
+
class DetailEditService {
|
|
11
|
+
constructor() { }
|
|
12
|
+
}
|
|
13
|
+
DetailEditService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
14
|
+
DetailEditService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditService, providedIn: 'root' });
|
|
15
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditService, decorators: [{
|
|
16
|
+
type: Injectable,
|
|
17
|
+
args: [{
|
|
18
|
+
providedIn: 'root'
|
|
19
|
+
}]
|
|
20
|
+
}], ctorParameters: function () { return []; } });
|
|
21
|
+
|
|
22
|
+
// strip the timezone offset to create format usable by <input>
|
|
23
|
+
function localiseDateTime(datetime) {
|
|
24
|
+
const date = new Date(datetime);
|
|
25
|
+
const year = date.getFullYear();
|
|
26
|
+
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
27
|
+
const day = date.getDate().toString().padStart(2, '0');
|
|
28
|
+
const hour = date.getHours().toString().padStart(2, '0');
|
|
29
|
+
const minute = date.getMinutes().toString().padStart(2, '0');
|
|
30
|
+
const second = date.getSeconds().toString().padStart(2, '0');
|
|
31
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}`;
|
|
32
|
+
}
|
|
33
|
+
// reconstruct original ISO format
|
|
34
|
+
function globaliseDateTime(datetime) {
|
|
35
|
+
const date = new Date(datetime);
|
|
36
|
+
const offset = -date.getTimezoneOffset();
|
|
37
|
+
const offsetAbs = Math.abs(offset);
|
|
38
|
+
const sign = offset >= 0 ? '+' : '-';
|
|
39
|
+
const hours = String(Math.floor(offsetAbs / 60)).padStart(2, '0');
|
|
40
|
+
const minutes = String(offsetAbs % 60).padStart(2, '0');
|
|
41
|
+
const offsetString = `${sign}${hours}:${minutes}`;
|
|
42
|
+
const year = date.getFullYear();
|
|
43
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
44
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
45
|
+
const hour = String(date.getHours()).padStart(2, '0');
|
|
46
|
+
const minute = String(date.getMinutes()).padStart(2, '0');
|
|
47
|
+
const second = String(date.getSeconds()).padStart(2, '0');
|
|
48
|
+
const datePart = `${year}-${month}-${day}`;
|
|
49
|
+
const timePart = `${hour}:${minute}:${second}.000`;
|
|
50
|
+
return `${datePart}T${timePart}${offsetString}`;
|
|
51
|
+
}
|
|
52
|
+
function getAge(dob) {
|
|
53
|
+
const then = new Date(dob);
|
|
54
|
+
const now = new Date();
|
|
55
|
+
let age = now.getFullYear() - then.getFullYear();
|
|
56
|
+
if (now.getMonth() < then.getMonth() ||
|
|
57
|
+
(now.getMonth() === then.getMonth() && now.getDate() < then.getDate())) {
|
|
58
|
+
age--;
|
|
59
|
+
}
|
|
60
|
+
return age;
|
|
61
|
+
}
|
|
62
|
+
function titleise(camel) {
|
|
63
|
+
let spaced = camel.replace(/(?=[A-Z])/g, ' ');
|
|
64
|
+
return spaced.toLowerCase().replace(/\b\w/g, ch => ch.toUpperCase());
|
|
65
|
+
}
|
|
66
|
+
function shallowCompare(a, b) {
|
|
67
|
+
return Object.entries(a).every(([k, v]) => b[k] === v);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function setControlError(control, errorKey, errorValue) {
|
|
71
|
+
const currentErrors = control.errors || {};
|
|
72
|
+
if (currentErrors[errorKey])
|
|
73
|
+
return;
|
|
74
|
+
control.setErrors(Object.assign(Object.assign({}, currentErrors), { [errorKey]: errorValue }));
|
|
75
|
+
}
|
|
76
|
+
function clearControlError(control, errorKey) {
|
|
77
|
+
const currentErrors = control.errors || {};
|
|
78
|
+
if (!currentErrors[errorKey])
|
|
79
|
+
return;
|
|
80
|
+
control.setErrors(Object.assign(Object.assign({}, currentErrors), { [errorKey]: null }));
|
|
81
|
+
}
|
|
82
|
+
function dateComparison(a, b, comparator) {
|
|
83
|
+
return function (control) {
|
|
84
|
+
if (!(control instanceof FormGroup))
|
|
85
|
+
return null;
|
|
86
|
+
const formGroup = control;
|
|
87
|
+
const aControl = formGroup.get(a);
|
|
88
|
+
const bControl = formGroup.get(b);
|
|
89
|
+
if (!(aControl === null || aControl === void 0 ? void 0 : aControl.value) || !(bControl === null || bControl === void 0 ? void 0 : bControl.value)) {
|
|
90
|
+
clearControlError(aControl, 'dateRange');
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const aDate = new Date(aControl.value);
|
|
94
|
+
const bDate = new Date(bControl.value);
|
|
95
|
+
const hasError = (comparator === 'after' && aDate <= bDate ||
|
|
96
|
+
comparator === 'before' && aDate >= bDate);
|
|
97
|
+
if (hasError) {
|
|
98
|
+
setControlError(aControl, 'dateRange', { against: b, comparator });
|
|
99
|
+
return { dateRange: true };
|
|
100
|
+
}
|
|
101
|
+
clearControlError(aControl, 'dateRange');
|
|
102
|
+
return null;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function withinNYears(n, dir = 'both', of) {
|
|
106
|
+
return function (control) {
|
|
107
|
+
const timestamp = control === null || control === void 0 ? void 0 : control.value;
|
|
108
|
+
if (!timestamp || isNaN(new Date(timestamp).getTime()))
|
|
109
|
+
return null;
|
|
110
|
+
const date = new Date(timestamp);
|
|
111
|
+
const against = of !== null && of !== void 0 ? of : new Date();
|
|
112
|
+
const min = new Date(against);
|
|
113
|
+
min.setFullYear(against.getFullYear() - n);
|
|
114
|
+
const max = new Date(against);
|
|
115
|
+
max.setFullYear(against.getFullYear() + n);
|
|
116
|
+
let isValid = false;
|
|
117
|
+
if (dir === 'after')
|
|
118
|
+
isValid = date < max && date > against;
|
|
119
|
+
else if (dir === 'before')
|
|
120
|
+
isValid = date > min && date < against;
|
|
121
|
+
else
|
|
122
|
+
isValid = date > min && date < max;
|
|
123
|
+
return isValid ? null : { notWithinYears: { n, against, dir } };
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function withinNHours(n, dir = 'both', of) {
|
|
127
|
+
return function (control) {
|
|
128
|
+
const timestamp = control === null || control === void 0 ? void 0 : control.value;
|
|
129
|
+
if (!timestamp || isNaN(new Date(timestamp).getTime()))
|
|
130
|
+
return null;
|
|
131
|
+
const date = new Date(timestamp);
|
|
132
|
+
const against = of !== null && of !== void 0 ? of : new Date();
|
|
133
|
+
const min = new Date(against);
|
|
134
|
+
min.setHours(against.getHours() - n);
|
|
135
|
+
const max = new Date(against);
|
|
136
|
+
max.setHours(against.getHours() + n);
|
|
137
|
+
let isValid = false;
|
|
138
|
+
if (dir === 'after')
|
|
139
|
+
isValid = date < max && date > against;
|
|
140
|
+
else if (dir === 'before')
|
|
141
|
+
isValid = date > min && date < against;
|
|
142
|
+
else
|
|
143
|
+
isValid = date > min && date < max;
|
|
144
|
+
return isValid ? null : { notWithinHours: { n, against, dir } };
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// translate validation errors into human readable strings
|
|
148
|
+
function translateError(name, control) {
|
|
149
|
+
const { errors } = control;
|
|
150
|
+
if (control.hasError('required'))
|
|
151
|
+
return `${titleise(name)} is a required value.`;
|
|
152
|
+
else if (control.hasError('min'))
|
|
153
|
+
return `${titleise(name)} needs to be a minimum of ${errors.min.min}.`;
|
|
154
|
+
else if (control.hasError('max'))
|
|
155
|
+
return `${titleise(name)} needs to be a maximum of ${errors.max.max}.`;
|
|
156
|
+
else if (control.hasError('future'))
|
|
157
|
+
return `${titleise(name)} needs to be in the past.`;
|
|
158
|
+
else if (control.hasError('dateRange'))
|
|
159
|
+
return `${titleise(name)} needs to be ${errors.dateRange.comparator} ${titleise(errors.dateRange.against)}.`;
|
|
160
|
+
else if (control.hasError('notWithinYears'))
|
|
161
|
+
return `${titleise(name)} needs to be within ${errors.notWithinYears.n} years of ${errors.notWithinYears.against}`;
|
|
162
|
+
else if (control.hasError('notWithinHours'))
|
|
163
|
+
return `${titleise(name)} needs to be within ${errors.notWithinHours.n} hours of ${errors.notWithinHours.against}`;
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
class EntriesPipe {
|
|
168
|
+
transform(value) {
|
|
169
|
+
return Object.entries(value);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
EntriesPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EntriesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
173
|
+
EntriesPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: EntriesPipe, name: "entries" });
|
|
174
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EntriesPipe, decorators: [{
|
|
175
|
+
type: Pipe,
|
|
176
|
+
args: [{
|
|
177
|
+
name: 'entries'
|
|
178
|
+
}]
|
|
179
|
+
}] });
|
|
180
|
+
|
|
181
|
+
const appDataPropertySetId = '34d46477-ad4b-47b4-b555-06358b92f60c';
|
|
182
|
+
const deviceDataPropertySetId = '7a32d851-5e89-46a6-a4b6-cca162dace29';
|
|
183
|
+
const propertyCodes = ['RecordingSession', 'Status', 'UserDetailChangeLog'];
|
|
184
|
+
class DetailEditComponent {
|
|
185
|
+
constructor(shared) {
|
|
186
|
+
this.shared = shared;
|
|
187
|
+
this.status = 'loading';
|
|
188
|
+
this.formGroup = new FormGroup({
|
|
189
|
+
dob: new FormControl('', [Validators.required, withinNYears(120, 'before')]),
|
|
190
|
+
height: new FormControl(0, [Validators.required, Validators.min(30), Validators.max(272)]),
|
|
191
|
+
weight: new FormControl(0, [Validators.required, Validators.min(1), Validators.max(635)]),
|
|
192
|
+
mealStart: new FormControl('', [Validators.required]),
|
|
193
|
+
mealEnd: new FormControl('', [Validators.required])
|
|
194
|
+
}, [
|
|
195
|
+
dateComparison('mealEnd', 'mealStart', 'after'),
|
|
196
|
+
dateComparison('mealStart', 'dob', 'after'),
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
parseInputErrors(name, control) {
|
|
200
|
+
return translateError(name, control);
|
|
201
|
+
}
|
|
202
|
+
// make sure than modifications that don't actually differ from the current are marked as such
|
|
203
|
+
monitorFormChanges() {
|
|
204
|
+
this.formGroup.valueChanges.subscribe(current => {
|
|
205
|
+
if (shallowCompare(current, this.initialFormState)) {
|
|
206
|
+
this.formGroup.markAsPristine();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
// handle the relevant data from the response body
|
|
211
|
+
extractFields(recordingSession) {
|
|
212
|
+
const { patient, mealStart, mealEnd, recordingStart } = recordingSession;
|
|
213
|
+
this.recordingStart = recordingStart;
|
|
214
|
+
return {
|
|
215
|
+
dob: this.config.owners[0].dateOfBirth,
|
|
216
|
+
height: patient.height,
|
|
217
|
+
weight: patient.weight,
|
|
218
|
+
mealStart: localiseDateTime(mealStart),
|
|
219
|
+
mealEnd: localiseDateTime(mealEnd)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
initialiseForm(state) {
|
|
223
|
+
this.formGroup.patchValue(state);
|
|
224
|
+
this.formGroup.markAsPristine();
|
|
225
|
+
this.formGroup.get('mealStart').addValidators(withinNHours(24, 'after', new Date(this.recordingStart)));
|
|
226
|
+
this.formGroup.get('mealEnd').addValidators(withinNHours(24, 'after', new Date(this.recordingStart)));
|
|
227
|
+
this.initialFormState = state; // save this for use in resets
|
|
228
|
+
this.monitorFormChanges();
|
|
229
|
+
}
|
|
230
|
+
// construct a patch object for the save request
|
|
231
|
+
integrateFields(mods, recordingSession) {
|
|
232
|
+
const { patient } = recordingSession;
|
|
233
|
+
patient.height = mods.height;
|
|
234
|
+
patient.weight = mods.weight;
|
|
235
|
+
patient.age = getAge(mods.dob);
|
|
236
|
+
recordingSession.mealStart = globaliseDateTime(mods.mealStart);
|
|
237
|
+
recordingSession.mealEnd = globaliseDateTime(mods.mealEnd);
|
|
238
|
+
this.appendChangeLog(mods);
|
|
239
|
+
return {
|
|
240
|
+
'Age': patient.age,
|
|
241
|
+
'RecordingSession': JSON.stringify(recordingSession),
|
|
242
|
+
'UserDetailChangeLog': this.detailChangeLog
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
postDeviceData(devicePropertySetId, data) {
|
|
246
|
+
return this.shared.saveDeviceData(this.config.deviceDataModelId, this.deviceDataId, devicePropertySetId, this.config.owners[0].userId, data);
|
|
247
|
+
}
|
|
248
|
+
// patch the device data record with the modifications
|
|
249
|
+
saveModifications() {
|
|
250
|
+
const mods = this.formGroup.getRawValue();
|
|
251
|
+
const patchedDeviceData = this.integrateFields(mods, this.recordingSession);
|
|
252
|
+
const patchedUserData = Object.assign(this.config.owners[0], { dateOfBirth: mods.dob });
|
|
253
|
+
this.status = 'sending';
|
|
254
|
+
forkJoin([
|
|
255
|
+
this.postDeviceData(appDataPropertySetId, patchedDeviceData),
|
|
256
|
+
this.shared.updateUser(this.config.owners[0].userId, patchedUserData)
|
|
257
|
+
]).pipe(tap(() => this.regenReport())).subscribe();
|
|
258
|
+
}
|
|
259
|
+
appendChangeLog(mods) {
|
|
260
|
+
const prevChangeLog = {};
|
|
261
|
+
const newChangeLog = {};
|
|
262
|
+
// Only include fields that have changed
|
|
263
|
+
for (const key in mods) {
|
|
264
|
+
if (this.initialFormState[key] !== mods[key]) {
|
|
265
|
+
prevChangeLog[key] = this.initialFormState[key];
|
|
266
|
+
newChangeLog[key] = mods[key];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const changeLog = {
|
|
270
|
+
author: this.config.currentUser.emailAddress,
|
|
271
|
+
prevChangeLog: prevChangeLog,
|
|
272
|
+
newChangeLog: newChangeLog,
|
|
273
|
+
timestamp: new Date().toISOString()
|
|
274
|
+
};
|
|
275
|
+
this.detailChangeLog.push(changeLog);
|
|
276
|
+
}
|
|
277
|
+
// get the device data record and initialise the form
|
|
278
|
+
loadDeviceData() {
|
|
279
|
+
this.shared.getDeviceData(this.config.deviceDataModelId, this.config.owners[0].userId, propertyCodes).subscribe({
|
|
280
|
+
next: (res) => {
|
|
281
|
+
var _a;
|
|
282
|
+
this.deviceDataId = res.deviceDataId;
|
|
283
|
+
this.recordingSession = JSON.parse(res.data['RecordingSession'].value);
|
|
284
|
+
this.initialiseForm(this.extractFields(this.recordingSession));
|
|
285
|
+
// checking for undefined in case no changes have been logged yet
|
|
286
|
+
this.detailChangeLog = res.data['UserDetailChangeLog'] === undefined ? [] : res.data['UserDetailChangeLog'].value;
|
|
287
|
+
this.status = ((_a = res.data['Status']) === null || _a === void 0 ? void 0 : _a.value) === 'Generating Report' ?
|
|
288
|
+
'generating' :
|
|
289
|
+
'success';
|
|
290
|
+
},
|
|
291
|
+
error: (err) => {
|
|
292
|
+
console.error(err);
|
|
293
|
+
this.status = "error";
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
regenReport() {
|
|
298
|
+
this.postDeviceData(deviceDataPropertySetId, { 'Algorithmhasrun': false, 'AlgorithmRun': true }).subscribe(() => {
|
|
299
|
+
this.status = 'generating'; // assume report starts generating immediately
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
resetModifications() {
|
|
303
|
+
this.formGroup.patchValue(this.initialFormState);
|
|
304
|
+
}
|
|
305
|
+
ngOnInit() {
|
|
306
|
+
var _a, _b, _c;
|
|
307
|
+
if (!((_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.owners) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.userId)) {
|
|
308
|
+
this.status = 'nouser';
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
this.loadDeviceData();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
DetailEditComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditComponent, deps: [{ token: i1.SharedService }], target: i0.ɵɵFactoryTarget.Component });
|
|
315
|
+
DetailEditComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: DetailEditComponent, selector: "lib-detail-edit", inputs: { config: "config" }, ngImport: i0, template: "<div class=\"wrapper\">\n <div class=\"status\" *ngIf=\"status === 'error'\">\n An error occured while trying to get the details.\n </div>\n <div class=\"status\" *ngIf=\"status === 'nouser'\">\n There is no user specified as the data owner.\n </div>\n <form *ngIf=\"status !== 'error' && status !== 'nouser'\" [formGroup]=\"formGroup\" (ngSubmit)=\"saveModifications()\">\n <div class=\"inputs\">\n <div class=\"form-row\">\n <label for=\"dob\">Date of Birth:</label>\n <input id=\"dob\" type=\"date\" formControlName=\"dob\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"weight\">Patient Weight (kg):</label>\n <input id=\"weight\" type=\"number\" formControlName=\"weight\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"height\">Patient Height (cm):</label>\n <input id=\"height\" type=\"number\" formControlName=\"height\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"mealStart\">Meal Start Time:</label>\n <input id=\"mealStart\" type=\"datetime-local\" formControlName=\"mealStart\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"mealEnd\">Meal End Time:</label>\n <input id=\"mealEnd\" type=\"datetime-local\" formControlName=\"mealEnd\">\n </div>\n </div>\n\n <div *ngIf=\"!formGroup.valid && formGroup.dirty\" class=\"errors\">\n <div *ngFor=\"let control of formGroup.controls | entries\">\n {{ parseInputErrors(control[0], control[1]) }}\n </div>\n </div>\n\n <div *ngIf=\"status === 'generating'\" class=\"info\">\n <div>The report is generating at the moment, check back later if you need to modify this entry.</div>\n </div>\n\n <div class=\"buttons\">\n <button type=\"button\" (click)=\"regenReport()\" [disabled]=\"status === 'sending' || status === 'generating' || status === 'loading'\">Regenerate Report</button>\n <button type=\"button\" (click)=\"resetModifications()\" [disabled]=\"status === 'sending' || status === 'generating' || formGroup.pristine\">Cancel Modification</button>\n <button type=\"submit\" [disabled]=\"status === 'sending' || status === 'generating' || !formGroup.valid || formGroup.pristine\">Save Details</button>\n </div>\n </form>\n</div>\n", styles: [".wrapper{width:100%;height:100%}.status{font-size:2rem;margin:5rem auto;text-align:center}form{display:flex;flex-direction:column;padding:2rem;gap:2rem;font-size:.9rem;max-width:70rem;margin:0 auto}form .inputs{display:flex;flex-flow:row wrap;gap:1rem 3rem}form .inputs .form-row{display:flex;gap:1rem;align-items:center;flex:1 0 calc(50% - 3rem);min-width:400px}form .inputs .form-row label{min-width:9rem}form .inputs .form-row input{background:#f5f5f6;height:36px;border:none;padding-inline:10px;flex-grow:1}form.ng-dirty .form-row .ng-invalid{outline:1px auto red}form .buttons{display:flex;gap:1rem}form .buttons button{padding:.5rem 1rem;height:36px;border:none;font-size:inherit;background:#f5f5f6}form .buttons button:not([disabled]){cursor:pointer}form .buttons button[type=submit]{background:#242C69;color:#fff}form .buttons button[disabled]{background:#fafafa;cursor:not-allowed}form .buttons button[type=submit][disabled]{background:#444C89}form .buttons button:first-child{margin-right:auto}.errors{padding:1rem;background:#fee;border:2px solid red;border-radius:4px;color:red}.info{padding:1rem;background:#eef;border:2px solid #242C69;border-radius:4px;color:#242c69}\n"], dependencies: [{ kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: EntriesPipe, name: "entries" }] });
|
|
316
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditComponent, decorators: [{
|
|
317
|
+
type: Component,
|
|
318
|
+
args: [{ selector: 'lib-detail-edit', template: "<div class=\"wrapper\">\n <div class=\"status\" *ngIf=\"status === 'error'\">\n An error occured while trying to get the details.\n </div>\n <div class=\"status\" *ngIf=\"status === 'nouser'\">\n There is no user specified as the data owner.\n </div>\n <form *ngIf=\"status !== 'error' && status !== 'nouser'\" [formGroup]=\"formGroup\" (ngSubmit)=\"saveModifications()\">\n <div class=\"inputs\">\n <div class=\"form-row\">\n <label for=\"dob\">Date of Birth:</label>\n <input id=\"dob\" type=\"date\" formControlName=\"dob\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"weight\">Patient Weight (kg):</label>\n <input id=\"weight\" type=\"number\" formControlName=\"weight\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"height\">Patient Height (cm):</label>\n <input id=\"height\" type=\"number\" formControlName=\"height\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"mealStart\">Meal Start Time:</label>\n <input id=\"mealStart\" type=\"datetime-local\" formControlName=\"mealStart\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"mealEnd\">Meal End Time:</label>\n <input id=\"mealEnd\" type=\"datetime-local\" formControlName=\"mealEnd\">\n </div>\n </div>\n\n <div *ngIf=\"!formGroup.valid && formGroup.dirty\" class=\"errors\">\n <div *ngFor=\"let control of formGroup.controls | entries\">\n {{ parseInputErrors(control[0], control[1]) }}\n </div>\n </div>\n\n <div *ngIf=\"status === 'generating'\" class=\"info\">\n <div>The report is generating at the moment, check back later if you need to modify this entry.</div>\n </div>\n\n <div class=\"buttons\">\n <button type=\"button\" (click)=\"regenReport()\" [disabled]=\"status === 'sending' || status === 'generating' || status === 'loading'\">Regenerate Report</button>\n <button type=\"button\" (click)=\"resetModifications()\" [disabled]=\"status === 'sending' || status === 'generating' || formGroup.pristine\">Cancel Modification</button>\n <button type=\"submit\" [disabled]=\"status === 'sending' || status === 'generating' || !formGroup.valid || formGroup.pristine\">Save Details</button>\n </div>\n </form>\n</div>\n", styles: [".wrapper{width:100%;height:100%}.status{font-size:2rem;margin:5rem auto;text-align:center}form{display:flex;flex-direction:column;padding:2rem;gap:2rem;font-size:.9rem;max-width:70rem;margin:0 auto}form .inputs{display:flex;flex-flow:row wrap;gap:1rem 3rem}form .inputs .form-row{display:flex;gap:1rem;align-items:center;flex:1 0 calc(50% - 3rem);min-width:400px}form .inputs .form-row label{min-width:9rem}form .inputs .form-row input{background:#f5f5f6;height:36px;border:none;padding-inline:10px;flex-grow:1}form.ng-dirty .form-row .ng-invalid{outline:1px auto red}form .buttons{display:flex;gap:1rem}form .buttons button{padding:.5rem 1rem;height:36px;border:none;font-size:inherit;background:#f5f5f6}form .buttons button:not([disabled]){cursor:pointer}form .buttons button[type=submit]{background:#242C69;color:#fff}form .buttons button[disabled]{background:#fafafa;cursor:not-allowed}form .buttons button[type=submit][disabled]{background:#444C89}form .buttons button:first-child{margin-right:auto}.errors{padding:1rem;background:#fee;border:2px solid red;border-radius:4px;color:red}.info{padding:1rem;background:#eef;border:2px solid #242C69;border-radius:4px;color:#242c69}\n"] }]
|
|
319
|
+
}], ctorParameters: function () { return [{ type: i1.SharedService }]; }, propDecorators: { config: [{
|
|
320
|
+
type: Input
|
|
321
|
+
}] } });
|
|
322
|
+
|
|
323
|
+
class DetailEditModule {
|
|
324
|
+
}
|
|
325
|
+
DetailEditModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
326
|
+
DetailEditModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: DetailEditModule, declarations: [DetailEditComponent,
|
|
327
|
+
EntriesPipe], imports: [JsonPipe,
|
|
328
|
+
ReactiveFormsModule,
|
|
329
|
+
CommonModule], exports: [DetailEditComponent] });
|
|
330
|
+
DetailEditModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditModule, imports: [ReactiveFormsModule,
|
|
331
|
+
CommonModule] });
|
|
332
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DetailEditModule, decorators: [{
|
|
333
|
+
type: NgModule,
|
|
334
|
+
args: [{
|
|
335
|
+
declarations: [
|
|
336
|
+
DetailEditComponent,
|
|
337
|
+
EntriesPipe
|
|
338
|
+
],
|
|
339
|
+
imports: [
|
|
340
|
+
JsonPipe,
|
|
341
|
+
ReactiveFormsModule,
|
|
342
|
+
CommonModule
|
|
343
|
+
],
|
|
344
|
+
exports: [
|
|
345
|
+
DetailEditComponent
|
|
346
|
+
]
|
|
347
|
+
}]
|
|
348
|
+
}] });
|
|
349
|
+
|
|
350
|
+
/*
|
|
351
|
+
* Public API Surface of detail-edit
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Generated bundle index. Do not edit.
|
|
356
|
+
*/
|
|
357
|
+
|
|
358
|
+
export { DetailEditComponent, DetailEditModule, DetailEditService };
|
|
359
|
+
//# sourceMappingURL=alimetry-detail-edit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alimetry-detail-edit.mjs","sources":["../../../projects/detail-edit/src/lib/detail-edit.service.ts","../../../projects/detail-edit/src/lib/utils.ts","../../../projects/detail-edit/src/lib/validators.ts","../../../projects/detail-edit/src/lib/entries.pipe.ts","../../../projects/detail-edit/src/lib/detail-edit.component.ts","../../../projects/detail-edit/src/lib/detail-edit.component.html","../../../projects/detail-edit/src/lib/detail-edit.module.ts","../../../projects/detail-edit/src/public-api.ts","../../../projects/detail-edit/src/alimetry-detail-edit.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class DetailEditService {\n\n constructor() { }\n}\n","// strip the timezone offset to create format usable by <input>\nexport function localiseDateTime(datetime: string) {\n const date = new Date(datetime);\n\n const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0');\n const day = date.getDate().toString().padStart(2, '0');\n const hour = date.getHours().toString().padStart(2, '0');\n const minute = date.getMinutes().toString().padStart(2, '0');\n const second = date.getSeconds().toString().padStart(2, '0');\n\n return `${year}-${month}-${day}T${hour}:${minute}:${second}`;\n}\n\n// reconstruct original ISO format\nexport function globaliseDateTime(datetime: string) {\n const date = new Date(datetime);\n\n const offset = -date.getTimezoneOffset();\n const offsetAbs = Math.abs(offset);\n\n const sign = offset >= 0 ? '+' : '-';\n const hours = String(Math.floor(offsetAbs / 60)).padStart(2, '0');\n const minutes = String(offsetAbs % 60).padStart(2, '0');\n const offsetString = `${sign}${hours}:${minutes}`;\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n const hour = String(date.getHours()).padStart(2, '0');\n const minute = String(date.getMinutes()).padStart(2, '0');\n const second = String(date.getSeconds()).padStart(2, '0');\n\n const datePart = `${year}-${month}-${day}`;\n const timePart = `${hour}:${minute}:${second}.000`;\n\n return `${datePart}T${timePart}${offsetString}`;\n}\n\nexport function getAge(dob: string) {\n const then = new Date(dob);\n const now = new Date();\n\n let age = now.getFullYear() - then.getFullYear();\n\n if (now.getMonth() < then.getMonth() ||\n (now.getMonth() === then.getMonth() && now.getDate() < then.getDate())) {\n age--;\n }\n\n return age;\n}\n\nexport function titleise(camel: string) {\n let spaced = camel.replace(/(?=[A-Z])/g, ' ');\n return spaced.toLowerCase().replace(/\\b\\w/g, ch => ch.toUpperCase());\n}\n\nexport function shallowCompare(a: object, b: object) {\n return Object.entries(a).every(([k, v]) => b[k] === v);\n}\n","import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn } from \"@angular/forms\";\nimport { titleise } from \"./utils\";\n\nfunction setControlError(control: AbstractControl, errorKey: string, errorValue: any) {\n const currentErrors = control.errors || {};\n if (currentErrors[errorKey]) return;\n control.setErrors({ ...currentErrors, [errorKey]: errorValue });\n}\n\nfunction clearControlError(control: AbstractControl, errorKey: string) {\n const currentErrors = control.errors || {};\n if (!currentErrors[errorKey]) return;\n control.setErrors({ ...currentErrors, [errorKey]: null });\n}\n\nexport function dateComparison(a: string, b: string, comparator: 'before' | 'after') {\n return function(control: AbstractControl<any, any>): ValidationErrors | null {\n if (!(control instanceof FormGroup)) return null;\n\n const formGroup = control as FormGroup;\n\n const aControl = formGroup.get(a);\n const bControl = formGroup.get(b);\n\n if (!aControl?.value || !bControl?.value) {\n clearControlError(aControl, 'dateRange');\n return null;\n }\n\n const aDate = new Date(aControl.value);\n const bDate = new Date(bControl.value);\n\n const hasError = (\n comparator === 'after' && aDate <= bDate ||\n comparator === 'before' && aDate >= bDate\n );\n\n if (hasError) {\n setControlError(aControl, 'dateRange', { against: b, comparator });\n return { dateRange: true };\n }\n\n clearControlError(aControl, 'dateRange');\n return null;\n }\n}\n\nexport function withinNYears(n: number, dir: 'both' | 'before' | 'after' = 'both', of?: Date): ValidatorFn {\n return function(control: AbstractControl): ValidationErrors | null {\n const timestamp = control?.value;\n if (!timestamp || isNaN(new Date(timestamp).getTime())) return null;\n\n const date = new Date(timestamp);\n const against = of ?? new Date();\n\n const min = new Date(against)\n min.setFullYear(against.getFullYear() - n);\n const max = new Date(against)\n max.setFullYear(against.getFullYear() + n);\n\n let isValid = false;\n if (dir === 'after') isValid = date < max && date > against;\n else if (dir === 'before') isValid = date > min && date < against;\n else isValid = date > min && date < max;\n\n return isValid ? null : { notWithinYears: { n, against, dir } };\n }\n}\n\nexport function withinNHours(n: number, dir: 'both' | 'before' | 'after' = 'both', of?: Date): ValidatorFn {\n return function(control: AbstractControl): ValidationErrors | null {\n const timestamp = control?.value;\n if (!timestamp || isNaN(new Date(timestamp).getTime())) return null;\n\n const date = new Date(timestamp);\n const against = of ?? new Date();\n\n const min = new Date(against)\n min.setHours(against.getHours() - n);\n const max = new Date(against)\n max.setHours(against.getHours() + n);\n\n let isValid = false;\n if (dir === 'after') isValid = date < max && date > against;\n else if (dir === 'before') isValid = date > min && date < against;\n else isValid = date > min && date < max;\n\n return isValid ? null : { notWithinHours: { n, against, dir } };\n }\n}\n\n// translate validation errors into human readable strings\nexport function translateError(name: string, control: AbstractControl) {\n const { errors } = control;\n\n if (control.hasError('required'))\n return `${titleise(name)} is a required value.`;\n else if (control.hasError('min'))\n return `${titleise(name)} needs to be a minimum of ${errors.min.min}.`;\n else if (control.hasError('max'))\n return `${titleise(name)} needs to be a maximum of ${errors.max.max}.`;\n else if (control.hasError('future'))\n return `${titleise(name)} needs to be in the past.`;\n else if (control.hasError('dateRange'))\n return `${titleise(name)} needs to be ${errors.dateRange.comparator} ${titleise(errors.dateRange.against)}.`;\n else if (control.hasError('notWithinYears'))\n return `${titleise(name)} needs to be within ${errors.notWithinYears.n} years of ${errors.notWithinYears.against}`;\n else if (control.hasError('notWithinHours'))\n return `${titleise(name)} needs to be within ${errors.notWithinHours.n} hours of ${errors.notWithinHours.against}`;\n\n return null;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'entries'\n})\nexport class EntriesPipe implements PipeTransform {\n transform(value: object): [string, any][] {\n return Object.entries(value);\n }\n}\n","import { Component, Input } from '@angular/core';\nimport { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms';\nimport { forkJoin, switchMap, tap } from 'rxjs';\nimport { Config, SharedService } from 'shared';\nimport { getAge, globaliseDateTime, localiseDateTime, shallowCompare } from './utils';\nimport { dateComparison, withinNYears, withinNHours, translateError } from './validators';\n\nconst appDataPropertySetId = '34d46477-ad4b-47b4-b555-06358b92f60c';\nconst deviceDataPropertySetId = '7a32d851-5e89-46a6-a4b6-cca162dace29';\nconst propertyCodes = ['RecordingSession', 'Status', 'UserDetailChangeLog'];\n\n@Component({\n selector: 'lib-detail-edit', templateUrl: './detail-edit.component.html',\n styleUrls: ['./detail-edit.component.css']\n})\nexport class DetailEditComponent {\n @Input() config!: Config;\n\n status: 'error' | 'loading' | 'success' | 'generating' | 'sending' | 'nouser' = 'loading';\n\n // populated by load request\n private deviceDataId: string;\n private recordingSession: object;\n private recordingStart: string;\n private initialFormState: object;\n private detailChangeLog: object[];\n\n formGroup = new FormGroup({\n dob: new FormControl<string>('', [Validators.required, withinNYears(120, 'before')]),\n height: new FormControl<number>(0, [Validators.required, Validators.min(30), Validators.max(272)]),\n weight: new FormControl<number>(0, [Validators.required, Validators.min(1), Validators.max(635)]),\n mealStart: new FormControl<string>('', [Validators.required]),\n mealEnd: new FormControl<string>('', [Validators.required])\n }, [\n dateComparison('mealEnd', 'mealStart', 'after'),\n dateComparison('mealStart', 'dob', 'after'),\n ]);\n\n constructor(private shared: SharedService) { }\n\n parseInputErrors(name: string, control: AbstractControl) {\n return translateError(name, control);\n }\n\n // make sure than modifications that don't actually differ from the current are marked as such\n private monitorFormChanges() {\n this.formGroup.valueChanges.subscribe(current => {\n if (shallowCompare(current, this.initialFormState)) {\n this.formGroup.markAsPristine();\n }\n })\n }\n\n // handle the relevant data from the response body\n private extractFields(recordingSession: Record<string, any>) {\n const { patient, mealStart, mealEnd, recordingStart } = recordingSession;\n this.recordingStart = recordingStart;\n\n return {\n dob: this.config.owners[0].dateOfBirth,\n height: patient.height,\n weight: patient.weight,\n mealStart: localiseDateTime(mealStart),\n mealEnd: localiseDateTime(mealEnd)\n }\n }\n\n private initialiseForm(state: object) {\n this.formGroup.patchValue(state);\n this.formGroup.markAsPristine();\n\n this.formGroup.get('mealStart').addValidators(withinNHours(24, 'after', new Date(this.recordingStart)));\n this.formGroup.get('mealEnd').addValidators(withinNHours(24, 'after', new Date(this.recordingStart)));\n\n this.initialFormState = state; // save this for use in resets\n this.monitorFormChanges();\n }\n\n // construct a patch object for the save request\n private integrateFields(mods: typeof this.formGroup.value, recordingSession: Record<string, any>) {\n const { patient } = recordingSession;\n patient.height = mods.height;\n patient.weight = mods.weight;\n patient.age = getAge(mods.dob);\n\n recordingSession.mealStart = globaliseDateTime(mods.mealStart);\n recordingSession.mealEnd = globaliseDateTime(mods.mealEnd);\n\n this.appendChangeLog(mods);\n\n return {\n 'Age': patient.age,\n 'RecordingSession': JSON.stringify(recordingSession),\n 'UserDetailChangeLog': this.detailChangeLog\n }\n }\n\n private postDeviceData(devicePropertySetId: string, data: object) {\n return this.shared.saveDeviceData(\n this.config.deviceDataModelId,\n this.deviceDataId,\n devicePropertySetId,\n this.config.owners[0].userId,\n data\n );\n }\n\n // patch the device data record with the modifications\n saveModifications() {\n const mods = this.formGroup.getRawValue();\n const patchedDeviceData = this.integrateFields(mods, this.recordingSession);\n const patchedUserData = Object.assign(this.config.owners[0], { dateOfBirth: mods.dob });\n\n this.status = 'sending';\n forkJoin([\n this.postDeviceData(appDataPropertySetId, patchedDeviceData),\n this.shared.updateUser(this.config.owners[0].userId, patchedUserData)\n ]).pipe(\n tap(() => this.regenReport())\n ).subscribe();\n }\n\n private appendChangeLog(mods: typeof this.formGroup.value) {\n const prevChangeLog: Partial<typeof this.formGroup.value> = {};\n const newChangeLog: Partial<typeof this.formGroup.value> = {};\n\n // Only include fields that have changed\n for (const key in mods) {\n if (this.initialFormState[key] !== mods[key]) {\n prevChangeLog[key] = this.initialFormState[key];\n newChangeLog[key] = mods[key];\n }\n }\n\n const changeLog = {\n author: this.config.currentUser.emailAddress,\n prevChangeLog: prevChangeLog,\n newChangeLog: newChangeLog,\n timestamp: new Date().toISOString()\n }\n\n this.detailChangeLog.push(changeLog);\n }\n\n // get the device data record and initialise the form\n private loadDeviceData() {\n this.shared.getDeviceData(\n this.config.deviceDataModelId,\n this.config.owners[0].userId,\n propertyCodes\n ).subscribe({\n next: (res) => {\n this.deviceDataId = res.deviceDataId;\n this.recordingSession = JSON.parse(res.data['RecordingSession'].value);\n this.initialiseForm(this.extractFields(this.recordingSession));\n // checking for undefined in case no changes have been logged yet\n this.detailChangeLog = res.data['UserDetailChangeLog'] === undefined ? [] : res.data['UserDetailChangeLog'].value;\n\n this.status = res.data['Status']?.value === 'Generating Report' ?\n 'generating' :\n 'success';\n },\n error: (err) => {\n console.error(err);\n this.status = \"error\";\n }\n });\n }\n\n regenReport() {\n this.postDeviceData(\n deviceDataPropertySetId,\n { 'Algorithmhasrun': false, 'AlgorithmRun': true }\n ).subscribe(() => {\n this.status = 'generating'; // assume report starts generating immediately\n })\n }\n\n resetModifications() {\n this.formGroup.patchValue(this.initialFormState);\n }\n\n ngOnInit() {\n if (!this.config?.owners?.[0]?.userId) {\n this.status = 'nouser';\n return;\n }\n\n this.loadDeviceData();\n }\n}\n","<div class=\"wrapper\">\n <div class=\"status\" *ngIf=\"status === 'error'\">\n An error occured while trying to get the details.\n </div>\n <div class=\"status\" *ngIf=\"status === 'nouser'\">\n There is no user specified as the data owner.\n </div>\n <form *ngIf=\"status !== 'error' && status !== 'nouser'\" [formGroup]=\"formGroup\" (ngSubmit)=\"saveModifications()\">\n <div class=\"inputs\">\n <div class=\"form-row\">\n <label for=\"dob\">Date of Birth:</label>\n <input id=\"dob\" type=\"date\" formControlName=\"dob\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"weight\">Patient Weight (kg):</label>\n <input id=\"weight\" type=\"number\" formControlName=\"weight\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"height\">Patient Height (cm):</label>\n <input id=\"height\" type=\"number\" formControlName=\"height\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"mealStart\">Meal Start Time:</label>\n <input id=\"mealStart\" type=\"datetime-local\" formControlName=\"mealStart\">\n </div>\n\n <div class=\"form-row\">\n <label for=\"mealEnd\">Meal End Time:</label>\n <input id=\"mealEnd\" type=\"datetime-local\" formControlName=\"mealEnd\">\n </div>\n </div>\n\n <div *ngIf=\"!formGroup.valid && formGroup.dirty\" class=\"errors\">\n <div *ngFor=\"let control of formGroup.controls | entries\">\n {{ parseInputErrors(control[0], control[1]) }}\n </div>\n </div>\n\n <div *ngIf=\"status === 'generating'\" class=\"info\">\n <div>The report is generating at the moment, check back later if you need to modify this entry.</div>\n </div>\n\n <div class=\"buttons\">\n <button type=\"button\" (click)=\"regenReport()\" [disabled]=\"status === 'sending' || status === 'generating' || status === 'loading'\">Regenerate Report</button>\n <button type=\"button\" (click)=\"resetModifications()\" [disabled]=\"status === 'sending' || status === 'generating' || formGroup.pristine\">Cancel Modification</button>\n <button type=\"submit\" [disabled]=\"status === 'sending' || status === 'generating' || !formGroup.valid || formGroup.pristine\">Save Details</button>\n </div>\n </form>\n</div>\n","import { CommonModule, JsonPipe } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { DetailEditComponent } from './detail-edit.component';\nimport { EntriesPipe } from './entries.pipe';\n\n\n\n@NgModule({\n declarations: [\n DetailEditComponent,\n EntriesPipe\n ],\n imports: [\n JsonPipe,\n ReactiveFormsModule,\n CommonModule\n ],\n exports: [\n DetailEditComponent\n ]\n})\nexport class DetailEditModule { }\n","/*\n * Public API Surface of detail-edit\n */\n\nexport * from './lib/detail-edit.service';\nexport * from './lib/detail-edit.component';\nexport * from './lib/detail-edit.module';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i4.EntriesPipe"],"mappings":"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"}
|