- wording angepasst lehreinheit -> lvteil

- Kollisionscheck an/aus wird angezeigt
- Shortcut für Ignore-Collision (F9) hinzugefuegt
- coursepicker:
 - design angepasst
 - favorisieren ermoeglicht
 - phrases
- bei lv immer alle raeume/lektoren/gruppen anzeigen unabhaengig vom filter
This commit is contained in:
ma0048
2026-07-15 14:58:41 +02:00
parent a413ae17b8
commit 81c16a3bf2
10 changed files with 247 additions and 62 deletions
@@ -14,6 +14,7 @@ class Config extends FHCAPI_Controller
'get' => ['admin:r', 'assistenz:r'],
'getHeader' => ['admin:r', 'assistenz:r'],
'set' => ['admin:r', 'assistenz:r'],
'updateCollision' => ['admin:r', 'assistenz:r'],
]);
// Load Phrases
@@ -23,12 +24,12 @@ class Config extends FHCAPI_Controller
$this->_ci = &get_instance();
$this->_ci->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel');
$this->_ci->load->model('system/Variable_model', 'VariableModel');
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
}
public function get()
{
$this->_ci->load->model('system/Variable_model', 'VariableModel');
$config = [];
$result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre']);
@@ -87,8 +88,6 @@ class Config extends FHCAPI_Controller
public function set()
{
$this->_ci->load->model('system/Variable_model', 'VariableModel');
$this->_ci->VariableModel->setVariable(
getAuthUID(),
'ignore_kollision',
@@ -112,5 +111,14 @@ class Config extends FHCAPI_Controller
$this->terminateWithSuccess();
}
public function updateCollision()
{
$original_ignore = $this->_ci->variablelib->getVar('ignore_kollision');
$this->_ci->VariableModel->setVariable(
getAuthUID(),
'ignore_kollision',
$original_ignore === 'true' ? 'false' : 'true'
);
}
}
@@ -211,7 +211,7 @@ class Coursepicker extends FHCAPI_Controller
'studiensemester_kurzbz' => $row->studiensemester_kurzbz,
'fachbereich_kurzbz' => isset($row->fachbereich_kurzbz) ? $row->fachbereich_kurzbz : null,
'lektoren' => array(),
'lehreinheit_id' => array(),
'lehreinheit_id' => $lehreinheit_id,
'lvnr' => array(),
'lehrfach' => array(),
'lehrform' => array(),
@@ -235,7 +235,6 @@ class Coursepicker extends FHCAPI_Controller
'name' => $row->vorname . ' ' . $row->nachname,
);
$group->lehreinheit_id[] = $row->lehreinheit_id;
$group->lvnr[] = $row->lvnr;
$group->lehrfach[] = $row->lehrfach;
$group->lehrform[] = $row->lehrform;
+69 -21
View File
@@ -344,43 +344,91 @@ class KalenderLib
$this->_getBasePlan($start_date, $end_date);
if (!is_null($ort))
$this->_ci->KalenderModel->db->where_in('tbl_kalender_ort.ort_kurzbz', $ort);
{
$ort_array = (array) $ort;
$escaped_orte = array();
foreach ($ort_array as $ort)
{
$escaped_orte[] = $this->_ci->KalenderModel->db->escape($ort);
}
$in_list = '(' . implode(',', $escaped_orte) . ')';
$this->_ci->KalenderModel->db->where(
"(EXISTS (
SELECT 1
FROM lehre.tbl_kalender_ort filter_ort
WHERE filter_ort.kalender_id = tbl_kalender.kalender_id
AND filter_ort.ort_kurzbz IN $in_list
))"
);
}
if (!is_null($uids))
{
$this->_ci->KalenderModel->db->group_start();
$this->_ci->KalenderModel->db->where_in('tbl_lehreinheitmitarbeiter.mitarbeiter_uid', $uids);
$this->_ci->KalenderModel->db->or_where_in('orginasator.uid', $uids);
$this->_ci->KalenderModel->db->or_where_in('teilnehmer.uid', $uids);
$this->_ci->KalenderModel->db->group_end();
$uid_array = (array) $uids;
$db = $this->_ci->KalenderModel->db;
$escaped_uids = array();
foreach ($uid_array as $uid)
$escaped_uids[] = $db->escape($uid);
$in_list = '(' . implode(',', $escaped_uids) . ')';
$this->_ci->KalenderModel->db->where(
"(EXISTS (
SELECT 1
FROM lehre.tbl_lehreinheitmitarbeiter filter_lem
WHERE filter_lem.lehreinheit_id = tbl_lehreinheit.lehreinheit_id
AND filter_lem.mitarbeiter_uid IN $in_list
)
OR EXISTS (
SELECT 1
FROM lehre.tbl_kalender_event_teilnehmer filter_org
WHERE filter_org.kalender_id = tbl_kalender.kalender_id
AND filter_org.rolle_kurzbz = 'organisator'
AND filter_org.uid IN $in_list
)
OR EXISTS (
SELECT 1
FROM lehre.tbl_kalender_event_teilnehmer filter_teil
WHERE filter_teil.kalender_id = tbl_kalender.kalender_id
AND filter_teil.rolle_kurzbz = 'teilnehmer'
AND filter_teil.uid IN $in_list
))"
);
}
if (!is_null($studiengaenge))
{
$db = $this->_ci->KalenderModel->db;
$or_conditions = array();
$this->_ci->KalenderModel->db->group_start();
$first = true;
foreach ($studiengaenge as $studiengang)
{
if ($first)
$this->_ci->KalenderModel->db->group_start();
else
{
$this->_ci->KalenderModel->db->or_group_start();
}
$first = false;
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang['studiengang_kz']);
$conditions = array();
$conditions[] = 'filter_lv.studiengang_kz = ' . $db->escape($studiengang['studiengang_kz']);
if (isset($studiengang['semester']))
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.semester', $studiengang['semester']);
$conditions[] = 'filter_lv.semester = ' . $db->escape($studiengang['semester']);
if (isset($studiengang['orgform_kurzbz']))
$this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.orgform_kurzbz', $studiengang['orgform_kurzbz']);
$conditions[] = 'filter_lv.orgform_kurzbz = ' . $db->escape($studiengang['orgform_kurzbz']);
$this->_ci->KalenderModel->db->group_end();
$or_conditions[] = '(' . implode(' AND ', $conditions) . ')';
}
$this->_ci->KalenderModel->db->group_end();
$or_block = implode(' OR ', $or_conditions);
$this->_ci->KalenderModel->db->where(
"(EXISTS (
SELECT 1
FROM lehre.tbl_kalender_lehreinheit filter_kl
JOIN lehre.tbl_lehreinheit filter_le ON filter_le.lehreinheit_id = filter_kl.lehreinheit_id
JOIN lehre.tbl_lehrveranstaltung filter_lv ON filter_lv.lehrveranstaltung_id = filter_le.lehrveranstaltung_id
WHERE filter_kl.kalender_id = tbl_kalender.kalender_id
AND ($or_block)
))"
);
}
$this->_ci->KalenderModel->db->where('NOT EXISTS (
+6
View File
@@ -34,5 +34,11 @@ export default {
url: 'api/frontend/v1/tempus/config/set',
params
};
},
updateCollision () {
return {
method: 'post',
url: 'api/frontend/v1/tempus/config/updateCollision'
};
}
};
+21 -17
View File
@@ -79,26 +79,30 @@ export default {
this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved'));
})
.catch(this.$fhcAlert.handleSystemErrors);
},
reload()
{
this.$api
.call(this.endpoints.get())
.then(res => {
Object.keys(res.data).forEach(key => {
const binding = { ...res.data[key] };
delete binding.value;
delete binding.options;
const options = res.data[key].options;
this.tempValues[key] = res.data[key].value;
this.setup[key] = {
binding,
options
};
});
this.$emit('update:modelValue', { ...this.tempValues });
})
.catch(this.$fhcAlert.handleSystemErrors);
}
},
created() {
this.$api
.call(this.endpoints.get())
.then(res => {
Object.keys(res.data).forEach(key => {
const binding = { ...res.data[key] };
delete binding.value;
delete binding.options;
const options = res.data[key].options;
this.tempValues[key] = res.data[key].value;
this.setup[key] = {
binding,
options
};
});
this.$emit('update:modelValue', { ...this.tempValues });
})
.catch(this.$fhcAlert.handleSystemErrors);
this.reload();
},
template: /* html */`
<fhc-form class="stv-config" ref="form" @submit.prevent="update">
+6
View File
@@ -271,6 +271,12 @@ export default {
></i>
<span>Reservierung</span>
</div>
<div class="d-flex align-items-center gap-2">
<i :class="appConfig.ignore_kollision ? 'fa-solid fa-triangle-exclamation text-danger' : 'fa-solid fa-circle-check text-success'"></i>
<span class="form-check-label">
{{ appConfig.ignore_kollision ? 'Kollisionscheck aus' : 'Kollisionscheck an' }}
</span>
</div>
</div>
</template>
</fhc-calendar>`
+50 -18
View File
@@ -25,7 +25,8 @@ export default {
searchparam: '',
allCourses: [],
sortBy: null,
multiWeekIds: new Set()
multiWeekIds: new Set(),
favorites: new Set()
}
},
computed: {
@@ -50,7 +51,7 @@ export default {
{
let dir = this.sortBy === 'lektor-asc' ? 1 : -1;
return result.sort((a, b) => {
result = result.sort((a, b) => {
let an = a.lektoren?.[0]?.kurzbz ?? '';
let bn = b.lektoren?.[0]?.kurzbz ?? '';
return an.localeCompare(bn) * dir;
@@ -58,22 +59,26 @@ export default {
}
else if (this.sortBy === 'kw-asc')
{
return result.sort((a, b) => (a.start_kw ?? 0) - (b.start_kw ?? 0));
result = result.sort((a, b) => (a.start_kw ?? 0) - (b.start_kw ?? 0));
}
else if (this.sortBy === 'kw-desc')
{
return result.sort((a, b) => (b.start_kw ?? 0) - (a.start_kw ?? 0));
result = result.sort((a, b) => (b.start_kw ?? 0) - (a.start_kw ?? 0));
}
else if (this.sortBy === 'stunden-asc')
{
return result.sort((a, b) => (a.offenestunden ?? 0) - (b.offenestunden ?? 0));
result = result.sort((a, b) => (a.offenestunden ?? 0) - (b.offenestunden ?? 0));
}
else if (this.sortBy === 'stunden-desc')
{
return result.sort((a, b) => (b.offenestunden ?? 0) - (a.offenestunden ?? 0));
result = result.sort((a, b) => (b.offenestunden ?? 0) - (a.offenestunden ?? 0));
}
return result;
return [...result].sort((a, b) => {
let ap = this.isPinned(a) ? 1 : 0;
let bp = this.isPinned(b) ? 1 : 0;
return bp - ap;
});
}
},
watch: {
@@ -124,7 +129,7 @@ export default {
showname: `${e.lehrfach} ${e.lehrform}`,
orig: {
type: 'lehreinheit',
lehreinheit_id: e.lehreinheit_id[0],
lehreinheit_id: e.lehreinheit_id,
blockung: e.stundenblockung,
entry: e,
}
@@ -175,7 +180,7 @@ export default {
},
toggleMultiWeek(course)
{
let id = course.lehreinheit_id[0];
let id = course.lehreinheit_id;
let weekIds = new Set(this.multiWeekIds);
if (weekIds.has(id))
weekIds.delete(id);
@@ -186,8 +191,27 @@ export default {
},
isMultiWeek(course)
{
return this.multiWeekIds.has(course.lehreinheit_id[0]);
return this.multiWeekIds.has(course.lehreinheit_id);
},
togglePin(course)
{
let id = course.lehreinheit_id;
let favs = new Set(this.favorites);
if (favs.has(id))
favs.delete(id);
else
favs.add(id);
this.favorites = favs;
localStorage.setItem('tempus_coursepicker_favs', JSON.stringify([...favs]));
},
isPinned(course)
{
return this.favorites.has(course.lehreinheit_id);
},
},
mounted() {
this.favorites = new Set(JSON.parse(localStorage.getItem('tempus_coursepicker_favs') || '[]'));
},
template: `
<div class="course-picker d-flex flex-column h-100">
@@ -229,8 +253,8 @@ export default {
</div>
</div>
<div v-if="studiengaenge.length <= 0" class="d-flex flex-column align-items-center justify-content-center text-center text-muted py-5 px-3 h-100">
<span class="small fw-semibold mb-1">Keine Lehreinheiten</span>
<span class="small">Wähle einen Studiengang, um Lehreinheiten anzuzeigen.</span>
<span class="small fw-semibold mb-1">{{$p.t('lehre', 'cptitleempty')}}</span>
<span class="small">{{$p.t('lehre', 'cpempty')}}</span>
</div>
<div v-else class="overflow-auto px-2 pb-2 flex-grow-1">
<div
@@ -238,13 +262,20 @@ export default {
:key="course.lehreinheit_id"
style="cursor:grab"
:style="courseStyle(course)"
class="course-picker-row"
class="course-picker-row rounded-3 mb-2 p-2"
:class="{ 'course-picker-row-pinned': isPinned(course) }"
v-draggable:move.noimage="dragLehreinheitCollection(course)"
tabindex="0"
>
<div class="d-flex gap-1">
<div class="d-flex gap-1 align-items-start">
<span class="fw-semibold small w-50" v-tooltip="course.lehrfach_bez">{{ course.lehrfach }} {{ course.lehrform }}</span>
<span class="fw-semibold small w-50" v-tooltip="course.raumtypalternativ">{{ course.raumtyp }}</span>
<i
class="fa fa-thumbtack"
:class="isPinned(course) ? 'text-primary' : 'text-muted'"
style="cursor:pointer"
@click.stop="togglePin(course)"
></i>
<i
class="fa fa-calendar-week"
:class="isMultiWeek(course) ? 'text-primary' : 'text-muted'"
@@ -256,14 +287,15 @@ export default {
<!--TODO(david) entfernen, dient nur für das mappen mit der lvverwaltung-->
<div class="d-flex gap-1">
<span class="small w-50" v-tooltip="course.lehreinheit_id">{{ course.lehreinheit_id[0] }} </span>
<span class="small w-50" v-tooltip="course.lehreinheit_id">{{ course.lehreinheit_id }} </span>
</div>
<div class="d-flex gap-1 text-muted">
<div class="w-50 d-flex flex-column" v-tooltip="course.anmerkung">
<div class="w-50 d-flex flex-column text-truncate" v-tooltip="course.anmerkung">
<span
v-for="verband in course.lehrverband"
:key="verband">
:key="verband"
class="text-truncate">
{{ verband }}
</span>
</div>
@@ -286,7 +318,7 @@ export default {
{{ lektor.kurzbz }}
</span>
<span v-if="course.lektoren.length > 3" class="text-muted fst-italic">
+{{ course.lektoren.length - 3 }} weitere...
+{{ course.lektoren.length - 3 }} {{$p.t('lehre', 'cpmore')}}
</span>
</div>
<span class="w-50 align-self-start">WR: {{ course.wochenrythmus }} Bl: {{ course.stundenblockung }}</span>
+10
View File
@@ -364,6 +364,16 @@ export default {
if (newDate && luxon.DateTime.isDateTime(newDate) && newDate.isValid)
this.calendarDate = newDate.toISODate();
},
updateCollision()
{
this.$api.call(ApiTempusConfig.updateCollision())
.then(() => {
this.$refs.config.reload();
this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved'));
})
.catch(this.$fhcAlert.handleSystemErrors);
},
toggleStatus(selected) {
if (!selected || selected.length === 0) {
this.visibleStatus = ['all'];
+12
View File
@@ -6,6 +6,18 @@ export function getTempusShortcuts(self) {
skipWhenTyping: false,
handler: () => self.focusSearchbar()
},
{
key: 'f',
ctrl: true,
skipWhenTyping: false,
handler: () => self.focusSearchbar()
},
{
key: 'F9',
ctrl: false,
skipWhenTyping: false,
handler: () => self.updateCollision('month')
},
{
key: '1',
ctrl: true,
+60
View File
@@ -3743,6 +3743,66 @@ $phrases = array(
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'cpempty',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Wähle einen Studiengang aus, um den LV-Teil anzuzeigen.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Select a degree program to display the teaching unit.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'cptitleempty',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kein LV-Teil',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No teaching unit',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'cpmore',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'weitere...',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'more...',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',