mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-12 15:04:04 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de1c7e5be4 |
@@ -15,7 +15,11 @@ class Note_model extends DB_Model
|
||||
public function getAllActive() {
|
||||
$qry ="SELECT *
|
||||
FROM lehre.tbl_note
|
||||
WHERE aktiv = true";
|
||||
WHERE aktiv = true
|
||||
ORDER BY CASE WHEN note BETWEEN 1 AND 5 THEN 0 ELSE 1 END,
|
||||
CASE WHEN note BETWEEN 1 AND 5 THEN note END,
|
||||
bezeichnung";
|
||||
|
||||
|
||||
return $this->execReadOnlyQuery($qry);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export const AbgabetoolAssistenz = {
|
||||
qgate2FilterSelected: [],
|
||||
pa_noteFilterSelected: [],
|
||||
noteFilterSelected: [],
|
||||
note_bezFilterSelected: [],
|
||||
count: 0,
|
||||
filteredcount: 0,
|
||||
selectedcount: 0,
|
||||
@@ -211,7 +212,7 @@ export const AbgabetoolAssistenz = {
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4orgformv2'))), field: 'orgform', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 50, visible: false},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, minWidth: 150, visible: false},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 50, visible: false},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4note'))), field: 'note_bez', headerFilter: true, sorter: this.notenSorter, visible: false, minWidth: 200, formatter: this.centeredTextFormatter},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4note'))), field: 'note_bez', headerFilter: this.notenHeaderFilterEditor, headerFilterFunc: this.notenHeaderFilterFunc, headerFilterParams: {}, headerFilterFuncParams: { noteField: 'note' }, sorter: this.notenSorter, visible: false, minWidth: 200, formatter: this.centeredTextFormatter},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, visible: false, formatter: this.centeredTextFormatter, minWidth: 100},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100, visible: false},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerv2'))), field: 'erstbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100, visible: false},
|
||||
@@ -536,8 +537,6 @@ export const AbgabetoolAssistenz = {
|
||||
return aData.note - bData.note
|
||||
},
|
||||
notenHeaderFilterEditor(cell, onRendered, success, cancel, editorParams) {
|
||||
if (!this.notenOptions) return;
|
||||
|
||||
const field = cell.getField();
|
||||
const stateKey = field + 'FilterSelected';
|
||||
let selected = [...(this[stateKey] || [])];
|
||||
@@ -554,21 +553,16 @@ export const AbgabetoolAssistenz = {
|
||||
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
|
||||
|
||||
|
||||
// mapping evaluated at render time, not at column definition time
|
||||
const fieldOptionsMap = {
|
||||
'pa_note': this.notenOptions,
|
||||
'note': this.allowedNotenOptions,
|
||||
};
|
||||
const options = fieldOptionsMap[cell.getField()] ?? this.notenOptions;
|
||||
if (!options) return;
|
||||
|
||||
// the table is built before getNoten resolves, so resolve the options on open
|
||||
const getOptions = () => (field === 'note' ? this.allowedNotenOptions : this.notenOptions) ?? [];
|
||||
|
||||
const updateDisplay = () => {
|
||||
display.value = options
|
||||
display.value = getOptions()
|
||||
.filter(o => selected.includes(o.note))
|
||||
.map(o => o.bezeichnung)
|
||||
.join(', ');
|
||||
};
|
||||
options.forEach(opt => {
|
||||
const buildDropdown = () => dropdown.replaceChildren(...getOptions().map(opt => {
|
||||
const row = document.createElement('label');
|
||||
row.style.cssText = 'display: flex; align-items: center; gap: 6px; padding: 4px 8px; cursor: pointer; white-space: nowrap;';
|
||||
row.addEventListener('mousedown', e => e.preventDefault());
|
||||
@@ -592,13 +586,15 @@ export const AbgabetoolAssistenz = {
|
||||
|
||||
row.appendChild(cb);
|
||||
row.appendChild(labelText);
|
||||
dropdown.appendChild(row);
|
||||
});
|
||||
return row;
|
||||
}));
|
||||
|
||||
updateDisplay();
|
||||
|
||||
display.addEventListener('click', () => {
|
||||
if (dropdown.style.display === 'none') {
|
||||
buildDropdown();
|
||||
updateDisplay();
|
||||
const rect = display.getBoundingClientRect();
|
||||
dropdown.style.top = rect.bottom + 'px';
|
||||
dropdown.style.left = rect.left + 'px';
|
||||
@@ -621,9 +617,11 @@ export const AbgabetoolAssistenz = {
|
||||
},
|
||||
|
||||
notenHeaderFilterFunc(filterVal, rowVal, rowData, filterParams) {
|
||||
if (!filterVal || !filterVal.length) return true;
|
||||
// rowVal is the raw integer note id or a note object
|
||||
const noteId = typeof rowVal === 'object' ? rowVal?.note : rowVal;
|
||||
if (!Array.isArray(filterVal) || !filterVal.length) return true;
|
||||
// noteField: the column shows a text, the note id is in another field
|
||||
const noteVal = filterParams?.noteField ? rowData[filterParams.noteField] : rowVal;
|
||||
// noteVal is the raw integer note id or a note object
|
||||
const noteId = typeof noteVal === 'object' ? noteVal?.note : noteVal;
|
||||
return filterVal.some(val => val == noteId); // loose equality: filter vals are numbers, noteId might be string
|
||||
},
|
||||
handleFilterActiveChanged(active) {
|
||||
@@ -1496,6 +1494,8 @@ export const AbgabetoolAssistenz = {
|
||||
for (let hf of saved.headerFilters) {
|
||||
if (hf.field === 'qgate1Status') this.qgate1FilterSelected = hf.value || [];
|
||||
if (hf.field === 'qgate2Status') this.qgate2FilterSelected = hf.value || [];
|
||||
// older saved states hold a text filter here
|
||||
if (hf.field === 'note_bez') this.note_bezFilterSelected = Array.isArray(hf.value) ? hf.value : [];
|
||||
table.setHeaderFilterValue(hf.field, hf.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
selectedcount: 0,
|
||||
qgate1FilterSelected: [],
|
||||
qgate2FilterSelected: [],
|
||||
note_bezFilterSelected: [],
|
||||
abgabetypenBetreuer: null,
|
||||
detailIsFullscreen: false,
|
||||
phrasenPromise: null,
|
||||
@@ -145,7 +146,7 @@ export const AbgabetoolMitarbeiter = {
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4orgformv2'))), field: 'orgform', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 50, visible: false},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, minWidth: 100,visible: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 50, visible: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4note'))), field: 'note_bez', headerFilter: true, visible: false, sorter: this.notenSorter, minWidth: 200, formatter: this.centeredTextFormatter},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4note'))), field: 'note_bez', headerFilter: this.notenHeaderFilterEditor, headerFilterFunc: this.notenHeaderFilterFunc, headerFilterParams: {}, headerFilterFuncParams: { noteField: 'note' }, visible: false, sorter: this.notenSorter, minWidth: 200, formatter: this.centeredTextFormatter},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, visible: true, minWidth: 100},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100, width: 500, visible: true},
|
||||
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerv2'))), field: 'betreuer_full_name', headerFilter: true, formatter: this.centeredTextFormatter, minWidth: 100, visible: false},
|
||||
@@ -270,6 +271,94 @@ export const AbgabetoolMitarbeiter = {
|
||||
const bData = bRow.getData()
|
||||
return aData.note - bData.note
|
||||
},
|
||||
notenHeaderFilterEditor(cell, onRendered, success, cancel, editorParams) {
|
||||
const field = cell.getField();
|
||||
const stateKey = field + 'FilterSelected';
|
||||
let selected = [...(this[stateKey] || [])];
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.style.cssText = 'position: relative; width: 100%;';
|
||||
|
||||
const display = document.createElement('input');
|
||||
display.readOnly = true;
|
||||
display.placeholder = '';
|
||||
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
|
||||
|
||||
const dropdown = document.createElement('div');
|
||||
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
|
||||
|
||||
|
||||
// the table is built before getNoten resolves, so resolve the options on open
|
||||
const getOptions = () => this.notenOptions ?? [];
|
||||
|
||||
const updateDisplay = () => {
|
||||
display.value = getOptions()
|
||||
.filter(o => selected.includes(o.note))
|
||||
.map(o => o.bezeichnung)
|
||||
.join(', ');
|
||||
};
|
||||
const buildDropdown = () => dropdown.replaceChildren(...getOptions().map(opt => {
|
||||
const row = document.createElement('label');
|
||||
row.style.cssText = 'display: flex; align-items: center; gap: 6px; padding: 4px 8px; cursor: pointer; white-space: nowrap;';
|
||||
row.addEventListener('mousedown', e => e.preventDefault());
|
||||
|
||||
const cb = document.createElement('input');
|
||||
cb.type = 'checkbox';
|
||||
cb.value = opt.note;
|
||||
cb.checked = selected.includes(opt.note);
|
||||
cb.style.cssText = 'margin: 0 6px;';
|
||||
cb.addEventListener('change', () => {
|
||||
selected = cb.checked
|
||||
? [...selected, opt.note]
|
||||
: selected.filter(v => v !== opt.note);
|
||||
this[stateKey] = [...selected];
|
||||
updateDisplay();
|
||||
success([...selected]);
|
||||
});
|
||||
|
||||
const labelText = document.createElement('span');
|
||||
labelText.textContent = opt.bezeichnung;
|
||||
|
||||
row.appendChild(cb);
|
||||
row.appendChild(labelText);
|
||||
return row;
|
||||
}));
|
||||
|
||||
updateDisplay();
|
||||
|
||||
display.addEventListener('click', () => {
|
||||
if (dropdown.style.display === 'none') {
|
||||
buildDropdown();
|
||||
updateDisplay();
|
||||
const rect = display.getBoundingClientRect();
|
||||
dropdown.style.top = rect.bottom + 'px';
|
||||
dropdown.style.left = rect.left + 'px';
|
||||
dropdown.style.display = 'block';
|
||||
} else {
|
||||
dropdown.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
display.addEventListener('blur', () => {
|
||||
setTimeout(() => { dropdown.style.display = 'none'; }, 150);
|
||||
});
|
||||
|
||||
document.body.appendChild(dropdown);
|
||||
wrapper.appendChild(display);
|
||||
cell.getElement().addEventListener('remove', () => dropdown.remove());
|
||||
onRendered(() => display.focus());
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
notenHeaderFilterFunc(filterVal, rowVal, rowData, filterParams) {
|
||||
if (!Array.isArray(filterVal) || !filterVal.length) return true;
|
||||
// noteField: the column shows a text, the note id is in another field
|
||||
const noteVal = filterParams?.noteField ? rowData[filterParams.noteField] : rowVal;
|
||||
// noteVal is the raw integer note id or a note object
|
||||
const noteId = typeof noteVal === 'object' ? noteVal?.note : noteVal;
|
||||
return filterVal.some(val => val == noteId); // loose equality: filter vals are numbers, noteId might be string
|
||||
},
|
||||
async openBenotung(type, link) {
|
||||
if(type === 'new') {
|
||||
window.open(link, '_blank')
|
||||
@@ -782,6 +871,8 @@ export const AbgabetoolMitarbeiter = {
|
||||
if (saved?.headerFilters && !this.headerFiltersRestored) {
|
||||
this.headerFiltersRestored = true // instantly avoid retriggers
|
||||
for (let hf of saved.headerFilters) {
|
||||
// older saved states hold a text filter here
|
||||
if (hf.field === 'note_bez') this.note_bezFilterSelected = Array.isArray(hf.value) ? hf.value : [];
|
||||
table.setHeaderFilterValue(hf.field, hf.value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user