Skip to content

Commit ac51c05

Browse files
[O2B-1140] Extract ALICE magnets configuration snapshot form component (#1450)
* [O2B-1140] Extract ALICE magnets configuration snapshot form component * Add hints on how to fill magnets info * Fix tests * Fix linter
1 parent 045cba6 commit ac51c05

13 files changed

Lines changed: 353 additions & 182 deletions

File tree

lib/domain/dtos/eosReport/CreateEosReportDto.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const { SHIFT_TYPES, ShiftTypes } = require('../../enums/ShiftTypes.js');
2828
* @property {string} [infoForRmRc]
2929
*/
3030

31-
const MagnetConfigurationSchema = Joi.object({ solenoid: Joi.string(), dipole: Joi.string() });
31+
const MagnetsConfigurationSchema = Joi.object({ solenoid: Joi.string(), dipole: Joi.string() });
3232

3333
const CreateEosReportDto = Joi.object({
3434
query: {
@@ -64,12 +64,12 @@ const CreateEosReportDto = Joi.object({
6464
is: ShiftTypes.SL,
6565
then: Joi.object({
6666
magnets: Joi.object({
67-
start: MagnetConfigurationSchema,
67+
start: MagnetsConfigurationSchema,
6868
intermediates: Joi.array().items(Joi.object({
69-
timestamp: Joi.string().pattern(/\d{2}:\d{2}:\d{2}/),
70-
magnetConfiguration: MagnetConfigurationSchema,
69+
timestamp: Joi.date().required(),
70+
magnetsConfiguration: MagnetsConfigurationSchema,
7171
})),
72-
end: MagnetConfigurationSchema,
72+
end: MagnetsConfigurationSchema,
7373
}),
7474
}),
7575
},

lib/public/components/common/form/inputs/DateTimeInputComponent.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import { h } from '/js/src/index.js';
1414
import { iconX } from '/js/src/icons.js';
1515
import { MILLISECONDS_IN_ONE_DAY } from '../../../../utilities/dateUtils.js';
16-
import { formatTimestampForDateTimeInput } from '../../../../utilities/formatting/HTMLInputDateFormat.js';
16+
import { formatTimestampForDateTimeInput } from '../../../../utilities/formatting/dateTimeInputFormatters.mjs';
1717
import { StatefulComponent } from '../../StatefulComponent.js';
1818

1919
/**
@@ -26,6 +26,7 @@ import { StatefulComponent } from '../../StatefulComponent.js';
2626
* @property {DateTimeInputRawData} value the actual inputs values
2727
* @property {function} onChange function called with the new value when it changes
2828
* @property {Partial<DateTimeInputRawData>} [defaults] the default raw values to use when partially updating inputs
29+
* @property {boolean} [seconds=false] states if the input has granularity up to seconds (if not, granularity is minutes)
2930
* @property {boolean} [required] states if the inputs can be omitted
3031
* @property {number} [min] the timestamp of the minimum date allowed by the input (included)
3132
* @property {number} [max] the timestamp of the maximum date allowed by the input (excluded)
@@ -90,6 +91,7 @@ export class DateTimeInputComponent extends StatefulComponent {
9091
type: 'time',
9192
required: this._required,
9293
value: this._value.time,
94+
step: this._seconds ? 1 : undefined,
9395
onchange: (e) => this._patchValue({ time: e.target.value }),
9496
min: inputsMin ? inputsMin.time : undefined,
9597
max: inputsMax ? inputsMax.time : undefined,
@@ -111,12 +113,14 @@ export class DateTimeInputComponent extends StatefulComponent {
111113
* @private
112114
*/
113115
_updateAttrs(attrs) {
114-
const { value, onChange, required = false, defaults = {}, min = null, max = null } = attrs;
116+
const { value, onChange, seconds, required = false, defaults = {}, min = null, max = null } = attrs;
115117
const { date: defaultDate = '', time: defaultTime = '' } = defaults;
116118

117119
this._value = value;
118120
this._onChange = onChange;
119121

122+
this._seconds = seconds;
123+
120124
this._required = required;
121125

122126
/**

lib/public/components/common/form/inputs/DateTimeInputModel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import {
1515
extractTimestampFromDateTimeInput,
1616
formatTimestampForDateTimeInput,
17-
} from '../../../../utilities/formatting/HTMLInputDateFormat.js';
17+
} from '../../../../utilities/formatting/dateTimeInputFormatters.mjs';
1818
import { Observable } from '/js/src/index.js';
1919

2020
/**
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import { StatefulComponent } from '../../StatefulComponent.js';
14+
import { DateTimeInputComponent } from './DateTimeInputComponent.js';
15+
import { extractTimestampFromDateTimeInput } from '../../../../utilities/formatting/dateTimeInputFormatters.mjs';
16+
import { h } from '/js/src/index.js';
17+
18+
/**
19+
* @typedef TimestampInputComponentAttrs
20+
* @property {function} onChange function called with the new value when it changes
21+
* @property {boolean} [required] states if the inputs can be omitted
22+
* @property {boolean} [seconds=false] states if the input has granularity up to seconds (if not, granularity is minutes)
23+
*/
24+
25+
/**
26+
* Non-controlled component providing user date & time inputs to fill a timestamp
27+
*
28+
* Because the raw data of the inputs can not be represented by a timestamp (for example partial fill of the inputs), this input is not
29+
* controlled and simply notify when its actual value changes
30+
*/
31+
export class TimestampInputComponent extends StatefulComponent {
32+
/**
33+
* Constructor
34+
* @param {object} vnode the component's inital vnode
35+
* @param {TimestampInputComponentAttrs} vnode.attrs the component's attributes
36+
*/
37+
constructor({ attrs }) {
38+
super();
39+
40+
const { seconds = false, onChange } = attrs;
41+
42+
/**
43+
* @type {DateTimeInputRawData}
44+
* @private
45+
*/
46+
this._raw = { date: '', time: '' };
47+
this._value = null;
48+
49+
this._onChange = onChange;
50+
this._seconds = seconds;
51+
}
52+
53+
/**
54+
* Render the component
55+
* @return {vnode} the component
56+
*/
57+
view() {
58+
return h(DateTimeInputComponent, {
59+
value: this._raw,
60+
onChange: this._handleDateTimeChange.bind(this),
61+
seconds: this._seconds,
62+
});
63+
}
64+
65+
/**
66+
* Handle change of date/time input and trigger the onChange
67+
*
68+
* @param {DateTimeInputRawData} raw the date/time input raw data
69+
* @return {void}
70+
* @private
71+
*/
72+
_handleDateTimeChange(raw) {
73+
this._raw = raw;
74+
let value;
75+
try {
76+
value = raw.date && raw.time ? extractTimestampFromDateTimeInput(raw) : null;
77+
} catch (_) {
78+
value = null;
79+
}
80+
81+
if (this._value !== value) {
82+
this._value = value;
83+
this._onChange(this._value);
84+
}
85+
this.notify();
86+
}
87+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import { Observable } from '/js/src/index.js';
14+
15+
/**
16+
* @typedef AliceMagnetsConfiguration the configuration of the ALICE magnets
17+
* @property {string} solenoid the configuration of the solenoid
18+
* @property {string} dipole the configuration of the dipole
19+
*/
20+
21+
/**
22+
* @typedef AliceMagnetsConfigurationSnapshot
23+
* @property {number} timestamp the timestamp of the snapshot
24+
* @property {AliceMagnetsConfiguration} magnetsConfiguration the configuration of the magnet at time of the snapshot
25+
*/
26+
27+
/**
28+
* Model of form to fill snapshots of ALICE magnets configurations
29+
*/
30+
export class AliceMagnetsConfigurationSnapshotsForm extends Observable {
31+
/**
32+
* Constructor
33+
*/
34+
constructor() {
35+
super();
36+
37+
/**
38+
* @type {(AliceMagnetsConfigurationSnapshot|null)[]}
39+
* @private
40+
*/
41+
this._snapshots = [];
42+
}
43+
44+
/**
45+
* Add a new empty magnets snapshot configuration
46+
*
47+
* @return {void}
48+
*/
49+
addSnapshot() {
50+
this._snapshots.push({
51+
timestamp: undefined,
52+
magnetsConfiguration: { solenoid: '', dipole: '' },
53+
});
54+
this.notify();
55+
}
56+
57+
/**
58+
* Drop the snapshot magnets configuration at the given key
59+
*
60+
* @param {number} key the key of the snapshot
61+
* @return {void}
62+
*/
63+
dropSnapshot(key) {
64+
this._snapshots[key] = null;
65+
this.notify();
66+
}
67+
68+
/**
69+
* Returns the list of all magnet configurations snapshots, including the deleted ones (null)
70+
*
71+
* @return {(AliceMagnetsConfigurationSnapshot|null)[]} the magnets configuration snapshots
72+
*/
73+
get allSnapshots() {
74+
return this._snapshots;
75+
}
76+
77+
/**
78+
* Return the list of magnet configurations snapshots
79+
*
80+
* @return {AliceMagnetsConfigurationSnapshot[]} the magnets configuration snapshots
81+
*/
82+
get snapshots() {
83+
return this._snapshots.filter((item) => item !== null);
84+
}
85+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import { h, iconTrash } from '/js/src/index.js';
14+
import { TimestampInputComponent } from '../inputs/TimestampInputComponent.js';
15+
16+
/**
17+
* Return a component to fill ALICE magnets configuration at a given time
18+
*
19+
* @param {AliceMagnetsConfigurationSnapshot} magnetsConfigurationSnapshot the magnets configuration snapshot being edited
20+
* @return {Component} the resulting component
21+
*/
22+
const aliceMagnetsConfigurationSnapshotForm = (magnetsConfigurationSnapshot) => h('.flex-row.items-center.g2', [
23+
h(TimestampInputComponent, {
24+
seconds: true,
25+
onChange: (timestamp) => {
26+
magnetsConfigurationSnapshot.timestamp = timestamp;
27+
},
28+
}),
29+
h('', '-'),
30+
aliceMagnetsConfigurationForm(magnetsConfigurationSnapshot.magnetsConfiguration),
31+
]);
32+
33+
/**
34+
* Return a component to fill ALICE magnets configuration
35+
*
36+
* @param {AliceMagnetsConfiguration} magnetsConfiguration the magnet configuration model
37+
* @return {Component} the magnet configuration form
38+
*/
39+
export const aliceMagnetsConfigurationForm = (magnetsConfiguration) => [
40+
h('', 'Solenoid'),
41+
h(
42+
'input.form-control.w-unset',
43+
{
44+
value: magnetsConfiguration.solenoid,
45+
// eslint-disable-next-line no-return-assign
46+
oninput: (e) => magnetsConfiguration.solenoid = e.target.value,
47+
placeholder: 'e.g. -6kA',
48+
},
49+
),
50+
h('', '-'),
51+
h('', 'Dipole'),
52+
h(
53+
'input.form-control.w-unset',
54+
{
55+
value: magnetsConfiguration.dipole,
56+
// eslint-disable-next-line no-return-assign
57+
oninput: (e) => magnetsConfiguration.dipole = e.target.value,
58+
placeholder: 'e.g. +30kA',
59+
},
60+
),
61+
];
62+
63+
/**
64+
* Component to fill sequential timestamped snapshots of ALICE magnets configuration
65+
*
66+
* @param {AliceMagnetsConfigurationSnapshotsFormModel} configurationSnapshotsFormModel the form model
67+
* @return {Component} the form component
68+
*/
69+
export const aliceMagnetsConfigurationsSnapshotsForm = (configurationSnapshotsFormModel) => {
70+
const entries = Object.entries(configurationSnapshotsFormModel.allSnapshots)
71+
.filter(([, value]) => value !== null);
72+
entries.sort((
73+
[, { timestamp: a }],
74+
[, { timestamp: b }],
75+
) => (a || Infinity) - (b || Infinity));
76+
77+
return [
78+
entries.map(([key, magnetsConfigurationSnapshot]) => h(
79+
`#magnets-${key}.flex-row.items-center.g2`,
80+
{
81+
key,
82+
},
83+
[
84+
aliceMagnetsConfigurationSnapshotForm(magnetsConfigurationSnapshot),
85+
// eslint-disable-next-line no-return-assign
86+
h('.btn.btn-danger', { onclick: () => configurationSnapshotsFormModel.dropSnapshot(Number(key)) }, iconTrash()),
87+
],
88+
)),
89+
h('.flex-row.items-center.g2', [
90+
h(
91+
'#magnets-add.button.btn.btn-primary',
92+
{
93+
onclick: () => configurationSnapshotsFormModel.addSnapshot(),
94+
},
95+
'Add',
96+
),
97+
]),
98+
];
99+
};

lib/public/utilities/formatting/HTMLInputDateFormat.js renamed to lib/public/utilities/formatting/dateTimeInputFormatters.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ export const formatTimestampForDateTimeInput = (timestamp) => {
3535

3636
const hours = pad(date.getHours());
3737
const minutes = pad(date.getMinutes());
38-
const timeExpression = `${hours}:${minutes}`;
38+
const seconds = pad(date.getSeconds());
39+
const timeExpression = `${hours}:${minutes}:${seconds}`;
3940

4041
return { date: dateExpression, time: timeExpression };
4142
};

0 commit comments

Comments
 (0)