From 9b8b1b25326dec1a7f6ec7b1c06e8f21b85191d8 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Fri, 20 Mar 2026 12:21:47 +0100 Subject: [PATCH 1/5] cellFormatter & TitleFormatter breakpoints at 100px to display longform or shortform text; phrasenupdate; lefthandside formatting per default; curated tabulator tooltips for complex cells WIP --- public/css/components/abgabetool/abgabe.css | 19 ++ .../Cis/Abgabetool/AbgabetoolAssistenz.js | 165 ++++++++++++++---- .../Cis/Abgabetool/AbgabetoolMitarbeiter.js | 150 +++++++++++++--- system/phrasesupdate.php | 6 +- 4 files changed, 275 insertions(+), 65 deletions(-) diff --git a/public/css/components/abgabetool/abgabe.css b/public/css/components/abgabetool/abgabe.css index d70481805..7c22d8e7f 100644 --- a/public/css/components/abgabetool/abgabe.css +++ b/public/css/components/abgabetool/abgabe.css @@ -305,4 +305,23 @@ /* If you use hover rows, you need to ensure the sticky cell matches the hover color */ #abgabetable .tabulator-row:hover .tabulator-cell.sticky-col { background-color: #ccc; /* Match your existing hover color */ +} + +.tabulator-cell { + container-type: inline-size; +} + +.tabulator-col-title { + container-type: inline-size; +} + +@container (max-width: 100px) { + .full-text { + display: none !important; + } + + .short-text { + display: inline-block !important; + width: 100%; + } } \ No newline at end of file diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index bb5c6a710..72b764931 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -53,7 +53,6 @@ export const AbgabetoolAssistenz = { }, data() { return { - tableData: null, studiensemesterOptions: null, allSem: null, curSem: null, @@ -185,7 +184,7 @@ export const AbgabetoolAssistenz = { {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'student_vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'student_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4studstatus'))), field: 'studienstatus', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4orgform'))), field: 'orgform', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4orgformv2'))), field: 'orgform', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4projekttyp'))), field: 'projekttyp_kurzbz', formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4stg'))), field: 'stg', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4note'))), field: 'note_bez', headerFilter: true, @@ -210,20 +209,34 @@ export const AbgabetoolAssistenz = { headerFilter: dateFilter, headerFilterFunc: this.headerFilterTerminCol, sorter: this.sortFuncTerminCol, - field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false}, + tooltip: this.toolTipFuncPrevTermin, + field: 'prevTermin', formatter: this.abgabterminFormatter, widthGrow: 1, width: 250}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin', headerFilter: dateFilter, headerFilterFunc: this.headerFilterTerminCol, sorter: this.sortFuncTerminCol, - formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false}, + tooltip: this.toolTipFuncNextTermin, + formatter: this.abgabterminFormatter, widthGrow: 1, width: 250}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))), headerFilter: 'list', headerFilterParams: { valuesLookup: this.getQGateStatusList }, - field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, + field: 'qgate1Status', + formatter: this.centeredTextFormatter, + titleFormatter: this.shortLongTitleFormatter, + titleFormatterParams: { + shortForm: 'QG1' + }, + widthGrow: 1, width: 220, tooltip: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))), headerFilter: 'list', headerFilterParams: { valuesLookup: this.getQGateStatusList }, - field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, + field: 'qgate2Status', + formatter: this.centeredTextFormatter, + titleFormatter: this.shortLongTitleFormatter, + titleFormatterParams: { + shortForm: 'QG2' + }, + widthGrow: 1, width: 220, tooltip: false}, ], persistence: false, persistenceID: "abgabetool_2026_03_16" @@ -247,6 +260,61 @@ export const AbgabetoolAssistenz = { ]}; }, methods: { + shortLongTitleFormatter(cell, formatterParams, onRendered) { + const longForm = cell.getValue() + const shortForm = formatterParams?.shortForm + + if(longForm && shortForm) { + return ` + ${longForm} + + ` + } else { + return ` + ${longForm} + ` + } + + }, + toolTipFuncPrevTermin(e, cell, onRendered) { + const data = cell.getData(); + return this.mapDateStyleToTabulatorTooltip(data.prevTermin.dateStyle); + }, + toolTipFuncNextTermin(e, cell, onRendered) { + const data = cell.getData(); + return this.mapDateStyleToTabulatorTooltip(data.nextTermin.dateStyle); + }, + mapDateStyleToTabulatorTooltip(dateStyleString) { + switch(dateStyleString) { + case 'bestanden': + return this.$p.t('abgabetool/c4tooltipBestanden') + break; + case 'nichtbestanden': + return this.$p.t('abgabetool/c4tooltipNichtBestanden') + break; + case 'beurteilungerforderlich': + return this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich') + break; + case 'verspaetet': + return this.$p.t('abgabetool/c4tooltipVerspaetet') + break; + case 'abgegeben': + return this.$p.t('abgabetool/c4tooltipAbgegeben') + break; + case 'verpasst': + return this.$p.t('abgabetool/c4tooltipVerpasst') + break; + case 'abzugeben': + return this.$p.t('abgabetool/c4tooltipAbzugeben') + break; + case 'standard': + return this.$p.t('abgabetool/c4tooltipStandardv2') + break; + default: return '' + } + }, handlePaUpdated(projektarbeit) { this.checkAbgabetermineProjektarbeit(projektarbeit) this.$refs.abgabeTable.tabulator.redraw(true) @@ -428,6 +496,32 @@ export const AbgabetoolAssistenz = { projekt.qgate2StatusRank = 1 } }) + + // set shorthand statuscode once real status has been determined + projekt.qgate1StatusShort = this.mapRankToShortStatus(projekt.qgate1StatusRank) + projekt.qgate2StatusShort = this.mapRankToShortStatus(projekt.qgate2StatusRank) + }, + mapRankToShortStatus(rank) { + switch(rank){ + case 0: // kein termin vorhanden + return '--' + break; + case 1: // noch nicht stattgefunden + return 'o' + break; + case 2: // noch nicht abgegeben + return '?' + break; + case 3: // noch nicht benotet + return '~' + break; + case 4: // negativ benotet + return '-' + break; + case 5: // positiv benotet + return '+' + break; + } }, getItemBezeichnung(item){ if(!item.bezeichnung) return '' @@ -901,37 +995,37 @@ export const AbgabetoolAssistenz = { this.$refs.drawer.show() }, centeredTextFormatter(cell) { - const val = cell.getValue() - if(!val) return + const longForm = cell.getValue() + if(!longForm) return + const data = cell.getData() + const entry = Object.entries(data).find(entry => entry[1] == longForm) - return '
' + - '

'+val+'

' + // shortFormKey must have same keyname as longForm but with 'Short' appended + const shortForm = data[entry[0]+'Short'] + + if(shortForm && longForm) { + return `
+ + ${longForm} + + +
`; + } else { + return '
' + + '

'+longForm+'

' + } }, detailFormatter(cell) { - return '
' + + return '
' + '
' }, - mailFormatter(cell) { - const val = cell.getValue() - return '
' + - '
' - }, - timelineFormatter() { - return '
' + - '
' - }, - beurteilungFormatter(cell) { - const val = cell.getValue() - if(val) { - return '
' + - '
' - } else return '-' - }, pkzTextFormatter(cell) { const val = cell.getValue() - return '
' + - ''+val+'
' + return '
' + + ''+val+'
' }, abgabterminFormatter(cell) { const val = cell.getValue() @@ -968,14 +1062,14 @@ export const AbgabetoolAssistenz = { const bezeichnung = val.bezeichnung?.bezeichnung ?? val.bezeichnung return '
' + - '
' + + '
' + icon + '
' + '
' + - '

'+bezeichnung+' - '+ this.formatDate(val.datum)+'

' + + '

'+bezeichnung+' - '+ this.formatDate(val.datum)+'

' + '
'+ '
' - + } else { return '' } @@ -1001,14 +1095,13 @@ export const AbgabetoolAssistenz = { return projekt.zweitbetreuer_full_name ?? '' }, async setupData(data){ - this.projektarbeiten = data[0] this.domain = data[1] - - this.tableData = this.mapProjekteToTableData(this.projektarbeiten) + + this.projektarbeiten = this.mapProjekteToTableData(data[0]) await this.tableBuiltPromise - this.$refs.abgabeTable.tabulator.setData(this.tableData); + this.$refs.abgabeTable.tabulator.setData(this.projektarbeiten); }, loadProjektarbeiten(all = false, callback) { this.loading = true diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index 6791d31aa..8f8a224d0 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -43,7 +43,6 @@ export const AbgabetoolMitarbeiter = { }, data() { return { - tableData: null, abgabetypenBetreuer: null, detailIsFullscreen: false, phrasenPromise: null, @@ -153,19 +152,29 @@ export const AbgabetoolMitarbeiter = { headerFilter: dateFilter, headerFilterFunc: this.headerFilterTerminCol, sorter: this.sortFuncTerminCol, - formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false}, + tooltip: this.toolTipFuncPrevTermin, + formatter: this.abgabterminFormatter, widthGrow: 1, width: 250}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nextAbgabetermin'))), field: 'nextTermin', headerFilter: dateFilter, headerFilterFunc: this.headerFilterTerminCol, sorter: this.sortFuncTerminCol, - formatter: this.abgabterminFormatter, widthGrow: 1, width: 250, tooltip: false}, + tooltip: this.toolTipFuncNextTermin, + formatter: this.abgabterminFormatter, widthGrow: 1, width: 250}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate1Status'))), headerFilter: 'list', - headerFilterParams: { valuesLookup: this.getQGateStatusList }, + headerFilterParams: { valuesLookup: this.getQGateStatusList }, + titleFormatter: this.shortLongTitleFormatter, + titleFormatterParams: { + shortForm: 'QG1' + }, field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))), headerFilter: 'list', - headerFilterParams: { valuesLookup: this.getQGateStatusList }, + headerFilterParams: { valuesLookup: this.getQGateStatusList }, + titleFormatter: this.shortLongTitleFormatter, + titleFormatterParams: { + shortForm: 'QG2' + }, field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false} ], persistence: false, @@ -205,6 +214,61 @@ export const AbgabetoolMitarbeiter = { ]}; }, methods: { + shortLongTitleFormatter(cell, formatterParams, onRendered) { + const longForm = cell.getValue() + const shortForm = formatterParams?.shortForm + + if(longForm && shortForm) { + return ` + ${longForm} + + ` + } else { + return ` + ${longForm} + ` + } + + }, + toolTipFuncPrevTermin(e, cell, onRendered) { + const data = cell.getData(); + return this.mapDateStyleToTabulatorTooltip(data.prevTermin.dateStyle); + }, + toolTipFuncNextTermin(e, cell, onRendered) { + const data = cell.getData(); + return this.mapDateStyleToTabulatorTooltip(data.nextTermin.dateStyle); + }, + mapDateStyleToTabulatorTooltip(dateStyleString) { + switch(dateStyleString) { + case 'bestanden': + return this.$p.t('abgabetool/c4tooltipBestanden') + break; + case 'nichtbestanden': + return this.$p.t('abgabetool/c4tooltipNichtBestanden') + break; + case 'beurteilungerforderlich': + return this.$p.t('abgabetool/c4tooltipBeurteilungerforderlich') + break; + case 'verspaetet': + return this.$p.t('abgabetool/c4tooltipVerspaetet') + break; + case 'abgegeben': + return this.$p.t('abgabetool/c4tooltipAbgegeben') + break; + case 'verpasst': + return this.$p.t('abgabetool/c4tooltipVerpasst') + break; + case 'abzugeben': + return this.$p.t('abgabetool/c4tooltipAbzugeben') + break; + case 'standard': + return this.$p.t('abgabetool/c4tooltipStandardv2') + break; + default: return '' + } + }, handlePaUpdated(projektarbeit) { this.checkAbgabetermineProjektarbeit(projektarbeit) this.$refs.abgabeTable.tabulator.redraw(true) @@ -451,6 +515,32 @@ export const AbgabetoolMitarbeiter = { projekt.qgate2StatusRank = 1 } }) + + // set shorthand statuscode once real status has been determined + projekt.qgate1StatusShort = this.mapRankToShortStatus(projekt.qgate1StatusRank) + projekt.qgate2StatusShort = this.mapRankToShortStatus(projekt.qgate2StatusRank) + }, + mapRankToShortStatus(rank) { + switch(rank){ + case 0: // kein termin vorhanden + return '--' + break; + case 1: // noch nicht stattgefunden + return 'o' + break; + case 2: // noch nicht abgegeben + return '?' + break; + case 3: // noch nicht benotet + return '~' + break; + case 4: // negativ benotet + return '-' + break; + case 5: // positiv benotet + return '+' + break; + } }, checkAbgabetermineProjektarbeit(projekt) { const now = luxon.DateTime.now() @@ -517,11 +607,11 @@ export const AbgabetoolMitarbeiter = { const bezeichnung = val.bezeichnung?.bezeichnung ?? val.bezeichnung return '
' + - '
' + - icon + + '
' + + icon + '
' + '
' + - '

'+bezeichnung+' - '+ this.formatDate(val.datum)+'

' + + '

'+bezeichnung+' - '+ this.formatDate(val.datum)+'

' + '
'+ '
' @@ -727,28 +817,37 @@ export const AbgabetoolMitarbeiter = { }, centeredTextFormatter(cell) { - const val = cell.getValue() - if(!val) return - - return '
' + - '

'+val+'

' + const longForm = cell.getValue() + if(!longForm) return + const data = cell.getData() + const entry = Object.entries(data).find(entry => entry[1] == longForm) + + // shortFormKey must have same keyname as longForm but with 'Short' appended + const shortForm = data[entry[0]+'Short'] + + if(shortForm && longForm) { + return `
+ + ${longForm} + + +
`; + } else { + return '
' + + '

'+longForm+'

' + } }, detailFormatter(cell) { - return '
' + + return '
' + '
' }, - beurteilungFormatter(cell) { - const val = cell.getValue() - if(val) { - return '
' + - '
' - } else return '-' - }, pkzTextFormatter(cell) { const val = cell.getValue() - return '
' + - ''+val+'
' + return '
' + + ''+val+'
' }, tableResolve(resolve) { this.tableBuiltResolve = resolve @@ -765,10 +864,9 @@ export const AbgabetoolMitarbeiter = { setupData(data){ - this.projektarbeiten = data[0] this.domain = data[1] - this.tableData = data[0]?.retval?.map(projekt => { + this.projektarbeiten = data[0]?.retval?.map(projekt => { this.checkAbgabetermineProjektarbeit(projekt) projekt.selectable = projekt.betreuerart_kurzbz !== 'Zweitbegutachter' @@ -789,7 +887,7 @@ export const AbgabetoolMitarbeiter = { }) this.$refs.abgabeTable.tabulator.setColumns(this.abgabeTableOptions.columns) - this.$refs.abgabeTable.tabulator.setData(this.tableData); + this.$refs.abgabeTable.tabulator.setData(this.projektarbeiten); }, loadProjektarbeiten(all = false, callback) { this.$api.call(ApiAbgabe.getMitarbeiterProjektarbeiten(all)) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 3b3b6f995..0903e7fe0 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -43767,18 +43767,18 @@ array( array( 'app' => 'core', 'category' => 'abgabetool', - 'phrase' => 'c4orgform', + 'phrase' => 'c4orgformv2', 'insertvon' => 'system', 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Organisationseinheit", + 'text' => "Organisationsform", 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => "organization unit", + 'text' => "organization form", 'description' => '', 'insertvon' => 'system' ) From 6ddb064c9efca342a9f7a050593df275fc6a682f Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Mon, 23 Mar 2026 10:09:46 +0100 Subject: [PATCH 2/5] qgateStatus tooltips --- .../Cis/Abgabetool/AbgabetoolAssistenz.js | 26 +++++++++++++------ .../Cis/Abgabetool/AbgabetoolMitarbeiter.js | 14 ++++++++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index 72b764931..aa9b80491 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -191,18 +191,18 @@ export const AbgabetoolAssistenz = { formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4sem'))), field: 'studiensemester_kurzbz', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4titel'))), field: 'titel', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerv2'))), field: 'erstbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerv2'))), field: 'erstbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerTitelPre'))), field: 'betreuer_titelpre', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerVorname'))), field: 'betreuer_vorname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerNachname'))), field: 'betreuer_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerVorname'))), field: 'betreuer_vorname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: true}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerNachname'))), field: 'betreuer_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: true}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4erstbetreuerTitelPost'))), field: 'betreuer_titelpost', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerv2'))), field: 'zweitbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerv2'))), field: 'zweitbetreuer', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerTitelPre'))), field: 'zweitbetreuer_titelpre', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerVorname'))), field: 'zweitbetreuer_vorname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerNachname'))), field: 'zweitbetreuer_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerVorname'))), field: 'zweitbetreuer_vorname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: true}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerNachname'))), field: 'zweitbetreuer_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: true}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4zweitbetreuerTitelPost'))), field: 'zweitbetreuer_titelpost', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1, visible: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4prevAbgabetermin'))), @@ -226,7 +226,12 @@ export const AbgabetoolAssistenz = { titleFormatterParams: { shortForm: 'QG1' }, - widthGrow: 1, width: 220, tooltip: false}, + widthGrow: 1, width: 220, + tooltip: (e, cell) => { + const data = cell.getData(); + return data.qgate1Status + } + }, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))), headerFilter: 'list', headerFilterParams: { valuesLookup: this.getQGateStatusList }, @@ -236,7 +241,12 @@ export const AbgabetoolAssistenz = { titleFormatterParams: { shortForm: 'QG2' }, - widthGrow: 1, width: 220, tooltip: false}, + widthGrow: 1, width: 220, + tooltip: (e, cell) => { + const data = cell.getData(); + return data.qgate2Status + } + }, ], persistence: false, persistenceID: "abgabetool_2026_03_16" diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index 8f8a224d0..05feb187f 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -167,7 +167,12 @@ export const AbgabetoolMitarbeiter = { titleFormatterParams: { shortForm: 'QG1' }, - field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, + field: 'qgate1Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, + tooltip: (e, cell) => { + const data = cell.getData(); + return data.qgate1Status + } + }, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4qgate2Status'))), headerFilter: 'list', headerFilterParams: { valuesLookup: this.getQGateStatusList }, @@ -175,7 +180,12 @@ export const AbgabetoolMitarbeiter = { titleFormatterParams: { shortForm: 'QG2' }, - field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false} + field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, + tooltip: (e, cell) => { + const data = cell.getData(); + return data.qgate2Status + } + } ], persistence: false, persistenceID: 'abgabeTableBetreuer2026-02-26' From 079a8e3ec1b3bcf8218a38e96ececb2bd4e5e7db Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Tue, 24 Mar 2026 15:19:36 +0100 Subject: [PATCH 3/5] added selected: x | filtered: y | total: z count to assistenz table --- .../Cis/Abgabetool/AbgabetoolAssistenz.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index aa9b80491..6d93946e8 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -53,6 +53,9 @@ export const AbgabetoolAssistenz = { }, data() { return { + count: 0, + filteredcount: 0, + selectedcount: 0, studiensemesterOptions: null, allSem: null, curSem: null, @@ -265,7 +268,12 @@ export const AbgabetoolAssistenz = { }) this.selectedData = data + this.selectedcount = data.length; } + }, + { + event: 'dataFiltered', + handler: (filters, rows) => this.filteredcount = rows.length } ]}; }, @@ -1108,7 +1116,8 @@ export const AbgabetoolAssistenz = { this.domain = data[1] this.projektarbeiten = this.mapProjekteToTableData(data[0]) - + this.count = this.projektarbeiten.length + await this.tableBuiltPromise this.$refs.abgabeTable.tabulator.setData(this.projektarbeiten); @@ -1165,6 +1174,17 @@ export const AbgabetoolAssistenz = { }, }, computed: { + countsToHTML() { + return this.$p.t('global/ausgewaehlt') + + ': ' + (this.selectedcount || 0) + '' + + ' | ' + + this.$p.t('global/gefiltert') + + ': ' + + '' + (this.filteredcount || 0) + '' + + ' | ' + + this.$p.t('global/gesamt') + + ': ' + (this.count || 0) + ''; + }, emailItems() { const menu = [] @@ -1583,6 +1603,7 @@ export const AbgabetoolAssistenz = { :title="''" @uuidDefined="handleUuidDefined" ref="abgabeTable" + :description="countsToHTML" :newBtnShow="true" :newBtnLabel="$p.t('abgabetool/neueTerminserie')" :newBtnDisabled="!selectedData.length" From 44e43d9479b97519f1d67ab8894dc301b14521c7 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Wed, 8 Apr 2026 15:47:17 +0200 Subject: [PATCH 4/5] phrasenkey fix 'c4fehlerAktualitaetProjektarbeit ' => 'c4fehlerAktualitaetProjektarbeit' --- system/phrasesupdate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 0903e7fe0..73bb55b64 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -41761,7 +41761,7 @@ array( array( 'app' => 'core', 'category' => 'abgabetool', - 'phrase' => 'c4fehlerAktualitaetProjektarbeit ', + 'phrase' => 'c4fehlerAktualitaetProjektarbeit', 'insertvon' => 'system', 'phrases' => array( array( From 6868e6814581968b12db09bfdf89274d6c57d8c4 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Thu, 9 Apr 2026 15:42:05 +0200 Subject: [PATCH 5/5] define datepickers to use ISO strings internally with model-type="yyyy-MM-dd" to avoid construction of new js dateobjects when deleting the value via text input, which could lead to a off by one day/timezone error when saving the date --- .../js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js | 3 +++ public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js | 5 +++-- public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js index 011e75145..9132bcfba 100644 --- a/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js +++ b/public/js/components/Cis/Abgabetool/AbgabeMitarbeiterDetail.js @@ -656,6 +656,7 @@ export const AbgabeMitarbeiterDetail = { :enable-time-picker="false" locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" :text-input="true" auto-apply> @@ -806,6 +807,7 @@ export const AbgabeMitarbeiterDetail = { :enable-time-picker="false" locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" :text-input="true" auto-apply> @@ -874,6 +876,7 @@ export const AbgabeMitarbeiterDetail = { :disabled="true" locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" >
diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index 6d93946e8..1a6e626bb 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -85,7 +85,7 @@ export const AbgabetoolAssistenz = { allowedNotenOptions: null, notenOptionsNonFinal: null, serienTermin: Vue.reactive({ - datum: new Date(), + datum: new Date().toISOString().split('T')[0], bezeichnung: { paabgabetyp_kurzbz: 'zwischen', bezeichnung: 'Zwischenabgabe' @@ -832,7 +832,7 @@ export const AbgabetoolAssistenz = { this.saving = true this.serienTermin.fixtermin = !this.serienTermin.invertedFixtermin this.$api.call(ApiAbgabe.postSerientermin( - this.serienTermin.datum.toISOString(), + this.serienTermin.datum, this.serienTermin.bezeichnung.paabgabetyp_kurzbz, this.serienTermin.bezeichnung.bezeichnung, this.serienTermin.kurzbz, @@ -1385,6 +1385,7 @@ export const AbgabetoolAssistenz = { :clearable="false" locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" :enable-time-picker="false" :text-input="true" auto-apply> diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js index 05feb187f..a2e88854f 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolMitarbeiter.js @@ -57,7 +57,7 @@ export const AbgabetoolMitarbeiter = { allowedNotenOptions: null, notenOptionsNonFinal: null, serienTermin: Vue.reactive({ - datum: new Date(), + datum: new Date().toISOString().split('T')[0], bezeichnung: { paabgabetyp_kurzbz: 'zwischen', bezeichnung: 'Zwischenabgabe' @@ -712,7 +712,7 @@ export const AbgabetoolMitarbeiter = { addSeries() { this.saving = true this.$api.call(ApiAbgabe.postSerientermin( - this.serienTermin.datum.toISOString(), + this.serienTermin.datum, this.serienTermin.bezeichnung.paabgabetyp_kurzbz, this.serienTermin.bezeichnung.bezeichnung, this.serienTermin.kurzbz, @@ -1043,6 +1043,7 @@ export const AbgabetoolMitarbeiter = { :enable-time-picker="false" locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" :text-input="true" auto-apply>