yellow dropdown styling only on editable tabulator colums for note_vorschlag; fetch note for punkte for notenvorschlag and pruefungsnote if certain config is set; added debounce helper file/function; WIP persisting punkte in backend

This commit is contained in:
Johann Hoffmann
2026-02-02 17:07:16 +01:00
parent 390a3c0d5a
commit 81eee814e9
6 changed files with 179 additions and 63 deletions
@@ -38,7 +38,8 @@ class Noten extends FHCAPI_Controller
'createPruefungen' => array('lehre/benotungstool:rw'), 'createPruefungen' => array('lehre/benotungstool:rw'),
'saveNotenvorschlagBulk' => array('lehre/benotungstool:rw'), 'saveNotenvorschlagBulk' => array('lehre/benotungstool:rw'),
'savePruefungenBulk' => array('lehre/benotungstool:rw'), 'savePruefungenBulk' => array('lehre/benotungstool:rw'),
'getCisConfig' => array('lehre/benotungstool:rw') 'getCisConfig' => array('lehre/benotungstool:rw'),
'getNoteByPunkte' => array('lehre/benotungstool:rw')
]); ]);
$this->load->library('AuthLib', null, 'AuthLib'); $this->load->library('AuthLib', null, 'AuthLib');
@@ -1046,5 +1047,26 @@ class Noten extends FHCAPI_Controller
} }
public function getNoteByPunkte() {
$result = $this->getPostJSON();
// TODO validate post properly
if(!property_exists($result, 'punkte')
|| !property_exists($result, 'lv_id')
|| !property_exists($result, 'sem_kurzbz')) {
$this->terminateWithError($this->p->t('global', 'missingParameters'), 'general');
}
$punkte = $result->punkte;
$lv_id = $result->lv_id;
$sem_kurzbz = $result->sem_kurzbz;
$result = $this->NotenschluesselaufteilungModel->getNote($punkte, $lv_id, $sem_kurzbz);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
} }
+2 -2
View File
@@ -24,13 +24,13 @@
/* styling for editable dropdown column of notenvorschläge in benotungstool*/ /* styling for editable dropdown column of notenvorschläge in benotungstool*/
#notentable .tabulator-tableholder .tabulator-editable { #notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"] {
position: relative; position: relative;
background-color: rgba(255, 255, 157, 0.73); background-color: rgba(255, 255, 157, 0.73);
cursor: pointer; cursor: pointer;
} }
#notentable .tabulator-tableholder .tabulator-editable::after { #notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"]::after {
content: "▾"; content: "▾";
position: absolute; position: absolute;
right: 6px; right: 6px;
+8 -1
View File
@@ -76,5 +76,12 @@ export default {
url: '/api/frontend/v1/Noten/savePruefungenBulk', url: '/api/frontend/v1/Noten/savePruefungenBulk',
params: { lv_id, sem_kurzbz, pruefungen } params: { lv_id, sem_kurzbz, pruefungen }
}; };
},
getNoteByPunkte(punkte, lv_id, sem_kurzbz) {
return {
method: 'post',
url: '/api/frontend/v1/Noten/getNoteByPunkte',
params: { punkte, lv_id, sem_kurzbz }
};
} }
}; }
@@ -8,6 +8,7 @@ import VueDatePicker from '../../vueDatepicker.js.php';
import LehreinheitenModule from '../../DropdownModes/LehreinheitenModule'; import LehreinheitenModule from '../../DropdownModes/LehreinheitenModule';
import MobilityLegende from '../../Mobility/Legende.js'; import MobilityLegende from '../../Mobility/Legende.js';
import FhcOverlay from "../../Overlay/FhcOverlay.js"; import FhcOverlay from "../../Overlay/FhcOverlay.js";
import {debounce} from "../../../helpers/debounce.js";
export const Benotungstool = { export const Benotungstool = {
name: "Benotungstool", name: "Benotungstool",
@@ -18,6 +19,7 @@ export const Benotungstool = {
MobilityLegende, MobilityLegende,
Dropdown: primevue.dropdown, Dropdown: primevue.dropdown,
Divider: primevue.divider, Divider: primevue.divider,
InputNumber: primevue.inputnumber,
Password: primevue.password, Password: primevue.password,
Textarea: primevue.textarea, Textarea: primevue.textarea,
Datepicker: VueDatePicker, Datepicker: VueDatePicker,
@@ -44,6 +46,7 @@ export const Benotungstool = {
}, },
data() { data() {
return { return {
debouncedFetchPunkteForPruefung: null,
config: null, // cis config config: null, // cis config
neuesPruefungsdatumModalVisible: false, neuesPruefungsdatumModalVisible: false,
loading: false, loading: false,
@@ -52,6 +55,7 @@ export const Benotungstool = {
tabulatorCanBeBuilt: false, tabulatorCanBeBuilt: false,
selectedPruefungNote: null, selectedPruefungNote: null,
selectedPruefungDate: new Date(), // v-model for pruefung edit datepicker selectedPruefungDate: new Date(), // v-model for pruefung edit datepicker
selectedPruefungPunkte: null,
distinctPruefungsDates: null, distinctPruefungsDates: null,
pruefungStudent: null, pruefungStudent: null,
pruefung: null, pruefung: null,
@@ -115,6 +119,15 @@ export const Benotungstool = {
const row = cell.getRow() const row = cell.getRow()
row.reformat() // trigger reformat of arrow row.reformat() // trigger reformat of arrow
} else if (field === 'punkte') {
const newValue = cell.getValue();
if(newValue == '' || newValue == null) return
this.$api.call(ApiNoten.getNoteByPunkte(newValue, this.lv_id, this.sem_kurzbz)).then(res => {
if(res?.meta?.status === 'success' && res.data >= 0) {
const row = cell.getRow();
row.update({note_vorschlag: res.data})
}
})
} }
} }
}, },
@@ -132,6 +145,13 @@ export const Benotungstool = {
]}; ]};
}, },
methods: { methods: {
fetchNoteForPunktePruefung(event) {
this.$api.call(ApiNoten.getNoteByPunkte(event.value, this.lv_id, this.sem_kurzbz)).then(res => {
if(res?.meta?.status === 'success' && res.data >= 0) {
this.selectedPruefungNote = this.notenOptions.find(n => n.note == res.data)
}
})
},
isValidDate_ddmmyyyy(str) { isValidDate_ddmmyyyy(str) {
if (typeof str !== 'string') return false; if (typeof str !== 'string') return false;
@@ -273,7 +293,7 @@ export const Benotungstool = {
this.$fhcAlert.alertDefault( this.$fhcAlert.alertDefault(
'success', 'success',
'Info', 'Info',
this.$p.t('benotungstool/notenImportSuccessAlert'), this.$capitalize(this.$p.t('benotungstool/notenImportSuccessAlert')),
true true
) )
const lvNoten = res.data const lvNoten = res.data
@@ -309,7 +329,7 @@ export const Benotungstool = {
this.$fhcAlert.alertDefault( this.$fhcAlert.alertDefault(
'success', 'success',
'Info', 'Info',
this.$p.t('benotungstool/pruefungImportSuccessAlert'), this.$capitalize(this.$p.t('benotungstool/pruefungImportSuccessAlert')),
true true
) )
this.handleAddNewPruefungenResponse(res, pruefungenbulk) this.handleAddNewPruefungenResponse(res, pruefungenbulk)
@@ -453,7 +473,7 @@ export const Benotungstool = {
virtualDom: false, virtualDom: false,
index: 'uid', index: 'uid',
layout: 'fitDataStretch', layout: 'fitDataStretch',
placeholder: this.$p.t('global/noDataAvailable'), placeholder: this.$capitalize(this.$p.t('global/noDataAvailable')),
selectable: true, selectable: true,
selectableRangeMode: "click", // shift+click selectableRangeMode: "click", // shift+click
selectablePersistence: false, // reset selection on table reload selectablePersistence: false, // reset selection on table reload
@@ -515,14 +535,30 @@ export const Benotungstool = {
cssClass: 'sticky-col' cssClass: 'sticky-col'
}, },
{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.$capitalize(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.$capitalize(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.$capitalize(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.$capitalize(this.$p.t('benotungstool/c4nachname'))), field: 'nachname', headerFilter: true, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('benotungstool/c4anwesenheitsquote')), field: 'anwquote', widthGrow: 1, formatter: this.percentFormatter}, {title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4anwesenheitsquote'))), field: 'anwquote', widthGrow: 1, formatter: this.percentFormatter},
{title: Vue.computed(() => this.$p.t('benotungstool/c4mobility')), field: 'mobility_zusatz', headerFilter: true, widthGrow: 1, visible: false}, {title: Vue.computed(() => this.$capitalize(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.$capitalize(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', {title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4punkte'))), field: 'punkte', widthGrow: 1,
editor: 'number',
editorParams: (cell) => {
return {
min: 0,
max: 9999,
step: 1,
elementAttributes: {
maxlength: "4"
},
selectContents: true,
verticalNavigation: "table"
}
},
variableHeight: true
},
{title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4notenvorschlag'))), field: 'note_vorschlag',
editor: 'list', editor: 'list',
editorParams: (cell) => { editorParams: (cell) => {
// write original cell value into row to it can be retrieved if edit is cancelled without selection // write original cell value into row to it can be retrieved if edit is cancelled without selection
@@ -537,6 +573,9 @@ export const Benotungstool = {
}; };
}, },
editable: (cell) => { editable: (cell) => {
// TODO: css style this a bit
// punkte features enables mapping but unable to set note directly
if(this.config?.CIS_GESAMTNOTE_PUNKTE) return false
const rowData = cell.getRow().getData(); const rowData = cell.getRow().getData();
const noteOption = this.notenOptions.find(opt => opt.note == rowData.note) const noteOption = this.notenOptions.find(opt => opt.note == rowData.note)
if(!noteOption) return true if(!noteOption) return true
@@ -561,7 +600,7 @@ export const Benotungstool = {
widthGrow: 1 widthGrow: 1
}, },
{title: '', width: 50, hozAlign: 'center', formatter: this.arrowFormatter, cellClick: this.saveNote, variableHeight: true}, {title: '', width: 50, hozAlign: 'center', formatter: this.arrowFormatter, cellClick: this.saveNote, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4lvnote')), field: 'lv_note', {title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4lvnote'))), field: 'lv_note',
formatter: this.notenFormatter, formatter: this.notenFormatter,
headerFilter: 'list', headerFilter: 'list',
headerFilterParams: () => { headerFilterParams: () => {
@@ -570,8 +609,8 @@ export const Benotungstool = {
headerFilterFunc: this.notenFilterFunc, headerFilterFunc: this.notenFilterFunc,
widthGrow: 1 widthGrow: 1
}, },
{title: Vue.computed(() => this.$p.t('benotungstool/c4freigabe')), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter, variableHeight: true}, {title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4freigabe'))), field: 'freigegeben', widthGrow: 1, formatter: this.freigabeFormatter, variableHeight: true},
{title: Vue.computed(() => this.$p.t('benotungstool/c4zeugnisnote')), {title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4zeugnisnote'))),
field: 'note', field: 'note',
formatter: this.notenFormatter, formatter: this.notenFormatter,
topCalc: this.negativeNotenCalc, topCalc: this.negativeNotenCalc,
@@ -583,7 +622,7 @@ export const Benotungstool = {
headerFilterFunc: this.notenFilterFunc, headerFilterFunc: this.notenFilterFunc,
widthGrow: 1 widthGrow: 1
}, },
{title: Vue.computed(() => this.$p.t('benotungstool/c4kommPruef')), {title: Vue.computed(() => this.$capitalize(this.$p.t('benotungstool/c4kommPruef'))),
field: 'kommPruef', widthGrow: 1, field: 'kommPruef', widthGrow: 1,
formatter: this.pruefungFormatter, formatter: this.pruefungFormatter,
topCalc: this.terminCalcFunc, topCalc: this.terminCalcFunc,
@@ -643,11 +682,11 @@ export const Benotungstool = {
}, },
terminCalcFormatter(cell) { terminCalcFormatter(cell) {
const cellval = cell.getValue() const cellval = cell.getValue()
return this.$p.t('benotungstool/prueflingSelectionv2')+': ' + cellval return this.$capitalize(this.$p.t('benotungstool/prueflingSelectionv2'))+': ' + cellval
}, },
negativeNotenCalcFormatter(cell) { negativeNotenCalcFormatter(cell) {
const cellval = cell.getValue() const cellval = cell.getValue()
return this.$p.t('benotungstool/c4negativ')+': ' + cellval return this.$capitalize(this.$p.t('benotungstool/c4negativ'))+': ' + cellval
}, },
negativeNotenCalc(entries) { negativeNotenCalc(entries) {
return entries.reduce((acc, cur) => { return entries.reduce((acc, cur) => {
@@ -673,18 +712,18 @@ export const Benotungstool = {
} }
// specific searchterm cases // specific searchterm cases
if(filterVal === this.$p.t('benotungstool/c4positiv')) { if(filterVal === this.$capitalize(this.$p.t('benotungstool/c4positiv'))) {
// option of the rowValue // option of the rowValue
const valOpt = this.notenOptions.find(opt => opt.note == rowVal) const valOpt = this.notenOptions.find(opt => opt.note == rowVal)
if(!valOpt) return false if(!valOpt) return false
return valOpt.positiv return valOpt.positiv
} }
if(filterVal === this.$p.t('benotungstool/c4negativ')) { if(filterVal === this.$capitalize(this.$p.t('benotungstool/c4negativ'))) {
const valOpt = this.notenOptions.find(opt => opt.note == rowVal) const valOpt = this.notenOptions.find(opt => opt.note == rowVal)
if(!valOpt) return false if(!valOpt) return false
return !valOpt.positiv return !valOpt.positiv
} }
if(filterVal === this.$p.t('benotungstool/c4noteEmpty') && rowVal === null) { if(filterVal === this.$capitalize(this.$p.t('benotungstool/c4noteEmpty')) && rowVal === null) {
return true return true
} }
@@ -771,6 +810,9 @@ export const Benotungstool = {
}).finally(()=>this.loading = false) }).finally(()=>this.loading = false)
},
punkteFormatter(cell) {
}, },
teilnotenFormatter(cell) { teilnotenFormatter(cell) {
const val = cell.getValue() const val = cell.getValue()
@@ -871,7 +913,7 @@ export const Benotungstool = {
// Third column (button) // Third column (button)
const button = document.createElement('button'); const button = document.createElement('button');
button.className = 'btn btn-outline-secondary'; button.className = 'btn btn-outline-secondary';
button.textContent = this.$p.t('benotungstool/changePruefungButtonText'); button.textContent = this.$capitalize(this.$p.t('benotungstool/changePruefungButtonText'));
button.addEventListener('click', () => { button.addEventListener('click', () => {
this.openPruefungModal(data, data[field], field); this.openPruefungModal(data, data[field], field);
}); });
@@ -888,7 +930,7 @@ export const Benotungstool = {
const button = document.createElement('button'); const button = document.createElement('button');
button.className = 'btn btn-outline-secondary'; button.className = 'btn btn-outline-secondary';
button.textContent = this.$p.t('benotungstool/addPruefungButtonText'); button.textContent = this.$capitalize(this.$p.t('benotungstool/addPruefungButtonText'));
button.addEventListener('click', () => { button.addEventListener('click', () => {
this.openPruefungModal(data, null, field) this.openPruefungModal(data, null, field)
}); });
@@ -915,8 +957,7 @@ export const Benotungstool = {
// newDate.setDate(+pruefungDateParts[2]) // newDate.setDate(+pruefungDateParts[2])
// works correctly // works correctly
const newDate = new Date(+pruefungDateParts[0], +pruefungDateParts[1], +pruefungDateParts[2]) const newDate = new Date(Number(pruefungDateParts[0]), Number(pruefungDateParts[1]) - 1, Number(pruefungDateParts[2]))
newDate.setMonth(newDate.getMonth() - 1)
this.selectedPruefungDate = newDate this.selectedPruefungDate = newDate
@@ -926,11 +967,13 @@ export const Benotungstool = {
this.selectedPruefungNote = null this.selectedPruefungNote = null
} }
this.selectedPruefungPunkte = this.pruefung?.punkte ?? null
this.$refs.modalContainerPruefung.show() this.$refs.modalContainerPruefung.show()
}, },
pruefungTitleFormatter(cell) { pruefungTitleFormatter(cell) {
const def = cell.getColumn().getDefinition() const def = cell.getColumn().getDefinition()
if(def.originalNote) return this.$p.t('benotungstool/c4originalZnote') if(def.originalNote) return this.$capitalize(this.$p.t('benotungstool/c4originalZnote'))
return def.title; return def.title;
}, },
arrowFormatter(cell) { arrowFormatter(cell) {
@@ -1131,10 +1174,13 @@ export const Benotungstool = {
this.notenTableOptions.height = window.visualViewport.height - rect.top - 50 this.notenTableOptions.height = window.visualViewport.height - rect.top - 50
this.$refs.notenTable.tabulator.setHeight(this.notenTableOptions.height) this.$refs.notenTable.tabulator.setHeight(this.notenTableOptions.height)
}, },
setupCreated() { async setupCreated() {
this.loading = true this.loading = true
// fetch cis config regarding gesamtnoteneingabe
this.$api.call(ApiNoten.getCisConfig()).then(res => { this.debouncedFetchPunkteForPruefung = debounce(this.fetchNoteForPunktePruefung, 500)
// fetch cis config regarding gesamtnoteneingabe, needs to be fetched before setup can finish
const configPromise = this.$api.call(ApiNoten.getCisConfig()).then(res => {
this.config = res.data this.config = res.data
}) })
@@ -1160,9 +1206,11 @@ export const Benotungstool = {
}) })
// fetch noten dropdown // fetch noten dropdown
this.$api.call(ApiNoten.getNoten()).then(res => { this.$api.call(ApiNoten.getNoten()).then(async res => {
this.notenOptions = res.data this.notenOptions = res.data
this.notenOptionsLehre = res.data.filter(n => n.lehre === true) this.notenOptionsLehre = res.data.filter(n => n.lehre === true)
await configPromise
this.notenTableOptions = this.getNotenTableOptions() this.notenTableOptions = this.getNotenTableOptions()
this.tabulatorCanBeBuilt = true // because promises would be more work and not much better here this.tabulatorCanBeBuilt = true // because promises would be more work and not much better here
}).catch(e => { }).catch(e => {
@@ -1254,10 +1302,12 @@ export const Benotungstool = {
const typ = this.pruefung ? this.pruefung.pruefungstyp_kurzbz : this.getPruefungstypForStudentByAntritt(this.pruefungStudent) const typ = this.pruefung ? this.pruefung.pruefungstyp_kurzbz : this.getPruefungstypForStudentByAntritt(this.pruefungStudent)
const note = this.selectedPruefungNote?.note ?? 9 // noch nicht eingetragen const note = this.selectedPruefungNote?.note ?? 9 // noch nicht eingetragen
// TODO: check if this is supposed to work this way
const punkte = this.selectedPruefungPunkte ?? 0
this.$api.call(ApiNoten.saveStudentPruefung( this.$api.call(ApiNoten.saveStudentPruefung(
this.pruefungStudent.uid, this.pruefungStudent.uid,
note, note,
this.pruefung?.punkte ?? '', punkte,
dateStr, dateStr,
this.lv_id, this.lv_id,
this.pruefungStudent.lehreinheit_id, this.pruefungStudent.lehreinheit_id,
@@ -1268,7 +1318,7 @@ export const Benotungstool = {
this.$fhcAlert.alertDefault( this.$fhcAlert.alertDefault(
'success', 'success',
'Info', 'Info',
this.$p.t('benotungstool/pruefungSaveForUid', [this.pruefungStudent.uid]), this.$capitalize(this.$p.t('benotungstool/pruefungSaveForUid', [this.pruefungStudent.uid])),
true true
) )
const s = this.studenten.find(s => s.uid === res.data[1]?.student_uid) const s = this.studenten.find(s => s.uid === res.data[1]?.student_uid)
@@ -1481,7 +1531,7 @@ export const Benotungstool = {
if(uidListError != '') { if(uidListError != '') {
this.$fhcAlert.alertError( this.$fhcAlert.alertError(
this.$p.t('benotungstool/c4pruefungAnlageError', [dateStrFront]) + ': ' + uidListError + ' ' this.$capitalize(this.$p.t('benotungstool/c4pruefungAnlageError', [dateStrFront])) + ': ' + uidListError + ' '
) )
} }
@@ -1489,7 +1539,7 @@ export const Benotungstool = {
this.$fhcAlert.alertDefault( this.$fhcAlert.alertDefault(
'success', 'success',
'Info', 'Info',
this.$p.t('benotungstool/pruefungAngelegtAn', [dateStrFront]) + ': ' + uidListSuccess, this.$capitalize(this.$p.t('benotungstool/pruefungAngelegtAn', [dateStrFront])) + ': ' + uidListSuccess,
true true
) )
@@ -1622,10 +1672,10 @@ export const Benotungstool = {
return cs return cs
}, },
getNotenfreigabeHinweistext() { getNotenfreigabeHinweistext() {
return this.$p.t('benotungstool/notenfreigabeHinweistextv3') return this.$capitalize(this.$p.t('benotungstool/notenfreigabeHinweistextv3'))
}, },
getNotenimportHinweistext() { getNotenimportHinweistext() {
return this.$p.t('benotungstool/notenimportHinweistextv3') return this.$capitalize(this.$p.t('benotungstool/notenimportHinweistextv3'))
} }
}, },
created() { created() {
@@ -1636,7 +1686,7 @@ export const Benotungstool = {
}, },
template: ` template: `
<bs-modal ref="modalContainerNotenImport" class="bootstrap-prompt" dialogClass="modal-lg"> <bs-modal ref="modalContainerNotenImport" class="bootstrap-prompt" dialogClass="modal-lg">
<template v-slot:title>{{$p.t('benotungstool/c4notenImportieren')}}</template> <template v-slot:title>{{$capitalize($p.t('benotungstool/c4notenImportieren'))}}</template>
<template v-slot:default> <template v-slot:default>
<div class="row mt-4 justify-content-center"> <div class="row mt-4 justify-content-center">
<div v-html="getNotenimportHinweistext"></div> <div v-html="getNotenimportHinweistext"></div>
@@ -1646,7 +1696,7 @@ export const Benotungstool = {
</div> </div>
</template> </template>
<template v-slot:footer> <template v-slot:footer>
<button type="button" class="btn btn-primary" @click="importNoten">{{ $p.t('benotungstool/c4import') }}</button> <button type="button" class="btn btn-primary" @click="importNoten">{{ $capitalize($p.t('benotungstool/c4import')) }}</button>
</template> </template>
</bs-modal> </bs-modal>
@@ -1654,10 +1704,10 @@ export const Benotungstool = {
@hideBsModal="neuesPruefungsdatumModalVisible = false" @hideBsModal="neuesPruefungsdatumModalVisible = false"
@showBsModal="neuesPruefungsdatumModalVisible = true" @showBsModal="neuesPruefungsdatumModalVisible = true"
> >
<template v-slot:title>{{$p.t('benotungstool/c4addNewPruefung')}}</template> <template v-slot:title>{{$capitalize($p.t('benotungstool/c4addNewPruefung'))}}</template>
<template v-slot:default> <template v-slot:default>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-3 text-center">{{$p.t('benotungstool/c4date')}}:</div> <div class="col-3 text-center">{{$capitalize($p.t('benotungstool/c4date'))}}:</div>
<div class="col-6"> <div class="col-6">
<datepicker <datepicker
v-model="selectedPruefungDate" v-model="selectedPruefungDate"
@@ -1671,7 +1721,7 @@ export const Benotungstool = {
</div> </div>
<div class="row mt-4 justify-content-center"> <div class="row mt-4 justify-content-center">
<div class="col-3 text-center">{{$p.t('benotungstool/prueflingSelectionv2')}}:</div> <div class="col-3 text-center">{{$capitalize($p.t('benotungstool/prueflingSelectionv2'))}}:</div>
<div class="col-6"> <div class="col-6">
<Multiselect <Multiselect
v-model="selectedUids" v-model="selectedUids"
@@ -1686,7 +1736,7 @@ export const Benotungstool = {
</template> </template>
<template v-slot:footer> <template v-slot:footer>
<button type="button" class="btn btn-primary" @click="addPruefung">{{ $p.t('benotungstool/c4addNewPruefung') }}</button> <button type="button" class="btn btn-primary" @click="addPruefung">{{ $capitalize($p.t('benotungstool/c4addNewPruefung')) }}</button>
</template> </template>
</bs-modal> </bs-modal>
@@ -1708,10 +1758,10 @@ export const Benotungstool = {
</bs-modal> </bs-modal>
<bs-modal ref="modalContainerPruefung" class="bootstrap-prompt" dialogClass="modal-lg"> <bs-modal ref="modalContainerPruefung" class="bootstrap-prompt" dialogClass="modal-lg">
<template v-slot:title>{{ pruefung ? $p.t('benotungstool/editPruefungFor') : $p.t('benotungstool/createPruefungFor') }} {{pruefungStudent?.vorname}} {{pruefungStudent?.nachname}}</template> <template v-slot:title>{{ pruefung ? $capitalize($p.t('benotungstool/editPruefungFor')) : $capitalize($p.t('benotungstool/createPruefungFor')) }} {{pruefungStudent?.vorname}} {{pruefungStudent?.nachname}}</template>
<template v-slot:default> <template v-slot:default>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-1 text-center">{{$p.t('benotungstool/c4date')}}:</div> <div class="col-1 text-center">{{$capitalize($p.t('benotungstool/c4date'))}}:</div>
<div class="col-6"> <div class="col-6">
<datepicker <datepicker
v-model="selectedPruefungDate" v-model="selectedPruefungDate"
@@ -1723,10 +1773,21 @@ export const Benotungstool = {
</datepicker> </datepicker>
</div> </div>
</div> </div>
<div class="row justify-content-center mt-4"> <div v-if="config?.CIS_GESAMTNOTE_PUNKTE == true" class="row justify-content-center mt-4">
<div class="col-1 text-center">{{$p.t('lehre/note')}}:</div> <div class="col-1 text-center">{{$capitalize($p.t('benotungstool/c4punkte'))}}:</div>
<div class="col-6"> <div class="col-6">
<Dropdown :placeholder="$p.t('lehre/note')" <InputNumber
v-model="selectedPruefungPunkte"
@input="debouncedFetchPunkteForPruefung"
inputId="selectedPruefungInput" :min="0" :max="100000">
</InputNumber>
</div>
</div>
<div class="row justify-content-center mt-4">
<div class="col-1 text-center">{{$capitalize($p.t('lehre/note'))}}:</div>
<div class="col-6">
<Dropdown :placeholder="$capitalize($p.t('lehre/note'))"
:disabled="config?.CIS_GESAMTNOTE_PUNKTE == true"
:style="{'width': '100%'}" :optionLabel="getOptionLabelNotePruefung" :style="{'width': '100%'}" :optionLabel="getOptionLabelNotePruefung"
v-model="selectedPruefungNote" :options="notenOptionsLehre" showClear> v-model="selectedPruefungNote" :options="notenOptionsLehre" showClear>
<template #optionsgroup="slotProps"> <template #optionsgroup="slotProps">
@@ -1737,7 +1798,7 @@ export const Benotungstool = {
</div> </div>
</template> </template>
<template v-slot:footer> <template v-slot:footer>
<button type="button" class="btn btn-primary" @click="savePruefungEingabe">{{ $p.t('global/speichern') }}</button> <button type="button" class="btn btn-primary" @click="savePruefungEingabe">{{ $capitalize($p.t('global/speichern')) }}</button>
</template> </template>
</bs-modal> </bs-modal>
@@ -1762,13 +1823,13 @@ export const Benotungstool = {
<div class="row"> <div class="row">
<div class="col-4"> <div class="col-4">
<h2>{{$p.t('benotungstool/benotungstoolTitle')}}</h2> <h2>{{$capitalize($p.t('benotungstool/benotungstoolTitle'))}}</h2>
<h4>{{ lv?.bezeichnung }}</h4> <h4>{{ lv?.bezeichnung }}</h4>
</div> </div>
<div class="col-2"> <div class="col-2">
<div class="col-lg-auto"> <div class="col-lg-auto">
<Dropdown @change="lvChanged" :style="{'width': '100%'}" :optionLabel="getOptionLabelLv" <Dropdown @change="lvChanged" :style="{'width': '100%'}" :optionLabel="getOptionLabelLv"
:placeholder="$p.t('lehre/lehrveranstaltung')" :placeholder="$capitalize($p.t('lehre/lehrveranstaltung'))"
v-model="selectedLehrveranstaltung" :options="lehrveranstaltungen" appendTo="self"> v-model="selectedLehrveranstaltung" :options="lehrveranstaltungen" appendTo="self">
<template #optionsgroup="slotProps"> <template #optionsgroup="slotProps">
<div> {{ option.fullString }} </div> <div> {{ option.fullString }} </div>
@@ -1821,21 +1882,21 @@ export const Benotungstool = {
<template #actions> <template #actions>
<button @click="openNewPruefungsdatumModal" role="button" :class="getNewBtnClass"> <button @click="openNewPruefungsdatumModal" role="button" :class="getNewBtnClass">
{{$p.t('benotungstool/c4addNewPruefung')}} <i class="fa fa-plus"></i> {{$capitalize($p.t('benotungstool/c4addNewPruefung'))}} <i class="fa fa-plus"></i>
</button> </button>
<Divider layout="vertical" style="transform: translateY(12px)"/> <Divider layout="vertical" style="transform: translateY(12px)"/>
<button @click="openNotenImportModal" role="button" :class="getNotenImportBtnClass"> <button @click="openNotenImportModal" role="button" :class="getNotenImportBtnClass">
{{$p.t('benotungstool/c4notenImportieren')}} <i class="fa fa-file-import"></i> {{$capitalize($p.t('benotungstool/c4notenImportieren'))}} <i class="fa fa-file-import"></i>
</button> </button>
<button @click="openSaveModal" role="button" :class="getSaveBtnClass"> <button @click="openSaveModal" role="button" :class="getSaveBtnClass">
{{$p.t('benotungstool/approveGrades')}} <i class="fa fa-save"></i> {{$capitalize($p.t('benotungstool/approveGrades'))}} <i class="fa fa-save"></i>
</button> </button>
<Divider layout="vertical" style="transform: translateY(12px)"/> <Divider layout="vertical" style="transform: translateY(12px)"/>
<h4>{{ getFreigabeCounter > 0 ? $p.t('benotungstool/freigabecounterPositiv', [getFreigabeCounter]) : '' }}</h4> <h4>{{ getFreigabeCounter > 0 ? $capitalize($p.t('benotungstool/freigabecounterPositiv', [getFreigabeCounter])) : '' }}</h4>
</template> </template>
</core-filter-cmpt> </core-filter-cmpt>
</div> </div>
+9
View File
@@ -0,0 +1,9 @@
export function debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn(...args)
}, delay);
};
}
+23 -6
View File
@@ -50878,18 +50878,18 @@ and represent the current state of research on the topic. The prescribed citatio
array( array(
'app' => 'core', 'app' => 'core',
'category' => 'benotungstool', 'category' => 'benotungstool',
'phrase' => 'c4note', 'phrase' => 'c4notenvorschlag',
'insertvon' => 'system', 'insertvon' => 'system',
'phrases' => array( 'phrases' => array(
array( array(
'sprache' => 'German', 'sprache' => 'German',
'text' => 'Punkte/Note', 'text' => 'Notenvorschlag',
'description' => '', 'description' => '',
'insertvon' => 'system' 'insertvon' => 'system'
), ),
array( array(
'sprache' => 'English', 'sprache' => 'English',
'text' => 'Points/Grade', 'text' => 'Grade Suggestion',
'description' => '', 'description' => '',
'insertvon' => 'system' 'insertvon' => 'system'
) )
@@ -51763,10 +51763,27 @@ and represent the current state of research on the topic. The prescribed citatio
) )
) )
), ),
array(
'app' => 'core',
'category' => 'benotungstool',
'phrase' => 'c4punkte',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Punkte',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Points',
'description' => '',
'insertvon' => 'system'
)
)
),
// CIS4 GESAMTNOTENEINGABE ENDE ------------------------------------------------------------------------------------ // CIS4 GESAMTNOTENEINGABE ENDE ------------------------------------------------------------------------------------
array( array(
'app' => 'core', 'app' => 'core',
'category' => 'lehre', 'category' => 'lehre',