Skip to content

Commit e596fe6

Browse files
committed
RotationConfigForm: Improve usablity
* Reorder elements * Rename elements * Change mode appearance
1 parent fdd08a0 commit e596fe6

File tree

2 files changed

+117
-112
lines changed

2 files changed

+117
-112
lines changed

application/forms/RotationConfigForm.php

Lines changed: 90 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,9 @@ protected function assembleModeSelection(): string
602602
'24-7' => $this->translate('24/7')
603603
];
604604

605-
$modeList = new HtmlElement('ul');
605+
$modeList = new HtmlElement('ul', Attributes::create([
606+
'class' => ['rotation-mode', $this->disableModeSelection ? 'disabled' : '']
607+
]));
606608
foreach ($modes as $mode => $label) {
607609
$radio = $this->createElement('input', 'mode', [
608610
'type' => 'radio',
@@ -684,8 +686,14 @@ protected function assembleModeSelection(): string
684686

685687
$this->addHtml(new HtmlElement(
686688
'div',
687-
Attributes::create(['class' => ['rotation-mode', $this->disableModeSelection ? 'disabled' : '']]),
688-
new HtmlElement('h2', null, Text::create($this->translate('Mode'))),
689+
Attributes::create([
690+
'class' => ['control-group']
691+
]),
692+
new HtmlElement(
693+
'div',
694+
Attributes::create(['class' => 'control-label-group']),
695+
Text::create($this->translate('Rotation Mode'))
696+
),
689697
$modeList
690698
));
691699

@@ -704,6 +712,7 @@ protected function assembleTwentyFourSevenOptions(FieldsetElement $options): Dat
704712
$options->addElement('number', 'interval', [
705713
'required' => true,
706714
'label' => $this->translate('Handoff every'),
715+
'description' => $this->translate('Have multiple rotation members take turns after this interval.'),
707716
'step' => 1,
708717
'min' => 1,
709718
'value' => 1,
@@ -796,6 +805,7 @@ protected function assemblePartialDayOptions(FieldsetElement $options): DateTime
796805
$options->addElement('number', 'interval', [
797806
'required' => true,
798807
'label' => $this->translate('Handoff every'),
808+
'description' => $this->translate('Have multiple rotation members take turns after this interval.'),
799809
'step' => 1,
800810
'min' => 1,
801811
'value' => 1,
@@ -912,7 +922,8 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime
912922
'step' => 1,
913923
'min' => 1,
914924
'value' => 1,
915-
'label' => $this->translate('Handoff every')
925+
'label' => $this->translate('Handoff every'),
926+
'description' => $this->translate('Have multiple rotation members take turns after this interval.')
916927
]);
917928

918929
$timeOptions = $this->getTimeOptions();
@@ -1029,17 +1040,9 @@ protected function assemble()
10291040

10301041
$this->addElement('hidden', 'priority', ['ignore' => true]);
10311042

1032-
$mode = $this->assembleModeSelection();
1033-
1034-
$autoSubmittedBy = $this->getRequest()->getHeader('X-Icinga-Autosubmittedby')[0] ?? '';
1035-
if ($autoSubmittedBy === 'mode') {
1036-
$this->clearPopulatedValue('options');
1037-
$this->clearPopulatedValue('first_handoff');
1038-
}
1039-
10401043
$this->addElement('text', 'name', [
10411044
'required' => true,
1042-
'label' => $this->translate('Title'),
1045+
'label' => $this->translate('Rotation Name'),
10431046
'validators' => [
10441047
new CallbackValidator(function ($value, $validator) {
10451048
$rotations = Rotation::on($this->db)
@@ -1051,7 +1054,7 @@ protected function assemble()
10511054
}
10521055

10531056
if ($rotations->first() !== null) {
1054-
$validator->addMessage($this->translate('A rotation with this title already exists'));
1057+
$validator->addMessage($this->translate('A rotation with this name already exists'));
10551058

10561059
return false;
10571060
}
@@ -1061,6 +1064,73 @@ protected function assemble()
10611064
]
10621065
]);
10631066

1067+
$termValidator = function (array $terms) {
1068+
$contactTerms = [];
1069+
$groupTerms = [];
1070+
foreach ($terms as $term) {
1071+
/** @var TermInput\Term $term */
1072+
if (strpos($term->getSearchValue(), ':') === false) {
1073+
// TODO: Auto-correct this to a valid type:id pair, if possible
1074+
$term->setMessage($this->translate('Is not a contact nor a group of contacts'));
1075+
continue;
1076+
}
1077+
1078+
list($type, $id) = explode(':', $term->getSearchValue(), 2);
1079+
if ($type === 'contact') {
1080+
$contactTerms[$id] = $term;
1081+
} elseif ($type === 'group') {
1082+
$groupTerms[$id] = $term;
1083+
}
1084+
}
1085+
1086+
if (! empty($contactTerms)) {
1087+
$contacts = (Contact::on(Database::get()))
1088+
->filter(Filter::equal('id', array_keys($contactTerms)));
1089+
foreach ($contacts as $contact) {
1090+
$contactTerms[$contact->id]
1091+
->setLabel($contact->full_name)
1092+
->setClass('contact');
1093+
}
1094+
}
1095+
1096+
if (! empty($groupTerms)) {
1097+
$groups = (Contactgroup::on(Database::get()))
1098+
->filter(Filter::equal('id', array_keys($groupTerms)));
1099+
foreach ($groups as $group) {
1100+
$groupTerms[$group->id]
1101+
->setLabel($group->name)
1102+
->setClass('group');
1103+
}
1104+
}
1105+
};
1106+
1107+
$members = (new TermInput('members'))
1108+
->setIgnored()
1109+
->setRequired()
1110+
->setOrdered()
1111+
->setReadOnly()
1112+
->setVerticalTermDirection()
1113+
->setLabel($this->translate('Rotation Members'))
1114+
->setSuggestionUrl($this->suggestionUrl->with(['showCompact' => true, '_disableLayout' => 1]))
1115+
->on(TermInput::ON_ENRICH, $termValidator)
1116+
->on(TermInput::ON_ADD, $termValidator)
1117+
->on(TermInput::ON_SAVE, $termValidator)
1118+
->on(TermInput::ON_PASTE, $termValidator);
1119+
$this->addElement($members);
1120+
1121+
// TODO: TermInput is not compatible with the new decorators yet: https://github.com/Icinga/ipl-web/pull/317
1122+
$legacyDecorator = new IcingaFormDecorator();
1123+
$members->setDefaultElementDecorator($legacyDecorator);
1124+
$legacyDecorator->decorate($members);
1125+
1126+
$mode = $this->assembleModeSelection();
1127+
1128+
$autoSubmittedBy = $this->getRequest()->getHeader('X-Icinga-Autosubmittedby')[0] ?? '';
1129+
if ($autoSubmittedBy === 'mode') {
1130+
$this->clearPopulatedValue('options');
1131+
$this->clearPopulatedValue('first_handoff');
1132+
}
1133+
10641134
$this->addElement('fieldset', 'options');
10651135
/** @var FieldsetElement $options */
10661136
$options = $this->getElement('options');
@@ -1098,7 +1168,7 @@ protected function assemble()
10981168
'aria-describedby' => 'first-handoff-description',
10991169
'min' => $earliestHandoff !== null ? $earliestHandoff->format('Y-m-d') : null,
11001170
'max' => $latestHandoff->format('Y-m-d'),
1101-
'label' => $this->translate('First Handoff'),
1171+
'label' => $this->translate('Rotation Start'),
11021172
'value' => $firstHandoffDefault,
11031173
'validators' => [
11041174
new CallbackValidator(
@@ -1110,14 +1180,14 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11101180
);
11111181
if ($earliestHandoff !== null && $chosenHandoff < $earliestHandoff) {
11121182
$validator->addMessage(sprintf(
1113-
$this->translate('The first handoff can only happen after %s'),
1183+
$this->translate('The rotation can only start after %s'),
11141184
$earliestHandoff->format('Y-m-d') // TODO: Use intl here
11151185
));
11161186

11171187
return false;
11181188
} elseif ($chosenHandoff > $latestHandoff) {
11191189
$validator->addMessage(sprintf(
1120-
$this->translate('The first handoff can only happen before %s'),
1190+
$this->translate('The rotation can only start before %s'),
11211191
$latestHandoff->format('Y-m-d') // TODO: Use intl here
11221192
));
11231193

@@ -1142,10 +1212,10 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11421212

11431213
$actualFirstHandoff = $ruleGenerator->current()[0]->getStartDate();
11441214
if ($actualFirstHandoff < new DateTime()) {
1145-
return $this->translate('The first handoff will happen immediately');
1215+
return $this->translate('The rotation will start immediately');
11461216
} else {
11471217
return sprintf(
1148-
$this->translate('The first handoff will happen on %s'),
1218+
$this->translate('The rotation will start on %s'),
11491219
(new \IntlDateFormatter(
11501220
\Locale::getDefault(),
11511221
\IntlDateFormatter::MEDIUM,
@@ -1157,65 +1227,6 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
11571227
));
11581228
}
11591229

1160-
$termValidator = function (array $terms) {
1161-
$contactTerms = [];
1162-
$groupTerms = [];
1163-
foreach ($terms as $term) {
1164-
/** @var TermInput\Term $term */
1165-
if (strpos($term->getSearchValue(), ':') === false) {
1166-
// TODO: Auto-correct this to a valid type:id pair, if possible
1167-
$term->setMessage($this->translate('Is not a contact nor a group of contacts'));
1168-
continue;
1169-
}
1170-
1171-
list($type, $id) = explode(':', $term->getSearchValue(), 2);
1172-
if ($type === 'contact') {
1173-
$contactTerms[$id] = $term;
1174-
} elseif ($type === 'group') {
1175-
$groupTerms[$id] = $term;
1176-
}
1177-
}
1178-
1179-
if (! empty($contactTerms)) {
1180-
$contacts = (Contact::on(Database::get()))
1181-
->filter(Filter::equal('id', array_keys($contactTerms)));
1182-
foreach ($contacts as $contact) {
1183-
$contactTerms[$contact->id]
1184-
->setLabel($contact->full_name)
1185-
->setClass('contact');
1186-
}
1187-
}
1188-
1189-
if (! empty($groupTerms)) {
1190-
$groups = (Contactgroup::on(Database::get()))
1191-
->filter(Filter::equal('id', array_keys($groupTerms)));
1192-
foreach ($groups as $group) {
1193-
$groupTerms[$group->id]
1194-
->setLabel($group->name)
1195-
->setClass('group');
1196-
}
1197-
}
1198-
};
1199-
1200-
$members = (new TermInput('members'))
1201-
->setIgnored()
1202-
->setRequired()
1203-
->setOrdered()
1204-
->setReadOnly()
1205-
->setVerticalTermDirection()
1206-
->setLabel($this->translate('Members'))
1207-
->setSuggestionUrl($this->suggestionUrl->with(['showCompact' => true, '_disableLayout' => 1]))
1208-
->on(TermInput::ON_ENRICH, $termValidator)
1209-
->on(TermInput::ON_ADD, $termValidator)
1210-
->on(TermInput::ON_SAVE, $termValidator)
1211-
->on(TermInput::ON_PASTE, $termValidator);
1212-
$this->addElement($members);
1213-
1214-
// TODO: TermInput is not compatible with the new decorators yet: https://github.com/Icinga/ipl-web/pull/317
1215-
$legacyDecorator = new IcingaFormDecorator();
1216-
$members->setDefaultElementDecorator($legacyDecorator);
1217-
$legacyDecorator->decorate($members);
1218-
12191230
$this->addElement('submit', 'submit', [
12201231
'label' => $this->getSubmitLabel()
12211232
]);
@@ -1243,7 +1254,7 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando
12431254
$this->getElement('submit')->prependWrapper((new HtmlDocument())->setHtmlContent(...$removeButtons));
12441255
}
12451256

1246-
$this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId()));
1257+
$this->addCsrfCounterMeasure(Session::getSession()->getId());
12471258
}
12481259

12491260
/**

public/css/form.less

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,51 +26,44 @@
2626
}
2727

2828
.rotation-config {
29+
ul {
30+
list-style: none;
31+
margin: 0 1em 0 0;
32+
padding: 0;
33+
}
34+
2935
.rotation-mode {
30-
width: 50em;
31-
padding: .5em 1em;
32-
margin: 0 auto;
36+
display: flex;
37+
justify-content: space-between;
38+
flex: 1 1 auto;
3339

34-
h2 {
35-
margin: 0;
40+
li {
41+
flex: 1 1 auto;
42+
width: 0;
43+
44+
&:not(:last-child) {
45+
margin-right: 1em;
46+
}
3647
}
3748

38-
ul {
49+
label {
3950
display: flex;
40-
justify-content: space-between;
41-
42-
list-style: none;
43-
margin: 0;
44-
padding: 0;
45-
46-
li {
47-
flex: 1 1 auto;
48-
width: 0;
51+
flex-direction: column;
52+
width: auto;
4953

50-
&:not(:last-child) {
51-
margin-right: 1em;
52-
}
54+
input {
55+
display: none;
5356
}
5457

55-
label {
56-
display: flex;
57-
flex-direction: column;
58-
width: auto;
59-
60-
input {
61-
display: none;
62-
}
63-
64-
.mode-img {
65-
width: 8em;
66-
margin-bottom: .5em;
67-
outline: 3px solid @icinga-blue;
68-
}
58+
.mode-img {
59+
width: 8em;
60+
margin-bottom: .5em;
61+
outline: 3px solid @icinga-blue;
6962
}
7063
}
7164
}
7265

73-
.control-group {
66+
.control-group:not(:has(.rotation-mode)) {
7467
align-items: baseline;
7568
}
7669

@@ -122,6 +115,7 @@
122115
}
123116

124117
.rotation-mode {
118+
padding: .75em;
125119
border: 1px solid @gray-light;
126120
.rounded-corners();
127121

0 commit comments

Comments
 (0)