aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2024-06-25 12:35:18 +0000
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2024-06-25 12:35:18 +0000
commit8fef8bc3c46650f9bdcb9beffeafdd01ef8fe76f (patch)
treed4364579b95152b9ad42278f01d24d343506e41c
parenta629426a92c0907821ffd20ad991b23c87bc3d66 (diff)
fixes #4320, fixes #1871
Closes #4320 and #1871 Merge request studip/studip!3125
-rw-r--r--app/controllers/consultation/admin.php26
-rw-r--r--app/views/consultation/overview/index.php2
-rw-r--r--app/views/consultation/overview/ungrouped.php2
-rw-r--r--lib/models/ConsultationSlot.php22
-rw-r--r--resources/vue/components/ConsultationCreator.vue32
5 files changed, 50 insertions, 34 deletions
diff --git a/app/controllers/consultation/admin.php b/app/controllers/consultation/admin.php
index 8ee7575..13451d2 100644
--- a/app/controllers/consultation/admin.php
+++ b/app/controllers/consultation/admin.php
@@ -165,13 +165,19 @@ class Consultation_AdminController extends ConsultationController
CSRFProtection::verifyUnsafeRequest();
try {
+ $interval = Request::int('interval');
$start = $this->getDateAndTime('start');
- $end = $this->getDateAndTime('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) {
+ $dow = date('w', $start);
+ }
+
// Determine duration of a slot and pause times
$duration = Request::int('duration');
$pause_time = Request::bool('pause') ? Request::int('pause_time') : null;
@@ -187,8 +193,8 @@ class Consultation_AdminController extends ConsultationController
$slot_count = ConsultationBlock::countSlots(
$start,
$end,
- Request::int('day-of-week'),
- Request::int('interval'),
+ $dow,
+ $interval,
$duration,
$pause_time,
$pause_duration
@@ -202,8 +208,8 @@ class Consultation_AdminController extends ConsultationController
$this->range,
$start,
$end,
- Request::int('day-of-week'),
- Request::int('interval')
+ $dow,
+ $interval
);
$stored = 0;
@@ -872,15 +878,19 @@ class Consultation_AdminController extends ConsultationController
}
}
- private function getDateAndTime($index)
+ private function getDateAndTime(string $index, string $index_time = null)
{
- if (!Request::submitted("{$index}-date") || !Request::submitted("{$index}-time")) {
+ if ($index_time === null) {
+ $index_time = $index;
+ }
+
+ if (!Request::submitted("{$index}-date") || !Request::submitted("{$index_time}-time")) {
throw new Exception("Date with index '{$index}' was not submitted properly");
}
return strtotime(implode(' ', [
Request::get("{$index}-date"),
- Request::get("{$index}-time")
+ Request::get("{$index_time}-time")
]));
}
diff --git a/app/views/consultation/overview/index.php b/app/views/consultation/overview/index.php
index dcad608..ef7f208 100644
--- a/app/views/consultation/overview/index.php
+++ b/app/views/consultation/overview/index.php
@@ -61,7 +61,7 @@
<?= Icon::create('add')->asImg(tooltip2(_('Termin reservieren'))) ?>
</a>
<? else: ?>
- <?= Icon::create('add', Icon::ROLE_INACTIVE)->asImg(tooltip2(_('Dieser Termin ist für Buchungen gesperrt.'))) ?>
+ <?= Icon::create('decline', Icon::ROLE_INACTIVE)->asImg(tooltip2(_('Dieser Termin ist für Buchungen gesperrt.'))) ?>
<? endif; ?>
</td>
</tr>
diff --git a/app/views/consultation/overview/ungrouped.php b/app/views/consultation/overview/ungrouped.php
index 147f588..c9eb519 100644
--- a/app/views/consultation/overview/ungrouped.php
+++ b/app/views/consultation/overview/ungrouped.php
@@ -91,7 +91,7 @@
<?= Icon::create('add')->asImg(tooltip2(_('Termin reservieren'))) ?>
</a>
<? else: ?>
- <?= Icon::create('add', Icon::ROLE_INACTIVE)->asImg(tooltip2(_('Dieser Termin ist für Buchungen gesperrt.'))) ?>
+ <?= Icon::create('decline', Icon::ROLE_INACTIVE)->asImg(tooltip2(_('Dieser Termin ist für Buchungen gesperrt.'))) ?>
<? endif; ?>
</td>
</tr>
diff --git a/lib/models/ConsultationSlot.php b/lib/models/ConsultationSlot.php
index c46e47c..1e935c5 100644
--- a/lib/models/ConsultationSlot.php
+++ b/lib/models/ConsultationSlot.php
@@ -19,6 +19,7 @@
* @property SimpleORMapCollection|ConsultationEvent[] $events has_many ConsultationEvent
* @property ConsultationBlock $block belongs_to ConsultationBlock
* @property ConsultationSlot|null $previous_slot has_one ConsultationSlot
+ * @property ConsultationSlot|null $next_slot has_one ConsultationSlot
* @property-read mixed $has_bookings additional field
* @property-read mixed $is_expired additional field
*/
@@ -48,8 +49,12 @@ class ConsultationSlot extends SimpleORMap
'on_delete' => 'delete',
];
$config['has_one']['previous_slot'] = [
- 'class_name' => ConsultationSlot::class,
- 'foreign_key' => 'previous_slot_id',
+ 'class_name' => ConsultationSlot::class,
+ 'foreign_key' => 'previous_slot_id',
+ ];
+ $config['has_one']['next_slot'] = [
+ 'class_name' => ConsultationSlot::class,
+ 'assoc_func' => 'findOneByPrevious_slot_id',
];
$config['registered_callbacks']['before_create'][] = function (ConsultationSlot $slot) {
@@ -183,14 +188,15 @@ class ConsultationSlot extends SimpleORMap
/**
* Returns whether the slot is bookable for the given user_id
*/
- public function isBookable(?string $user_id = null): bool
+ public function isBookable(): bool
{
- return !$this->isOccupied($user_id)
+ return !$this->isOccupied()
&& !$this->isLocked()
- && !(
- $this->block->consecutive
- && $this->previous_slot
- && !$this->previous_slot->isOccupied()
+ && (
+ !$this->block->consecutive
+ || !$this->block->has_bookings
+ || ($this->previous_slot && $this->previous_slot->isOccupied())
+ || ($this->next_slot && $this->next_slot->isOccupied())
);
}
diff --git a/resources/vue/components/ConsultationCreator.vue b/resources/vue/components/ConsultationCreator.vue
index 375e98a..73c3cb1 100644
--- a/resources/vue/components/ConsultationCreator.vue
+++ b/resources/vue/components/ConsultationCreator.vue
@@ -30,7 +30,7 @@
<label :class="{'col-3': !isSingleDay}">
<span class="required">{{ $gettext('Intervall') }}</span>
- <select required name="interval" v-model="interval">
+ <select required name="interval" v-model.number="interval">
<option v-for="(label, value) in intervals" :key="value" :value="value">
{{ label }}
</option>
@@ -40,9 +40,9 @@
<label class="col-3" v-if="!isSingleDay">
<span class="required">{{ $gettext('Am Wochentag') }}</span>
- <select required name="day-of-week" v-model="dayOfWeek">
- <option v-for="(label, value) in daysOfTheWeek" :value="value" :key="value">
- {{ label }}
+ <select required name="day-of-week" @change="evt => dayOfWeek = parseInt(evt.target.value, 10)">
+ <option v-for="dow in daysOfTheWeek" :value="dow.key" :key="dow.key" :selected="dayOfWeek === dow.key">
+ {{ dow.label }}
</option>
</select>
</label>
@@ -359,15 +359,15 @@ export default {
return STUDIP.CSRF_TOKEN;
},
daysOfTheWeek() {
- return {
- 1: this.$gettext('Montag'),
- 2: this.$gettext('Dienstag'),
- 3: this.$gettext('Mittwoch'),
- 4: this.$gettext('Donnerstag'),
- 5: this.$gettext('Freitag'),
- 6: this.$gettext('Samstag'),
- 0: this.$gettext('Sonntag'),
- };
+ return [
+ {key: 1, label: this.$gettext('Montag')},
+ {key: 2, label: this.$gettext('Dienstag')},
+ {key: 3, label: this.$gettext('Mittwoch')},
+ {key: 4, label: this.$gettext('Donnerstag')},
+ {key: 5, label: this.$gettext('Freitag')},
+ {key: 6, label: this.$gettext('Samstag')},
+ {key: 0, label: this.$gettext('Sonntag')},
+ ];
},
intervals() {
return {
@@ -385,7 +385,7 @@ export default {
return this.rangeType === 'Institute';
},
isSingleDay() {
- return this.interval === '0';
+ return this.interval === 0;
},
needsConfirmation() {
return this.slotCount > this.slotCountThreshold;
@@ -419,7 +419,7 @@ export default {
errors.push(this.$gettext('Die Endzeit liegt vor der Startzeit!'));
}
- if (this.startDate > this.endDate) {
+ if (this.interval > 0 && this.startDate > this.endDate) {
errors.push(this.$gettext('Das Enddatum liegt vor dem Startdatum!'));
}
@@ -457,7 +457,7 @@ export default {
},
watch: {
interval(current) {
- if (current === '0') {
+ if (current === 0) {
this.endDate = new Date(this.startDate);
}
},