course picker

- kurse vom lektor koennen angezeigt werden
- searchbar button hinzuefuegt um kurse vom lektor im course picker zu laden
This commit is contained in:
ma0048
2026-08-12 11:10:07 +02:00
parent a912fcf019
commit 33b50bf479
6 changed files with 168 additions and 164 deletions
@@ -5,11 +5,13 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
class Coursepicker extends FHCAPI_Controller
{
private $_ci;
const ALLOWED_COURSE_FILTER = ['uid', 'stg'];
public function __construct()
{
parent::__construct([
'search' => self::PERM_LOGGED,
'getByStg' => self::PERM_LOGGED
'getCourses' => self::PERM_LOGGED,
]);
$this->_ci = &get_instance();
@@ -22,88 +24,40 @@ class Coursepicker extends FHCAPI_Controller
$this->loadPhrases(['ui']);
}
public function search()
{
$query = $this->input->get('query');
if (is_null($query))
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
$query_words = explode(' ', $query);
//TODO Where weiter anpassen z.B. Fachbereich
$this->_ci->LehreinheitModel->addSelect('tbl_lehreinheit.lehreinheit_id,
tbl_lehreinheit.lvnr,
tbl_lehreinheit.lehrfach_id,
lehrfach.kurzbz AS lehrfach,
lehrfach.bezeichnung AS lehrfach_bez,
lehrfach.farbe AS lehrfach_farbe,
tbl_lehreinheit.lehrform_kurzbz AS lehrform,
lema.mitarbeiter_uid AS lektor_uid,
tbl_mitarbeiter.kurzbz AS lektor,
tbl_studiengang.studiengang_kz,
upper(tbl_studiengang.typ::character varying::text || tbl_studiengang.kurzbz::text) AS studiengang,
lvb.semester,
lvb.verband,
lvb.gruppe,
lvb.gruppe_kurzbz,
tbl_lehreinheit.raumtyp,
tbl_lehreinheit.raumtypalternativ,
tbl_lehreinheit.stundenblockung,
tbl_lehreinheit.wochenrythmus,
lema.semesterstunden,
lema.planstunden,
tbl_lehreinheit.start_kw,
tbl_lehreinheit.anmerkung,
tbl_lehreinheit.studiensemester_kurzbz');
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitmitarbeiter lema', 'tbl_lehreinheit.lehreinheit_id = lema.lehreinheit_id');
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitgruppe lvb', 'tbl_lehreinheit.lehreinheit_id = lvb.lehreinheit_id');
$this->_ci->LehreinheitModel->addJoin('public.tbl_studiengang', 'lvb.studiengang_kz = tbl_studiengang.studiengang_kz');
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id');
$this->_ci->LehreinheitModel->addJoin('public.tbl_mitarbeiter', 'lema.mitarbeiter_uid = tbl_mitarbeiter.mitarbeiter_uid');
$this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrform', 'tbl_lehrform.lehrform_kurzbz = tbl_lehreinheit.lehrform_kurzbz');
$this->_ci->MitarbeiterModel->db->group_start();
foreach ($query_words as $word)
{
$this->_ci->LehreinheitModel->db->group_start();
$this->_ci->LehreinheitModel->db->where('lema.mitarbeiter_uid ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lvb.gruppe_kurzbz ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('tbl_studiengang.kurzbzlang ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lvb.verband ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lvb.gruppe ILIKE', "%" . $word . "%");
$this->_ci->LehreinheitModel->db->or_where('lehrfach.bezeichnung ILIKE', "%" . $word . "%");
if (is_numeric($word))
{
$this->_ci->LehreinheitModel->db->or_where('tbl_studiengang.studiengang_kz', $word);
$this->_ci->LehreinheitModel->db->or_where('lvb.semester', $word);
}
$this->_ci->LehreinheitModel->db->group_end();
}
$this->_ci->LehreinheitModel->db->group_end();
$this->_ci->LehreinheitModel->db->where('tbl_lehreinheit.studiensemester_kurzbz = \'SS2025\'');
$this->_ci->LehreinheitModel->db->where(array('tbl_lehrform.verplanen' => true));
$result = $this->_ci->LehreinheitModel->load();
$this->terminateWithSuccess(hasData($result) ? getData($result) : array());
}
public function getByStg()
public function getCourses()
{
$this->_ci->form_validation->set_data($_POST);
$this->_ci->form_validation->set_rules('studiengaenge[]',"studiengaenge","required");
$this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
if($this->_ci->form_validation->run() === FALSE)
$this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
$studiengaenge = $this->input->post('studiengaenge');
$studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz');
$studiensemester_kurzbz = $this->_ci->input->post('studiensemester_kurzbz', TRUE);
$filter = $this->_checkFilter(self::ALLOWED_COURSE_FILTER);
$this->_getBasePlan($studiensemester_kurzbz);
$this->_ci->LehreinheitModel->db->group_start();
if (isset($filter->stg))
$this->_addStgWhere($filter->stg);
if (isset($filter->uid))
{
$uid_array = (array) $filter->uid;
$this->_ci->LehreinheitModel->db->or_where_in('tbl_benutzer.uid', $uid_array);
}
$this->_ci->LehreinheitModel->db->group_end();
$this->_ci->LehreinheitModel->addOrder('lehreinheit_id');
$result = $this->_ci->LehreinheitModel->load();
$result = hasData($result) ? getData($result) : array();
$this->terminateWithSuccess($this->_mapEvents($result));
}
private function _getBasePlan($studiensemester_kurzbz)
{
$this->_ci->LehreinheitModel->addSelect('
tbl_lehreinheit.lehreinheit_id,
tbl_lehreinheit.lvnr,
@@ -142,6 +96,15 @@ class Coursepicker extends FHCAPI_Controller
$this->_ci->LehreinheitModel->addJoin('public.tbl_benutzer', 'ma.mitarbeiter_uid = tbl_benutzer.uid');
$this->_ci->LehreinheitModel->addJoin('public.tbl_person', 'tbl_benutzer.person_id = tbl_person.person_id');
$this->_ci->LehreinheitModel->db->group_start();
$this->_ci->LehreinheitModel->db->where('tbl_lehrform.verplanen', true);
$this->_ci->LehreinheitModel->db->where('tbl_lehreinheit.studiensemester_kurzbz', $studiensemester_kurzbz);
$this->_ci->LehreinheitModel->db->group_end();
}
private function _addStgWhere($studiengaenge)
{
$this->_ci->LehreinheitModel->db->group_start();
$first = true;
foreach ($studiengaenge as $studiengang)
@@ -165,14 +128,10 @@ class Coursepicker extends FHCAPI_Controller
$this->_ci->LehreinheitModel->db->group_end();
}
$this->_ci->LehreinheitModel->db->group_end();
}
$result = $this->_ci->LehreinheitModel->loadWhere(array(
'tbl_lehrform.verplanen' => true,
'tbl_lehreinheit.studiensemester_kurzbz' => $studiensemester_kurzbz
));
$result = hasData($result) ? getData($result) : array();
private function _mapEvents($result)
{
$lehreinheit_ids = array_values(array_unique(array_column($result, 'lehreinheit_id')));
$offen_map = array();
@@ -281,10 +240,8 @@ class Coursepicker extends FHCAPI_Controller
$group->verplant = $this->_formatArr($group->verplant);
$group->offenestunden = $this->_formatArr($group->offenestunden);
}
$this->terminateWithSuccess(array_values($grouped));
return array_values($grouped);
}
private function _formatArr($arr)
{
$values = array_values(array_unique($arr));
@@ -296,5 +253,24 @@ class Coursepicker extends FHCAPI_Controller
return $formatted;
}
private function _checkFilter($filters)
{
$filter_valid = false;
$filter_object = new stdClass();
foreach ($filters as $filter)
{
if ($this->_ci->input->post($filter))
{
$filter_valid = true;
$filter_object->$filter = $this->_ci->input->post($filter);
}
}
if (!$filter_valid)
$this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
return $filter_object;
}
}
+3 -10
View File
@@ -1,16 +1,9 @@
export default {
search(query) {
return {
method: 'get',
url: 'api/frontend/v1/tempus/coursepicker/search',
params: { query }
};
},
getByStg(studiengaenge, studiensemester_kurzbz) {
getCourses(filter, studiensemester_kurzbz) {
return {
method: 'post',
url: 'api/frontend/v1/tempus/coursepicker/getByStg',
params: { studiengaenge, studiensemester_kurzbz }
url: 'api/frontend/v1/tempus/coursepicker/getCourses',
params: { ...filter, studiensemester_kurzbz }
};
},
+37 -19
View File
@@ -14,6 +14,10 @@ export default {
type: Array,
default: [],
},
lecturers: {
type: Array,
default: [],
},
studiensemester: {
type: String,
default: null
@@ -26,7 +30,8 @@ export default {
allCourses: [],
sortBy: null,
multiWeekIds: new Set(),
favorites: new Set()
favorites: new Set(),
filter: {}
}
},
computed: {
@@ -86,32 +91,45 @@ export default {
studiengaenge: {
deep: true,
handler(val) {
this.searchparam = '';
this.loadCoursesByStg(val);
this.loadCourses();
}
},
lecturers: {
deep: true,
handler() {
this.loadCourses();
}
},
studiensemester() {
if (this.studiengaenge)
this.loadCoursesByStg(this.studiengaenge);
this.loadCourses();
},
},
methods: {
async loadCoursesByStg(stg) {
if (stg.length <= 0)
{
this.allCourses = [];
return;
async loadCourses()
{
const hasLektoren = this.lecturers.length > 0;
const hasStg = this.studiengaenge.length > 0;
this.filter = {};
if (hasLektoren)
this.filter.uid = this.lecturers;
if (hasStg) {
this.filter.stg = this.studiengaenge.map(
({ studiengang_kz, semester, orgform_kurzbz }) => ({
studiengang_kz,
semester,
orgform_kurzbz,
}),
);
}
let payload = stg.map(({ studiengang_kz, semester, orgform_kurzbz }) => ({
studiengang_kz,
semester,
orgform_kurzbz
}));
this.$api.call(ApiCoursePicker.getByStg(payload, this.studiensemester))
if (Object.keys(this.filter).length === 0)
return this.allCourses = [];
this.$api.call(ApiCoursePicker.getCourses(this.filter, this.studiensemester))
.then(result => {
this.allCourses = result.data.map(e => ({
this.allCourses = result?.data?.map(e => ({
lehreinheit_id: e.lehreinheit_id,
lektoren: e.lektoren,
raumtyp: e.raumtyp,
@@ -177,7 +195,7 @@ export default {
},
reload()
{
this.loadCoursesByStg(this.studiengaenge);
this.loadCourses();
},
toggleMultiWeek(course)
{
@@ -253,7 +271,7 @@ export default {
</button>
</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">
<div v-if="Object.keys(filter).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">{{$p.t('lehre', 'cptitleempty')}}</span>
<span class="small">{{$p.t('lehre', 'cpempty')}}</span>
</div>
@@ -47,6 +47,10 @@ export default {
<i :class="l.overlays.wishes ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
<span class="form-check-label">Zeitwünsche</span>
</div>
<div class="d-flex align-items-center gap-2" @click="l.showCoursePicker = !l.showCoursePicker" style="cursor:pointer">
<i :class="l.showCoursePicker ? 'fa-solid fa-toggle-on text-primary' : 'fa-solid fa-toggle-off text-muted'"></i>
<span class="form-check-label">Course Picker</span>
</div>
</div>
</div>
</div>
@@ -43,6 +43,14 @@ export function getTempusSearchbarOptions(self)
self.addToFilter(data, 'mitarbeiter');
}
},
{
label: "zum Course Picker hinzufügen",
icon: "fas fa-plus",
type: "function",
action: (data) => {
self.addToCoursePicker(data);
}
},
]
},
}
+54 -49
View File
@@ -181,21 +181,11 @@ export default {
.filter((lecture) => lecture.showEvents)
.map((lecture) => lecture.uid);
},
visibleStatusOptions() {
return Object.entries(this.visibleStatusArray).map(([key, label]) => ({
key,
label,
}));
},
visibleStatusValue() {
if (this.visibleStatus.includes("all"))
return this.visibleStatusOptions.filter(
(visibleStatus) => visibleStatus.key === "all",
);
return this.visibleStatus.map((status) => ({
key: status,
label: this.visibleStatusArray[status],
}));
courseLecturers() {
if (!this.lecturers.length) return [];
return this.lecturers
.filter((lecture) => lecture.showCoursePicker)
.map((lecture) => lecture.uid);
},
keyboardShortcuts() {
return getTempusShortcuts(this);
@@ -276,12 +266,11 @@ export default {
.call(ApiKalender.syncToStudent(orig.kalender_id))
.then(() => this.$refs.calendar.resetEventLoader());
},
setOrt: function (data) {
setOrt (data) {
this.ort_kurzbz = data.ort_kurzbz;
this.rooms = [{ ort_kurzbz: data.ort_kurzbz }];
},
onSelectVerbandAndClose(payload) {
console.log(payload);
this.onSelectVerband(payload);
bootstrap.Offcanvas.getOrCreateInstance(this.$refs.verbandMenu).hide();
},
@@ -307,41 +296,62 @@ export default {
];
}
},
setEmp: function (data) {
addToCoursePicker(data)
{
const uid = data.uid;
const label = data.name;
let lecturer = this.lecturers.find((lecture) => lecture.uid === uid);
for (const lect of this.lecturers) delete this.overlayCache[lect.uid];
if (!lecturer)
{
this.addToFilter(data, 'mitarbeiter');
lecturer = this.lecturers.find((lecture) => lecture.uid === uid);
this.lecturers = [
if (lecturer)
{
uid,
label,
showEvents: true,
overlays: { blocks: true, wishes: true },
},
];
lecturer.overlays = {
blocks: false,
wishes: false
}
}
}
this.$refs.calendar.resetEventLoader();
if (this.lastRange) this.handleRange(this.lastRange);
if (lecturer)
{
lecturer.showCoursePicker = true
}
},
addToFilter: function (filter, type) {
if (type === "ort") {
addToFilter (filter, type) {
if (type === "ort")
{
const ort_kurzbz = filter.ort_kurzbz;
if (!this.rooms.some((room) => room.ort_kurzbz === ort_kurzbz)) {
if (!this.rooms.some((room) => room.ort_kurzbz === ort_kurzbz))
{
this.rooms.push({ ort_kurzbz });
}
} else if (type === "mitarbeiter") {
}
else if (type === "mitarbeiter")
{
const uid = filter.uid;
const label = filter.name;
if (!this.lecturers.some((l) => l.uid === uid)) {
let lecturer = this.lecturers.find((lecture) => lecture.uid === uid);
if (!lecturer)
{
this.lecturers.push({
uid,
label,
showEvents: true,
overlays: { blocks: true, wishes: true },
showCoursePicker: false
});
}
else
{
lecturer.showEvents = true;
lecturer.overlays = { blocks: true, wishes: true };
}
}
this.$refs.calendar.resetEventLoader();
@@ -1175,21 +1185,10 @@ export default {
</button>
</div>
<div class="px-2 py-1 w-100">
<!--<Multiselect
:model-value="visibleStatusValue"
@update:model-value="val => toggleStatus(val.map(o => o.key))"
option-label="label"
:options="visibleStatusOptions"
placeholder="Status filtern"
:hide-selected="false"
:show-toggle-all="false"
class="w-100"
/>-->
<div
class="d-flex gap-1 py-1"
data-cy="previewRoleOptionsHolder"
>
class="d-flex gap-1 py-1"
data-cy="previewRoleOptionsHolder"
>
<button
class="btn btn-sm"
:class="previewRole === 'planer' ? 'btn-dark' : 'btn-outline-dark'"
@@ -1239,7 +1238,13 @@ export default {
v-model:parked-keys="parkedKeys"
></parking-slot>
<fhc-coursepicker ref="coursepicker" :studiengaenge="studiengaenge" @select-lecturer="setEmp" @select-kw="jumpToKw" :studiensemester="selectedStudiensemester"></fhc-coursepicker>
<fhc-coursepicker
ref="coursepicker"
:studiengaenge="studiengaenge"
:lecturers="courseLecturers"
@select-lecturer="addToFilter($event, 'mitarbeiter')"
@select-kw="jumpToKw"
:studiensemester="selectedStudiensemester"/>
</div>
<stv-studiensemester v-model:studiensemester-kurzbz="selectedStudiensemester"></stv-studiensemester>