custom sticky css; offcanvas mobility legende; mobiltiy zusatz in seperate column; added getMobilityZusatzForUids & formatZusatz similar to digital anw mobility zusaetze;

This commit is contained in:
Johann Hoffmann
2025-11-25 17:23:49 +01:00
parent 1c236cce02
commit 43925e3088
11 changed files with 406 additions and 104 deletions
@@ -31,7 +31,7 @@ class Benotungstool extends Auth_Controller
// TODO: check if related CIS config is also loaded when being routed in Cis4 by vuerouter
// TODO: check if new benotungstool should be configurable the exact same way?
// TODO: move this to some config api?
$viewData = array(
'uid'=>getAuthUID(),
@@ -59,6 +59,8 @@ class Noten extends FHCAPI_Controller
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('codex/Mobilitaet_model', 'MobilitaetModel');
$this->load->model('organisation/Erhalter_model', 'ErhalterModel');
$this->load->helper('hlp_sancho_helper');
@@ -92,26 +94,24 @@ class Noten extends FHCAPI_Controller
$grades = array();
$student_uids = array_map($func, $studentenData);
if(count($student_uids) > 0) {
$mobres = $this->MobilitaetModel->getMobilityZusatzForUids($student_uids);
$mobData = $this->getDataOrTerminateWithError($mobres);
$result = $this->ErhalterModel->load();
$erhalter = getData($result)[0];
$erhalter_kz = '9' . sprintf("%03s", $erhalter->erhalter_kz);
foreach($mobData as $mob) {
$grades[$mob->uid]['mobility_zusatz'] = $this->MobilitaetModel->formatZusatz($mob, $erhalter_kz);
}
}
foreach($student_uids as $uid) {
$grades[$uid]['grades'] = [];
$res = $this->StudentModel->load([$uid]);
if(!isError($res) && hasData($res)) $student = getData($res)[0];
$prestudent_id = $student->prestudent_id;
// TODO: last class to get rid of but this one is complicated
$mobility = new mobilitaet();
$mobility->loadPrestudent($prestudent_id);
$output = $mobility->result;
$eintrag = '';
foreach ($output as $k)
{
if(($k->mobilitaetstyp_kurzbz == 'GS') && ($k->studiensemester_kurzbz == $sem_kurzbz))
$eintrag = ' (d.d.)';
}
$grades[$uid]['mobility'] = $eintrag;
$result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, $uid, $sem_kurzbz);
@@ -131,6 +131,7 @@ class Noten extends FHCAPI_Controller
// send $grades reference to moodle addon
// TODO: DONT LET FAULTY MOODLE TOKEN BREAK THE STARTUP
// Events::trigger(
// 'getExternalGrades',
// function & () use (&$grades)
@@ -142,6 +143,8 @@ class Noten extends FHCAPI_Controller
// 'stsem' => $sem_kurzbz
// ]
// );
// calculate notenvorschläge from teilnoten
foreach($studentenData as $student) {
@@ -11,4 +11,73 @@ class Mobilitaet_model extends DB_Model
$this->dbTable = 'bis.tbl_mobilitaet';
$this->pk = 'mobilitaet_id';
}
public function getMobilityZusatzForUids($uids) {
$qry = "SELECT distinct on(nachname, vorname, public.tbl_benutzer.person_id) uid,
tbl_mitarbeiter.mitarbeiter_uid,
tbl_note.lkt_ueberschreibbar, tbl_note.anmerkung,
tbl_mobilitaet.mobilitaetstyp_kurzbz,
(CASE WHEN bis.tbl_mobilitaet.studiensemester_kurzbz = vw_student_lehrveranstaltung.studiensemester_kurzbz THEN 1 ELSE 0 END) as doubledegree,
public.tbl_prestudent.gsstudientyp_kurzbz as ddtype,
(SELECT status_kurzbz FROM public.tbl_prestudentstatus
WHERE prestudent_id=tbl_student.prestudent_id
ORDER BY datum DESC, insertamum DESC, ext_id DESC LIMIT 1) as studienstatus
FROM
campus.vw_student_lehrveranstaltung
JOIN public.tbl_benutzer USING(uid)
JOIN public.tbl_person USING(person_id)
LEFT JOIN public.tbl_student ON(uid=student_uid)
LEFT JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid)
LEFT JOIN public.tbl_studentlehrverband USING(student_uid,studiensemester_kurzbz)
LEFT JOIN lehre.tbl_zeugnisnote on(vw_student_lehrveranstaltung.lehrveranstaltung_id=tbl_zeugnisnote.lehrveranstaltung_id
AND tbl_zeugnisnote.student_uid=tbl_student.student_uid
AND tbl_zeugnisnote.studiensemester_kurzbz=tbl_studentlehrverband.studiensemester_kurzbz)
LEFT JOIN lehre.tbl_note USING (note)
LEFT JOIN bis.tbl_bisio ON(uid=tbl_bisio.student_uid)
LEFT JOIN public.tbl_studiengang ON(tbl_student.studiengang_kz=tbl_studiengang.studiengang_kz)
LEFT JOIN bis.tbl_mobilitaet USING(prestudent_id)
LEFT JOIN public.tbl_prestudent USING(prestudent_id)
WHERE uid IN ?";
return $this->execReadOnlyQuery($qry, [$uids]);
}
public function formatZusatz($entry, $erhalter_kz) {
$zusatz = '';
if (isset($entry->studienstatus) && $entry->studienstatus === 'Incoming') {
$zusatz = '(i)';
}
if (isset($entry->lkt_ueberschreibbar) && $entry->lkt_ueberschreibbar === false) {
$zusatz .= ' (' . ($entry->anmerkung ?? '') . ')';
}
if (isset($entry->mitarbeiter_uid) && $entry->mitarbeiter_uid !== null) {
$zusatz .= ' (ma)';
}
if (isset($entry->stg_kz_student) && $entry->stg_kz_student == $erhalter_kz) {
$zusatz .= ' (a.o.)';
}
if (
isset($entry->mobilitaetstyp_kurzbz) && $entry->mobilitaetstyp_kurzbz &&
isset($entry->doubledegree) && $entry->doubledegree === 1
) {
$zusatz .= ' (d.d.';
$ddtype = $entry->ddtype ?? null;
if ($ddtype == 'Intern') {
$zusatz .= 'i.)';
} elseif ($ddtype == 'Extern') {
$zusatz .= 'o.)';
} else {
$zusatz .= ')';
}
}
return $zusatz;
}
}
@@ -898,8 +898,4 @@ class Prestudent_model extends DB_Model
return true;
}
public function getMobilityPrestudent(){
}
}
@@ -24,6 +24,7 @@ $includesArray = array(
'public/css/components/FormUnderline.css',
'public/css/Cis4/Cms.css',
'public/css/Cis4/Studium.css',
'public/css/Cis4/Benotungstool.css'
),
'customJSs' => array(
'vendor/npm-asset/primevue/accordion/accordion.min.js',
@@ -36,4 +36,4 @@ $this->load->view('templates/FHC-Header', $includesArray);
></cis-menu>
</header>
<main id="cis-main" class="flex-grow-1 p-4 pt-2">
<main id="cis-main" class="flex-grow-1 p-4 pt-2" style="min-width: 0;">
+41
View File
@@ -0,0 +1,41 @@
/* 1. Stick the Header */
#notentable .tabulator-header .tabulator-col.sticky-col {
position: sticky;
left: 0;
z-index: 10; /* Must be higher than other headers */
background-color: #fff; /* Opaque background is required */
border-right: 2px solid #ddd; /* Optional: Separator line */
}
/* 2. Stick the Data Cells */
#notentable .tabulator-tableholder .tabulator-row .tabulator-cell.sticky-col {
position: sticky;
left: 0;
z-index: 10; /* Ensure it floats above other cells */
background-color: #fff; /* Match your row background color */
border-right: 2px solid #ddd; /* Optional: Separator line */
}
/* 3. Fix for Hover Effects (Optional) */
/* If you use hover rows, you need to ensure the sticky cell matches the hover color */
#notentable .tabulator-row:hover .tabulator-cell.sticky-col {
background-color: #ccc; /* Match your existing hover color */
}
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable::after {
content: "▾";
position: absolute;
right: 6px;
color: rgba(176, 176, 106, 0.73);;
font-size: x-large;
bottom: 6px;
pointer-events: none;
}
-17
View File
@@ -854,21 +854,4 @@ html {
#cis-main .modal-footer {
background-color: var(--fhc-secondary);
}
/* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable {
position: relative;
background-color: rgba(255, 255, 157, 0.73);
cursor: pointer;
}
#notentable .tabulator-tableholder .tabulator-editable::after {
content: "▾";
position: absolute;
right: 6px;
color: rgba(176, 176, 106, 0.73);;
font-size: x-large;
bottom: 6px;
pointer-events: none;
}
+151
View File
@@ -0,0 +1,151 @@
export default {
name: 'BootstrapOffcanvas',
data: () => ({
offcanvas: null
}),
props: {
backdrop: {
type: [Boolean, String],
default: true,
validator(value) {
return ['static', true, false].includes(value);
}
},
keyboard: {
type: Boolean,
default: true
},
scroll: {
type: Boolean,
default: false
},
placement: {
type: String,
default: 'start', // start | end | top | bottom
validator(value) {
return ['start', 'end', 'top', 'bottom'].includes(value);
}
},
noCloseBtn: Boolean,
headerClass: {
type: [String, Array, Object],
default: ''
},
bodyClass: {
type: [String, Array, Object],
default: 'p-4'
},
footerClass: {
type: [String, Array, Object],
default: ''
},
dialogClass: [String, Array, Object]
},
emits: [
"hideBsOffcanvas",
"hiddenBsOffcanvas",
"hidePreventedBsOffcanvas",
"showBsOffcanvas",
"shownBsOffcanvas"
],
methods: {
dispose() {
return this.offcanvas?.dispose();
},
hide() {
return this.offcanvas?.hide();
},
show(relatedTarget) {
return this.offcanvas?.show(relatedTarget);
},
toggle() {
return this.offcanvas?.toggle();
},
popup(body, options, title, footer) {
const BsOffcanvas = this,
slots = {};
if (body !== undefined)
slots.default = () => body;
if (title !== undefined)
slots.title = () => title;
if (footer !== undefined)
slots.footer = () => footer;
let includedPrimevue = false;
if (typeof primevue !== 'undefined')
includedPrimevue = true;
return new Promise((resolve, reject) => {
const instance = Vue.createApp({
name: 'OffcanvasTmpApp',
setup() {
return () =>
Vue.h(BsOffcanvas, {
class: 'offcanvas-wrapper',
ref: 'offcanvas',
...options
}, slots);
},
mounted() {
this.$refs.offcanvas.show();
},
beforeUnmount() {
if (this.$refs.offcanvas)
this.$refs.offcanvas.result !== false ? resolve(this.$refs.offcanvas.result) : reject();
},
unmounted() {
wrapper.parentElement.removeChild(wrapper);
}
});
const wrapper = document.createElement('div');
if (includedPrimevue) {
instance.use(primevue.config.default, { zIndex: { overlay: 9999 } });
}
import('../../plugins/Phrasen.js').then((Phrasen) => {
instance.use(Phrasen.default);
instance.mount(wrapper);
document.body.appendChild(wrapper);
});
});
}
},
mounted() {
if (this.$refs.offcanvas) {
this.offcanvas = new bootstrap.Offcanvas(this.$refs.offcanvas, {
backdrop: this.backdrop,
keyboard: this.keyboard,
scroll: this.scroll
});
}
},
template: `
<div ref="offcanvas"
class="bootstrap-offcanvas offcanvas"
:class="['offcanvas-' + placement, dialogClass]"
tabindex="-1"
@[\`hide.bs.offcanvas\`]="$emit('hideBsOffcanvas')"
@[\`hidden.bs.offcanvas\`]="$emit('hiddenBsOffcanvas')"
@[\`hidePrevented.bs.offcanvas\`]="$emit('hidePreventedBsOffcanvas')"
@[\`show.bs.offcanvas\`]="$emit('showBsOffcanvas')"
@[\`shown.bs.offcanvas\`]="$emit('shownBsOffcanvas')"
>
<div class="offcanvas-header" :class="headerClass" v-if="$slots.title">
<h5 class="offcanvas-title">
<slot name="title"></slot>
</h5>
<button v-if="!noCloseBtn" type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body" :class="bodyClass">
<slot></slot>
</div>
<div v-if="$slots.footer" class="offcanvas-footer" :class="footerClass">
<slot name="footer"></slot>
</div>
</div>
`
}
@@ -3,6 +3,7 @@ import ApiLehre from "../../../api/factory/lehre.js";
import ApiNoten from "../../../api/factory/noten.js";
import ApiStudiensemester from "../../../api/factory/studiensemester.js";
import BsModal from '../../Bootstrap/Modal.js';
import BsOffcanvas from '../../Bootstrap/Offcanvas.js';
import VueDatePicker from '../../vueDatepicker.js.php';
import LehreinheitenModule from '../../DropdownModes/LehreinheitenModule';
import MobilityLegende from '../../Mobility/Legende.js';
@@ -12,6 +13,7 @@ export const Benotungstool = {
name: "Benotungstool",
components: {
BsModal,
BsOffcanvas,
CoreFilterCmpt,
MobilityLegende,
Dropdown: primevue.dropdown,
@@ -43,8 +45,6 @@ export const Benotungstool = {
data() {
return {
neuesPruefungsdatumModalVisible: false,
offsetLeft: 0,
headerEl: null,
loading: false,
selectedUids: [], // shared selection state
selectedLehreinheit: null,
@@ -116,6 +116,17 @@ export const Benotungstool = {
row.reformat() // trigger reformat of arrow
}
}
},
{
event: "cellClick",
handler: async (e, cell) => {
const field = cell.getField()
if(field == "mobility_zusatz") {
this.$refs.drawer.show()
e.stopPropagation()
}
}
}
]};
},
@@ -258,7 +269,12 @@ export const Benotungstool = {
this.loading = true
this.$api.call(ApiNoten.saveNotenvorschlagBulk(this.lv_id, this.sem_kurzbz, notenbulk)).then(res => {
if(res.meta.status === 'success') {
this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/notenImportSuccessAlert'))
this.$fhcAlert.alertDefault(
'success',
'Info',
this.$p.t('benotungstool/notenImportSuccessAlert'),
true
)
const lvNoten = res.data
@@ -289,7 +305,12 @@ export const Benotungstool = {
this.$api.call(ApiNoten.saveStudentPruefungBulk(this.lv_id, this.sem_kurzbz, pruefungenbulk))
.then((res)=> {
if(res.meta.status === 'success') {
this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/pruefungImportSuccessAlert'))
this.$fhcAlert.alertDefault(
'success',
'Info',
this.$p.t('benotungstool/pruefungImportSuccessAlert'),
true
)
this.handleAddNewPruefungenResponse(res, pruefungenbulk)
}
}).finally(()=>{this.loading = false})
@@ -426,8 +447,8 @@ export const Benotungstool = {
},
getNotenTableOptions() {
return {
rowHeight: 40,
height: 700,
virtualDom: false,
index: 'uid',
layout: 'fitDataStretch',
placeholder: this.$p.t('global/noDataAvailable'),
@@ -442,6 +463,7 @@ export const Benotungstool = {
return true; // student can be selected to add pruefung
},
rowHeight: 40,
rowFormatter: this.fixTabulatorSelectionFormatter,
columns: [
{
@@ -488,13 +510,15 @@ export const Benotungstool = {
handleClick: this.selectAllHandler
},
width: 50,
},
{title: Vue.computed(() => this.$p.t('benotungstool/c4mail')), field: 'email', formatter: this.mailFormatter, tooltip: false, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4antrittCount')), field: 'hoechsterAntritt', tooltip: false, widthGrow: 1},
{title: 'UID', field: 'uid', tooltip: false, widthGrow: 1, topCalc: this.sumCalcFunc},
cssClass: 'sticky-col'
},
{title: 'UID', field: 'uid', tooltip: false, widthGrow: 1, topCalc: this.sumCalcFunc, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$p.t('benotungstool/c4mail')), field: 'email', formatter: this.mailFormatter, tooltip: false, visible: false, widthGrow: 1, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4antrittCountv2')), field: 'hoechsterAntritt', tooltip: false, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4vorname')), field: 'vorname', headerFilter: true, tooltip: false, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4nachname')), field: 'nachname', headerFilter: true, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4teilnoten')), field: 'teilnote', widthGrow: 1, formatter: this.teilnotenFormatter},
{title: Vue.computed(() => this.$p.t('benotungstool/c4nachname')), field: 'nachname', headerFilter: true, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4mobility')), field: 'mobility_zusatz', headerFilter: true, widthGrow: 1, visible: false},
{title: Vue.computed(() => this.$p.t('benotungstool/c4teilnoten')), field: 'teilnote', widthGrow: 1, formatter: this.teilnotenFormatter, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4note')), field: 'note_vorschlag',
editor: 'list',
editorParams: (cell) => {
@@ -531,8 +555,9 @@ export const Benotungstool = {
if(p || !match?.lkt_ueberschreibbar) style = 'color: gray;font-style: italic; background-color: #f0f0f0;pointer-events: none;opacity: 0.6;user-select: none;cursor: not-allowed;'
return '<div style="'+style+'">' + val + '</div>'
},
widthGrow: 1},
{title: '', width: 50, hozAlign: 'center', formatter: this.arrowFormatter, cellClick: this.saveNote},
widthGrow: 1
},
{title: '', width: 50, hozAlign: 'center', formatter: this.arrowFormatter, cellClick: this.saveNote, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4lvnote')), field: 'lv_note',
formatter: this.notenFormatter,
headerFilter: 'list',
@@ -540,8 +565,9 @@ export const Benotungstool = {
return { values: ["\u00A0",this.$p.t('benotungstool/c4noteEmpty') ,this.$p.t('benotungstool/c4positiv'), this.$p.t('benotungstool/c4negativ') ,...this.notenOptions.map(opt => opt.bezeichnung)] }
},
headerFilterFunc: this.notenFilterFunc,
widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4freigabe')), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter},
widthGrow: 1
},
{title: Vue.computed(() => this.$p.t('benotungstool/c4freigabe')), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4zeugnisnote')),
field: 'note',
formatter: this.notenFormatter,
@@ -552,13 +578,15 @@ export const Benotungstool = {
return { values: ["\u00A0", this.$p.t('benotungstool/c4noteEmpty'),this.$p.t('benotungstool/c4positiv'), this.$p.t('benotungstool/c4negativ') ,...this.notenOptions.map(opt => opt.bezeichnung)] }
},
headerFilterFunc: this.notenFilterFunc,
widthGrow: 1},
widthGrow: 1
},
{title: Vue.computed(() => this.$p.t('benotungstool/c4kommPruef')),
field: 'kommPruef', widthGrow: 1,
formatter: this.pruefungFormatter,
topCalc: this.terminCalcFunc,
topCalcFormatter: this.terminCalcFormatter,
hozAlign:"center", minWidth: 150}
hozAlign:"center", minWidth: 150, visible: false
}
],
persistence: false,
}
@@ -701,14 +729,16 @@ export const Benotungstool = {
freigabeFormatter(cell) {
const value = cell.getValue()
let style = 'display: flex; justify-content: center; align-items: center; height: 100%;'
if(value === 'ok') {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
return '<div style="'+style+'">' +
'<i class="fa fa-circle-check" style="color:green"></i></div>'
} else if (value === 'offen') {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
return '<div style="'+style+'">' +
'<i class="fa-regular fa-circle"></i></div>'
} else if (value === 'changed') {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
return '<div style="'+style+'">' +
'<i class="fa fa-circle-check"></i></div>'
}
@@ -741,11 +771,14 @@ export const Benotungstool = {
},
teilnotenFormatter(cell) {
const val = cell.getValue()
return '<div style="white-space: pre-line;">'+val+'</div>'
let style = 'white-space: pre-line;'
return '<div style="">'+val+'</div>'
},
pruefungFormatter(cell) {
const data = cell.getData()
const noteDef = data.note ? this.notenOptions.find(n => n.note == data.note) : null
if(!data.note || !noteDef?.lkt_ueberschreibbar) {
return ''
@@ -901,18 +934,23 @@ export const Benotungstool = {
const row = cell.getRow()
const data = row.getData()
let style = 'display: flex; justify-content: center; align-items: center; height: 100%;'
if(!data.note_vorschlag || (data.note_vorschlag == data.lv_note)) { // uncolored arrow
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
return '<div style="'+style+'">' +
'<i class="fa fa-arrow-right"></i></div>'
}
// can save a notenvorschlag -> colored
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
return '<div style="'+style+'">' +
'<i class="fa fa-arrow-right fa-2xl" style="color:#00649C"></i></div>'
},
mailFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%">' +
let style = 'display: flex; justify-content: center; align-items: center; height: 100%;'
return '<div style="'+style+'">' +
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
buildMailToLink(student){
@@ -999,7 +1037,7 @@ export const Benotungstool = {
const grades = this.teilnoten[s.uid].grades
s.teilnote = ''
s.mobility_zusatz = this.teilnoten[s.uid].mobility_zusatz
grades.forEach(g => {
const notenOption = this.notenOptions.find(n=>n.note == g.grade)
if(notenOption.positiv) s.teilnote += ('<span>'+g.text +'</span>'+ '<br/>')
@@ -1121,29 +1159,8 @@ export const Benotungstool = {
this.tableBuiltPromise = new Promise(this.tableResolve)
await this.tableBuiltPromise
this.headerEl = document.getElementById('nav-main')
if(this.headerEl) {
this.headerEl.addEventListener("shown.bs.collapse", this.handleSidebar);
this.headerEl.addEventListener("hidden.bs.collapse", this.handleSidebar);
this.handleSidebar()
}
this.loadNoten(this.lv_id, this.sem_kurzbz)
// this.calcMaxTableHeight()
},
handleSidebar() {
// this.$refs.notenTable.tabulator.blockRedraw(); // stop automatic resizes
this.offsetLeft = this.headerEl.getBoundingClientRect().width;
// setTimeout(() => {
// this.$refs.notenTable.tabulator.restoreRedraw(); // re-enable
// this.$refs.notenTable.tabulator.redraw(true); // manual recalculation
// }, 400); // after animation finishes
this.calcMaxTableHeight()
},
lvChanged(e) {
@@ -1233,8 +1250,12 @@ export const Benotungstool = {
typ
)).then(res => {
if(res.meta.status === 'success') { //'Prüfung für Student ' + this.pruefungStudent.uid + ' bearbeitet oder angelegt'
this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/pruefungSaveForUid', [this.pruefungStudent.uid]))
this.$fhcAlert.alertDefault(
'success',
'Info',
this.$p.t('benotungstool/pruefungSaveForUid', [this.pruefungStudent.uid]),
true
)
const s = this.studenten.find(s => s.uid === res.data[1]?.student_uid)
s.freigabedatum = this.parseDate(res.data[1]?.['freigabedatum'])
@@ -1357,7 +1378,12 @@ export const Benotungstool = {
this.$api.call(ApiNoten.saveStudentenNoten(this.password, this.changedNoten, this.lv_id, this.sem_kurzbz))
.then((res) => {
if(res.meta.status === 'success') {
this.$fhcAlert.alertWarning('Noten gespeichert')
this.$fhcAlert.alertDefault(
'success',
'Info',
'Noten gespeichert',
true
)
}
res.data.forEach(d => {
@@ -1416,8 +1442,12 @@ export const Benotungstool = {
this.sem_kurzbz,
)).then(res => {
if(res.meta.status === "success") {
this.$fhcAlert.alertSuccess(this.$p.t('benotungstool/pruefungAngelegtAn', [dateStrFront]))
this.$fhcAlert.alertDefault(
'success',
'Info',
this.$p.t('benotungstool/pruefungAngelegtAn', [dateStrFront]),
true
)
this.handleAddNewPruefungenResponse(res, uids)
@@ -1558,13 +1588,6 @@ export const Benotungstool = {
mounted() {
this.setupMounted()
},
unmounted() {
if(this.headerEl) {
this.headerEl.removeEventListener("shown.bs.collapse", this.handleSidebar)
this.headerEl.removeEventListener("hidden.bs.collapse", this.handleSidebar)
this.headerEl = null
}
},
template: `
<bs-modal ref="modalContainerNotenImport" class="bootstrap-prompt" dialogClass="modal-lg">
<template v-slot:title>{{$p.t('benotungstool/c4notenImportieren')}}</template>
@@ -1672,6 +1695,23 @@ export const Benotungstool = {
</template>
</bs-modal>
<BsOffcanvas
ref="drawer"
placement="end"
:backdrop="true"
:style="{ '--bs-offcanvas-width': '600px' }"
>
<template #title>
</template>
<MobilityLegende/>
<template #footer>
</template>
</BsOffcanvas>
<FhcOverlay :active="loading || saving"></FhcOverlay>
<div class="row">
@@ -1721,7 +1761,7 @@ export const Benotungstool = {
</div>
<hr>
<div id="notentable" class="row" :style="'overflow-x: auto; max-width: calc(98vw - ' + offsetLeft + 'px);'">
<div id="notentable" class="row" :style="'overflow-x: auto;'">
<core-filter-cmpt
v-if="tabulatorCanBeBuilt"
@uuidDefined="handleUuidDefined"
@@ -1753,8 +1793,6 @@ export const Benotungstool = {
</template>
</core-filter-cmpt>
</div>
<MobilityLegende/>
`,
};
+23 -3
View File
@@ -50698,18 +50698,18 @@ and represent the current state of research on the topic. The prescribed citatio
array(
'app' => 'core',
'category' => 'benotungstool',
'phrase' => 'c4antrittCount',
'phrase' => 'c4antrittCountv2',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anzahl Prüfungsantritte',
'text' => 'Antritte',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Number of exam attempts',
'text' => 'Attempts',
'description' => '',
'insertvon' => 'system'
)
@@ -50735,6 +50735,26 @@ and represent the current state of research on the topic. The prescribed citatio
)
)
),
array(
'app' => 'core',
'category' => 'benotungstool',
'phrase' => 'c4mobility',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Mobilität',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Mobility',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'benotungstool',