diff --git a/public/js/components/Cis/Benotungstool/Benotungstool.js b/public/js/components/Cis/Benotungstool/Benotungstool.js
index 8abaef7ad..0440ffba0 100644
--- a/public/js/components/Cis/Benotungstool/Benotungstool.js
+++ b/public/js/components/Cis/Benotungstool/Benotungstool.js
@@ -1,6 +1,7 @@
import {CoreFilterCmpt} from "../../filter/Filter.js";
import ApiLehre from "../../../api/factory/lehre.js";
import ApiNoten from "../../../api/factory/noten.js";
+import ApiAuthinfo from "../../../api/factory/authinfo.js";
import ApiStudiensemester from "../../../api/factory/studiensemester.js";
import BsModal from '../../Bootstrap/Modal.js';
import BsOffcanvas from '../../Bootstrap/Offcanvas.js';
@@ -9,7 +10,7 @@ import LehreinheitenModule from '../../DropdownModes/LehreinheitenModule.js';
import MobilityLegende from '../../Mobility/Legende.js';
import FhcOverlay from "../../Overlay/FhcOverlay.js";
import {debounce} from "../../../helpers/debounce.js";
-import {centeredFormatter} from "../../../tabulator/formatter/centered.js";
+import {centeredTextFormatter} from "../../../tabulator/formatter/centered.js";
export const Benotungstool = {
name: "Benotungstool",
@@ -30,25 +31,20 @@ export const Benotungstool = {
props: {
lv_id: {
default: null,
- required: true
+ required: false
},
sem_kurzbz: {
default: null,
- required: true
- },
- viewData: {
- type: Object,
- required: true,
- default: () => ({uid: ''}),
- validator(value) {
- return value && value.uid
- }
+ required: false
}
},
data() {
return {
+ uid: null,
headerFiltersRestored: false,
filtersRestored: false,
+ filteredRows: null,
+ filteredcount: 0,
colLayoutRestored: false,
sortRestored: false,
stateRestored: false,
@@ -137,10 +133,32 @@ export const Benotungstool = {
this.undoSelection(cell)
}
}
+ },
+ {
+ event: 'dataFiltered',
+ handler: (filters, rows) => {
+ this.filteredRows = rows;
+ this.filteredcount = rows.length;
+
+ if (!this.selectedUids.length) return;
+
+ const visibleData = new Set(rows.map(r => r.getData()));
+ const filteredOut = this.selectedUids.filter(su => !visibleData.has(su));
+ if (!filteredOut.length) return;
+
+ const filteredOutSet = new Set(filteredOut);
+ this.$refs.notenTable.tabulator.getSelectedRows()
+ .filter(r => filteredOutSet.has(r.getData()))
+ .forEach(r => r.deselect());
+ }
}
]};
},
methods: {
+ async fetchAuthUID() {
+ const authIdResponse = await this.$api.call(ApiAuthinfo.getAuthUID());
+ this.uid = authIdResponse.data.uid;
+ },
loadState() {
return JSON.parse(localStorage.getItem(this.persistenceID) || "null");
},
@@ -724,12 +742,13 @@ export const Benotungstool = {
virtualDom: true,
virtualDomBuffer: 5000,
index: 'uid',
- layout: 'fitDataStretch',
+ layout: 'fitData',
placeholder: this.$capitalize(this.$p.t('global/noDataAvailable')),
selectable: true,
selectableRangeMode: "click", // shift+click
selectablePersistence: false, // reset selection on table reload
selectableCheck: this.selectableCheck,
+ rowHeight: 30,
rowFormatter: this.fixTabulatorSelectionFormatter,
columns: this.getColumnsDefinition(),
persistence: false,
@@ -797,18 +816,18 @@ export const Benotungstool = {
field: 'selectCol',
title: ''
})
- columns.push({title: 'UID', field: 'uid', tooltip: false, widthGrow: 1, topCalc: this.sumCalcFunc, formatter: centeredFormatter, cssClass: 'sticky-col'})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mail'))), field: 'email', formatter: this.mailFormatter, tooltip: false, visible: false, widthGrow: 1, variableHeight: true})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4antrittCountv2'))), field: 'hoechsterAntritt', formatter: centeredFormatter, tooltip: false, widthGrow: 1})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4vorname'))), field: 'vorname', formatter: centeredFormatter, headerFilter: true, tooltip: false, widthGrow: 1})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4nachname'))), field: 'nachname', formatter: centeredFormatter, headerFilter: true, widthGrow: 1})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4anwesenheitsquote'))), field: 'anwquote', widthGrow: 1, formatter: this.percentFormatter})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mobility'))), field: 'mobility_zusatz', formatter: centeredFormatter, headerFilter: true, widthGrow: 1, visible: false})
+ columns.push({title: 'UID', field: 'uid', tooltip: false, topCalc: this.sumCalcFunc, formatter: centeredTextFormatter, cssClass: 'sticky-col'})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mail'))), field: 'email', formatter: this.mailFormatter, tooltip: false, visible: false, variableHeight: true})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4antrittCountv2'))), field: 'hoechsterAntritt', formatter: centeredTextFormatter, tooltip: false})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4vorname'))), field: 'vorname', formatter: centeredTextFormatter, headerFilter: true, tooltip: false})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4nachname'))), field: 'nachname', formatter: centeredTextFormatter, headerFilter: true})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4anwesenheitsquote'))), field: 'anwquote', formatter: this.percentFormatter})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4mobility'))), field: 'mobility_zusatz', formatter: centeredTextFormatter, headerFilter: true, visible: false})
if(this.config?.CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE) {
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4teilnoten'))), field: 'teilnote', widthGrow: 1, formatter: this.teilnotenFormatter, variableHeight: true})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4teilnoten'))), field: 'teilnote', formatter: this.teilnotenFormatter, variableHeight: true})
}
if(this.config?.CIS_GESAMTNOTE_PUNKTE) {
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4punkte'))), field: 'punkte', widthGrow: 1,
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4punkte'))), field: 'punkte',
editor: this.liveNumberEditor,
editable: (cell) => {
const rowData = cell.getRow().getData();
@@ -856,8 +875,7 @@ export const Benotungstool = {
if(val === undefined) return ''
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 '
' + val + '
'
- },
- widthGrow: 1
+ }
})
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4notenvorschlagUebernehmen'))), field: 'übernehmen', width: 150, hozAlign: 'center', formatter: this.arrowFormatter,
cellClick: this.saveNote,
@@ -868,10 +886,9 @@ export const Benotungstool = {
headerFilterParams: () => {
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
+ headerFilterFunc: this.notenFilterFunc
})
- columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4freigabe'))), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter, variableHeight: true})
+ columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4freigabe'))), field: 'freigegeben', formatter: this.freigabeFormatter, variableHeight: true})
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4zeugnisnote'))),
field: 'note',
formatter: this.notenFormatter,
@@ -881,8 +898,7 @@ export const Benotungstool = {
headerFilterParams: () => {
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
+ headerFilterFunc: this.notenFilterFunc
})
columns.push({title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4kommPruef'))),
field: 'kommPruef', widthGrow: 1,
@@ -918,19 +934,19 @@ export const Benotungstool = {
},
selectAllHandler(e, cell) {
const table = cell.getTable();
- const rows = table.getRows();
+ const rows = this.filteredRows ?? table.getRows();
- // custom select all logic
const allowed = rows.filter(r => r.getData().selectable);
const selected = allowed.every(r => r.isSelected());
- if(selected){
+ if (selected) {
allowed.forEach(r => r.deselect());
+ e.target.checked = false;
} else {
allowed.forEach(r => r.select());
+ e.target.checked = true;
}
- // stop built-in handler
e.stopPropagation();
return false;
},
@@ -1031,7 +1047,7 @@ export const Benotungstool = {
notenFormatter(cell) {
const value = cell.getValue()
const field = cell.getField()
- let style = 'display: flex; justify-content: center; align-items: center; height: 100%;';
+ let style = 'display: flex; justify-content: start; align-items: center; height: 100%;';
// Wenn sich die Zeugnisnote von der von Ihnen freigegebenen Note unterscheidet,
// wird erstere rot umrandet markiert.
@@ -1494,6 +1510,7 @@ export const Benotungstool = {
this.$refs.notenTable.tabulator.redraw(true);
},
loadNoten(lv_id, sem_kurzbz) {
+ if (!lv_id || !sem_kurzbz) return
this.loading = true
this.$api.call(ApiNoten.getStudentenNoten(lv_id, sem_kurzbz))
.then(res => {
@@ -1515,8 +1532,21 @@ export const Benotungstool = {
this.notenTableOptions.height = window.visualViewport.height - rect.top - 50
this.$refs.notenTable.tabulator.setHeight(this.notenTableOptions.height)
},
+ loadLehrveranstaltungen(sem_kurzbz, preselectLvId = null) {
+ return this.$api.call(ApiLehre.getZugewieseneLv(this.uid, sem_kurzbz)).then(res => {
+ this.lehrveranstaltungen = res.data
+ this.lehrveranstaltungen.forEach(lva => {
+ lva.fullString = `${lva.stg_kurzbz} - ${lva.lv_semester} - ${lva.orgform}: ${lva.lv_bezeichnung}`
+ })
+ this.selectedLehrveranstaltung = preselectLvId
+ ? this.lehrveranstaltungen.find(lva => lva.lehrveranstaltung_id == preselectLvId) ?? null
+ : null
+ })
+ },
async setupCreated() {
this.loading = true
+
+ await this.fetchAuthUID()
this.debouncedFetchPunkteForPruefung = debounce(this.fetchNoteForPunktePruefung, 500)
@@ -1524,28 +1554,33 @@ export const Benotungstool = {
const configPromise = this.$api.call(ApiNoten.getCisConfig()).then(res => {
this.config = res.data
})
-
- // fetch lva dropdown
- this.$api.call(ApiLehre.getZugewieseneLv(this.viewData?.uid, this.sem_kurzbz)).then(res => {
- this.lehrveranstaltungen = res.data
+
+ this.$api.call(ApiStudiensemester.getAllStudiensemesterAndAktOrNext()).then(res => {
+ this.studiensemester = res.data[0]
- // build dropdown option string
- this.lehrveranstaltungen.forEach(lva => {
- lva.fullString = `${lva.stg_kurzbz} - ${lva.lv_semester}: ${lva.lv_bezeichnung}`
- })
-
- this.selectedLehrveranstaltung = this.lehrveranstaltungen.find(lva => lva.lehrveranstaltung_id == this.lv_id)
+ let defaultSem = this.sem_kurzbz
+ ? this.studiensemester.find(s => s.studiensemester_kurzbz === this.sem_kurzbz)
+ : null
+
+ if (!defaultSem) {
+ const aktOrNext = res.data[1]
+ let aktKurzbz = null
+ if (typeof aktOrNext === 'string') aktKurzbz = aktOrNext
+ else if (Array.isArray(aktOrNext)) aktKurzbz = aktOrNext[0]?.studiensemester_kurzbz ?? aktOrNext[0]
+ else if (aktOrNext && typeof aktOrNext === 'object') aktKurzbz = aktOrNext.studiensemester_kurzbz
+
+ defaultSem = this.studiensemester.find(s => s.studiensemester_kurzbz === aktKurzbz)
+ }
+
+ this.selectedSemester = defaultSem ?? this.studiensemester[0] ?? null
+
+ const sem = this.selectedSemester?.studiensemester_kurzbz
+ if (sem) this.loadLehrveranstaltungen(sem, this.lv_id)
})
LehreinheitenModule.setupContext(this.$.appContext.config.globalProperties)
LehreinheitenModule.bindParams(Vue.ref(Vue.computed(() => this.LeDropdownParams)));
- // fetch sem_kurzbz dropdown
- this.$api.call(ApiStudiensemester.getStudiensemester()).then(res => {
- this.studiensemester = res.data[0]
- this.selectedSemester = this.studiensemester.find(sem => sem.studiensemester_kurzbz === this.sem_kurzbz)
- })
-
// fetch noten dropdown
this.$api.call(ApiNoten.getNoten()).then(async res => {
this.notenOptions = res.data
@@ -1562,50 +1597,46 @@ export const Benotungstool = {
async setupMounted() {
this.tableBuiltPromise = new Promise(this.tableResolve)
await this.tableBuiltPromise
-
- this.loadNoten(this.lv_id, this.sem_kurzbz)
+
+ if (this.lv_id && this.sem_kurzbz) {
+ this.loadNoten(this.lv_id, this.sem_kurzbz)
+ } else {
+ this.loading = false
+ }
this.calcMaxTableHeight()
-
},
lvChanged(e) {
+ const sem = this.selectedSemester?.studiensemester_kurzbz ?? this.sem_kurzbz
this.$router.push({
name: "Benotungstool",
params: {
- sem_kurzbz: this.sem_kurzbz,
+ sem_kurzbz: sem,
lv_id: e.value.lehrveranstaltung_id
}
})
// reload data
- this.loadNoten(e.value.lehrveranstaltung_id, this.sem_kurzbz)
+ this.loadNoten(e.value.lehrveranstaltung_id, sem)
},
ssChanged(e) {
- // change url params & write history
- this.$router.push({
- name: "Benotungstool",
- params: {
- sem_kurzbz: e.value.studiensemester_kurzbz,
- lv_id: this.lv_id
- }
- })
+ const sem = e.value.studiensemester_kurzbz
+ const keepLvId = this.selectedLehrveranstaltung?.lehrveranstaltung_id ?? this.lv_id
this.loading = true
- // diff lv_id -> reload zugewiesene lv
- this.$api.call(ApiLehre.getZugewieseneLv(this.viewData?.uid, e.value.studiensemester_kurzbz)).then(res => {
- this.lehrveranstaltungen = res.data
+ this.loadLehrveranstaltungen(sem, keepLvId).then(() => {
+ const lvId = this.selectedLehrveranstaltung?.lehrveranstaltung_id ?? null
- // build dropdown option string
- this.lehrveranstaltungen.forEach(lva => {
- lva.fullString = `${lva.stg_kurzbz} - ${lva.lv_semester}: ${lva.lv_bezeichnung}`
+ this.$router.push({
+ name: "Benotungstool",
+ params: { sem_kurzbz: sem, lv_id: lvId ?? undefined }
})
- this.selectedLehrveranstaltung = this.lehrveranstaltungen.find(lva => lva.lehrveranstaltung_id == this.lv_id)
- }).then(()=>{
-
- // reload data
- this.loadNoten(this.lv_id, e.value.studiensemester_kurzbz)
- }).finally( () => this.loading = false)
-
+ if (lvId) {
+ this.loadNoten(lvId, sem)
+ } else if (this.$refs.notenTable?.tabulator) {
+ this.$refs.notenTable.tabulator.setData([]) // no matching LV -> clear
+ }
+ }).finally(() => this.loading = false)
},
getOptionLabel(option) {
return option.studiensemester_kurzbz
@@ -1644,6 +1675,8 @@ export const Benotungstool = {
const typ = this.pruefung ? this.pruefung.pruefungstyp_kurzbz : this.getPruefungstypForStudentByAntritt(this.pruefungStudent)
const note = this.selectedPruefungNote?.note ?? 9 // noch nicht eingetragen
const punkte = this.selectedPruefungPunkte ?? 0
+
+ this.loading = true
this.$api.call(ApiNoten.saveStudentPruefung(
this.pruefungStudent.uid,
note,
@@ -1701,6 +1734,7 @@ export const Benotungstool = {
}).finally(()=> {
this.pruefungStudent = null
this.pruefung = null
+ this.loading = false
})
this.$refs.modalContainerPruefung.hide()
@@ -1846,6 +1880,7 @@ export const Benotungstool = {
})
},
saveNoteneingabe() {
+ this.loading = true
this.$api.call(ApiNoten.saveStudentenNoten(this.password, this.changedNoten, this.lv_id, this.sem_kurzbz))
.then((res) => {
if(res.meta.status === 'success') {
@@ -1866,6 +1901,8 @@ export const Benotungstool = {
this.changedNotenCounter++;
this.$refs.notenTable.tabulator.redraw(true)
+ }).finally(() => {
+ this.loading = false
})
this.$refs.modalContainerNotenSpeichern.hide()
@@ -1882,9 +1919,6 @@ export const Benotungstool = {
getOptionLabelNotePruefung(option) {
return option.bezeichnung
},
- leChanged(e) {
- this.selectedLehreinheit = e.value
- },
addPruefung(){
this.$refs.modalContainerNeuesPruefungsdatum.hide()
@@ -2041,9 +2075,16 @@ export const Benotungstool = {
},
selectedLehreinheit(newVal) {
if(!this.$refs.notenTable) return
- this.$refs.notenTable.tabulator.clearFilter();
+ this.$refs.notenTable.tabulator.getFilters()
+ .filter(f => f.field === 'lehreinheit_id')
+ .forEach(() => table.removeFilter("lehreinheit_id", "=", undefined))
if(newVal) this.$refs.notenTable.tabulator.setFilter("lehreinheit_id", "=", newVal.lehreinheit_id);
},
+ selectedLehrveranstaltung(newVal, oldVal) {
+ if (this.selectedLehreinheit) {
+ this.selectedLehreinheit = null
+ }
+ },
getKommPruefCount(newVal) {
if(!this.config.CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF) return 0
if(this.$refs.notenTable?.tabulator && newVal > 0) {
@@ -2056,6 +2097,16 @@ export const Benotungstool = {
}
},
computed: {
+ countsToHTML() {
+ return this.$p.t('global/ausgewaehlt')
+ + ': ' + (this.selectedUids.length || 0) + ''
+ + ' | '
+ + this.$p.t('global/gefiltert')
+ + ': ' + (this.filteredcount || 0) + ''
+ + ' | '
+ + this.$p.t('global/gesamt')
+ + ': ' + (this.studenten?.length || 0) + '';
+ },
maxAntrittCount() {
let maxAntritte = 1;
@@ -2077,9 +2128,9 @@ export const Benotungstool = {
},
LeDropdownParams() {
return {
- lv_id: this.lv_id,
- sem_kurzbz: this.sem_kurzbz
- }
+ lv_id: this.selectedLehrveranstaltung?.lehrveranstaltung_id ?? null,
+ sem_kurzbz: this.selectedSemester?.studiensemester_kurzbz ?? null
+ }
},
getStudentenOptions() {
return this.studenten ? this.studenten.filter(s => s.selectable) : []
@@ -2128,14 +2179,16 @@ export const Benotungstool = {
this.setupMounted()
},
template: `
-
+
{{$capitalize($p.t('benotungstool/c4notenImportieren'))}}
-
-
+
-
-
+
@@ -2143,13 +2196,12 @@ export const Benotungstool = {
-
+ @showBsModal="neuesPruefungsdatumModalVisible = true">
{{$capitalize($p.t('benotungstool/c4addNewPruefung'))}}
-
+
{{$capitalize($p.t('benotungstool/c4date'))}}:
-
-
+
+
{{$capitalize($p.t('benotungstool/prueflingSelectionv2'))}}:
-
-
-
+
-
+
{{ $p.t('benotungstool/noteneingabeSpeichern') }}
-
-
+
-
+
@@ -2200,11 +2251,11 @@ export const Benotungstool = {
-
+
{{ pruefung ? $capitalize($p.t('benotungstool/editPruefungFor')) : $capitalize($p.t('benotungstool/createPruefungFor')) }} {{pruefungStudent?.vorname}} {{pruefungStudent?.nachname}}
-
-
{{$capitalize($p.t('benotungstool/c4date'))}}:
+
+
{{$capitalize($p.t('benotungstool/c4date'))}}:
-
-
{{$capitalize($p.t('benotungstool/c4punkte'))}}:
+
+
{{$capitalize($p.t('benotungstool/c4punkte'))}}:
-
+ inputId="selectedPruefungInput" :min="0" :max="100000"
+ class="w-100">
-
-
{{$capitalize($p.t('lehre/note'))}}:
+
+
{{$capitalize($p.t('lehre/note'))}}:
-
{{ option.bezeichnung }}
@@ -2243,7 +2295,7 @@ export const Benotungstool = {
-
+
-
-
+
+
{{$capitalize($p.t('benotungstool/benotungstoolTitle'))}}
- {{ lv?.bezeichnung }}
+ {{ selectedLehrveranstaltung?.lv_bezeichnung }}
+
+
+
+
-
-
-
- {{ option.fullString }}
-
-
-
+
+
+ {{ option.fullString }}
+
+
-
-
-
-
-
-
- {{ slotProps.option.infoString }}
-
- {{ slotProps.option.studentcount }}
-
- {{ slotProps.option.termincount }}
-
-
-
-
+
+
+
-
-
-
-
- {{ option.studiensemester_kurzbz }}
-
-
-
+
+
+
+ {{ slotProps.option.infoString }}
+
+ {{ slotProps.option.studentcount }}
+
+ {{ slotProps.option.termincount }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ option.studiensemester_kurzbz }}
+
+
@@ -2315,6 +2371,8 @@ export const Benotungstool = {