diff --git a/app/controllers/consultation/admin.php b/app/controllers/consultation/admin.php index 86993497b547175509fe00d9f1c730287b0ad968..21507edb0418176d60288f835322003f09b43e97 100644 --- a/app/controllers/consultation/admin.php +++ b/app/controllers/consultation/admin.php @@ -199,22 +199,27 @@ class Consultation_AdminController extends ConsultationController try { $interval = Request::int('interval'); $start = $this->getDateAndTime('start'); - $end = $this->getDateAndTime($interval === 0 ? 'start' : 'end', 'end'); + $end = $this->getDateAndTime($interval <= 0 ? 'start' : 'end', 'end'); if (date('Hi', $end) <= date('Hi', $start)) { throw new InvalidArgumentException(_('Die Endzeit liegt vor der Startzeit!')); } $dow = Request::int('day-of-week'); - if ($interval === 0) { + if ($interval <= 0) { $dow = date('w', $start); } // Determine duration of a slot and pause times $duration = Request::int('duration'); + + if ($duration === null && $interval === -1) { + $duration = (int) (($end - $start) / 60); + } + $pause_time = Request::bool('pause') ? Request::int('pause_time') : null; $pause_duration = Request::bool('pause') ? Request::int('pause_duration') : null; - if ($pause_time && $pause_time < $duration) { + if ($interval >= 0 && $pause_time && $pause_time < $duration) { throw new InvalidArgumentException(_('Die definierte Zeit bis zur Pause ist kleiner als die Dauer eines Termins.')); } @@ -231,6 +236,7 @@ class Consultation_AdminController extends ConsultationController $pause_time, $pause_duration ); + if ($slot_count >= self::SLOT_COUNT_THRESHOLD && !Request::int('confirmed')) { $this->flash['confirm-many'] = $slot_count; throw new Exception('', -1); @@ -257,7 +263,7 @@ class Consultation_AdminController extends ConsultationController $block->lock_time = Request::int('lock_time'); $block->consecutive = Request::bool('consecutive', false); - $slots = $block->createSlots(Request::int('duration'), $pause_time, $pause_duration); + $slots = $block->createSlots($duration, $pause_time, $pause_duration); if (count($slots) === 0) { continue; } diff --git a/app/views/consultation/admin/edit.php b/app/views/consultation/admin/edit.php index f9b416a982c2bb38ac7074e8a571a1f5d4b938cb..1fd0cf1af72c216cb094063ae788ad256c8e2a31 100644 --- a/app/views/consultation/admin/edit.php +++ b/app/views/consultation/admin/edit.php @@ -45,7 +45,7 @@ <? endif; ?> <label> - <?= _('Maximale Teilnehmerzahl') ?> + <?= _('Maximale Teilnehmendenzahl') ?> <?= tooltipIcon(_('Falls Sie mehrere Personen zulassen wollen (wie z.B. zu einer Klausureinsicht), so geben Sie hier die maximale Anzahl an Personen an, die sich anmelden dürfen.')) ?> <input required type="text" name="size" id="size" min="1" max="50" value="<?= $block->size ?>"> diff --git a/lib/models/ConsultationBlock.php b/lib/models/ConsultationBlock.php index cde8f7707c9c401862630f8555d2de93ed827405..3e5de6e10e8e759de96321e4991fa9fb92515120 100644 --- a/lib/models/ConsultationBlock.php +++ b/lib/models/ConsultationBlock.php @@ -212,7 +212,7 @@ class ConsultationBlock extends SimpleORMap implements PrivacyObject ); } - if (!$interval) { + if ($interval <= 0) { break; } diff --git a/resources/assets/stylesheets/scss/forms.scss b/resources/assets/stylesheets/scss/forms.scss index 2aa105fad4c8eeb16c521a418facda22f9d6ea36..d847dbe128e2238acc723d082e1da4053e5eb5d5 100644 --- a/resources/assets/stylesheets/scss/forms.scss +++ b/resources/assets/stylesheets/scss/forms.scss @@ -637,7 +637,10 @@ form.inline { > :not(footer[data-dialog-button]) { flex: 0; - margin-bottom: auto; + + &:last-of-type { + margin-bottom: auto; + } } footer[data-dialog-button] { diff --git a/resources/vue/components/ConsultationCreator.vue b/resources/vue/components/ConsultationCreator.vue index 163e23883c279058b5cdfdd43af084b4bc2cb318..56f972650677d7fa8868136230ac7fdccc8669ed 100644 --- a/resources/vue/components/ConsultationCreator.vue +++ b/resources/vue/components/ConsultationCreator.vue @@ -31,8 +31,8 @@ <label :class="{'col-3': !isSingleDay}"> <span class="required">{{ $gettext('Intervall') }}</span> <select required name="interval" v-model.number="interval"> - <option v-for="(label, value) in intervals" :key="value" :value="value"> - {{ label }} + <option v-for="item in intervals" :key="item.key" :value="item.key"> + {{ item.label }} </option> </select> </label> @@ -71,7 +71,7 @@ ></Datepicker> </label> - <label for="start-time" class="col-3"> + <label for="start-time" :class="{'col-3': !isSingleDate}"> <span class="required">{{ $gettext('Von') }}</span> <Timepicker name="start-time" @@ -80,7 +80,7 @@ ></Timepicker> </label> - <label for="ende_hour" class="col-3"> + <label for="ende_hour" :class="{'col-3': !isSingleDate}"> <span class="required">{{ $gettext('Bis') }}</span> <Timepicker name="end-time" @@ -89,26 +89,26 @@ ></Timepicker> </label> - <label class="col-3"> + <label class="col-3" v-if="!isSingleDate"> <span class="required">{{ $gettext('Dauer eines Termins in Minuten') }}</span> <input required type="number" name="duration" min="1" v-model="duration"> </label> - <label class="col-3"> - {{ $gettext('Maximale Teilnehmerzahl') }} + <label :class="{'col-3': !isSingleDate}"> + {{ $gettext('Maximale Teilnehmendenzahl') }} <StudipTooltipIcon :text="$gettext('Falls Sie mehrere Personen zulassen wollen (wie z.B. zu einer Klausureinsicht), so geben Sie hier die maximale Anzahl an Personen an, die sich anmelden dürfen.')"></StudipTooltipIcon> <input required type="text" name="size" id="size" min="1" max="50" v-model="size"> </label> - <label> + <label v-if="!isSingleDate"> <input type="checkbox" name="pause" value="1" v-model="pause"> {{ $gettext('Pausen zwischen den Terminen einfügen?') }} </label> - <label class="col-3" v-if="pause"> + <label class="col-3" v-if="!isSingleDate && pause"> {{ $gettext('Eine Pause nach wie vielen Minuten einfügen?') }} <input type="number" name="pause_time" min="1" v-model="pauseTime"> @@ -123,16 +123,16 @@ <label> <input type="checkbox" name="lock" value="1" v-model="lock"> - {{ $gettext('Termine für Buchungen sperren?') }} + {{ isSingleDate ? $gettext('Termin für Buchungen sperren?') : $gettext('Termin für Buchungen sperren?') }} </label> <label v-if="lock"> - {{ $gettext('Wieviele Stunden vor Beginn des Blocks sollen die Termine für Buchungen gesperrt werden?') }} + {{ isSingleDate ? $gettext('Wieviele Stunden vor Beginn des Blocks soll der Termin für Buchungen gesperrt werden?') : $gettext('Wieviele Stunden vor Beginn des Blocks sollen die Termine für Buchungen gesperrt werden?') }} <input type="number" name="lock_time" min="1" v-model="lockTime"> </label> - <label> + <label v-if="!isSingleDate"> <input type="checkbox" name="consecutive" value="1" v-model="consecutive"> {{ $gettext('Termine innerhalb der Blöcke nur fortlaufend vergeben') }} @@ -204,14 +204,14 @@ <legend>{{ $gettext('Weitere Einstellungen') }}</legend> <label> - {{ $gettext('Information zu den Terminen in diesem Block') }} + {{ isSingleDate ? $gettext('Information zu diesem Termin') : $gettext('Information zu den Terminen in diesem Block') }} <textarea name="note" v-model="note"></textarea> </label> <label> <input type="checkbox" name="calender-events" value="1" v-model="calendarEvents"> - {{ $gettext('Die freien Termine auch im Kalender markieren') }} + {{ isSingleDate ? $gettext('Den freien Termin auch im Kalender markieren') : $gettext('Die freien Termine auch im Kalender markieren') }} </label> <label v-if="isCourse"> @@ -370,13 +370,14 @@ export default { ]; }, intervals() { - return { - 0: this.$gettext('einmalig (ohne Wiederholung)'), - 1: this.$gettext('wöchentlich'), - 2: this.$gettext('zweiwöchentlich'), - 3: this.$gettext('dreiwöchentlich'), - 4: this.$gettext('monatlich'), - }; + return [ + {key: -1, label: this.$gettext('Einzeltermin')}, + {key: 0, label: this.$gettext('einmalig (ohne Wiederholung)')}, + {key: 1, label: this.$gettext('wöchentlich')}, + {key: 2, label: this.$gettext('zweiwöchentlich')}, + {key: 3, label: this.$gettext('dreiwöchentlich')}, + {key: 4, label: this.$gettext('monatlich')}, + ]; }, isCourse() { return this.rangeType === 'Course'; @@ -385,7 +386,11 @@ export default { return this.rangeType === 'Institute'; }, isSingleDay() { - return this.interval === 0; + return this.interval === 0 + || this.interval === -1; + }, + isSingleDate() { + return this.interval === -1; }, needsConfirmation() { return this.slotCount > this.slotCountThreshold; @@ -438,11 +443,11 @@ export default { errors.push(this.$gettext('Die Endzeit liegt vor der Startzeit!')); } - if (this.interval > 0 && this.compareDates(this.startDate, this.endDate, '>')) { + if (!this.isSingleDay && this.compareDates(this.startDate, this.endDate, '>')) { errors.push(this.$gettext('Das Enddatum liegt vor dem Startdatum!')); } - if (this.pauseTime && this.pauseTime < this.duration) { + if (!this.isSingleDate && this.pauseTime && this.pauseTime < this.duration) { errors.push(this.$gettext('Die definierte Zeit bis zur Pause ist kleiner als die Dauer eines Termins.')); } @@ -475,10 +480,20 @@ export default { } }, watch: { - interval(current) { - if (current === 0) { + interval(current, previous) { + if (current === 0 || current === -1) { this.endDate = new Date(this.startDate); } + + if (current === -1) { + const start = this.combineDateAndTime(this.startDate, this.startTime); + const end = this.combineDateAndTime(this.endDate, this.endTime); + this.duration = Math.floor((end - start) / 1000 / 60); + } + + if (current !== -1 && previous === -1) { + this.duration = 15; + } }, recalculationProperty: { handler() { diff --git a/resources/vue/components/Timepicker.vue b/resources/vue/components/Timepicker.vue index e0b0febfe5cee9bdf3b81febbb3b5bf4e23fe4e0..d3a1ec6b9cc28dffdb011ceaf28b781942b5074a 100644 --- a/resources/vue/components/Timepicker.vue +++ b/resources/vue/components/Timepicker.vue @@ -35,3 +35,8 @@ export default { } } </script> +<style scoped> +input[type="time"]::-webkit-calendar-picker-indicator { + display: none; +} +</style>