WIP replicating the lvMenu links & dropdowns accurately inside a tabulator5 actionButtons custom formatted column with all its kinks; basic links already working, design/layout definitely not finished, WIP working on how to get a sensible link dropdown in there; also still considering if this page even needs 2 seperate http requests at all

This commit is contained in:
Johann Hoffmann
2026-04-20 17:54:27 +02:00
parent ccd8d5f871
commit 5374f71732
3 changed files with 90 additions and 10 deletions
@@ -67,7 +67,7 @@ class LvMenu extends FHCAPI_Controller
$lvMenuOptionList = $this->input->post('lvMenuOptionList', true);
$result =[];
foreach($lvMenuOptionList as $lvMenuOptions){
$lvMenu = $this->getLvMenu($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
$lvMenu = $this->getLvMenuInternal($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']);
$result[$lvMenuOptions['lvid']]=$lvMenu;
}
+8 -3
View File
@@ -17,14 +17,12 @@ export default {
studiensemester: null,
lvs: {},
currentSemester: null,
lvMenues: null,
mode: 'table' // TODO: load from local storage
};
},
provide() {
return {
type: Vue.computed(() => this.type),
lvMenues: Vue.computed(() => this.lvMenues)
}
},
inject: ['isStudent', 'isMitarbeiter'],
@@ -50,7 +48,14 @@ export default {
this.firstLoad = false;
this.$api.call(ApiAddons.getMultipleLvMenu(this.lvs[this.currentSemester].lvs, this.currentSemester)).then(res => {
this.lvMenues = res.data
if(res.data) {
Object.entries(res.data).forEach((entry) => {
// entry[0] -> key -> lvid
// entry[1] -> value -> menu
const lv = this.lvs[this.currentSemester].lvs.find(lv => lv.lehrveranstaltung_id == entry[0])
lv.menu = entry[1]
})
}
})
})
+81 -6
View File
@@ -25,9 +25,9 @@ export default {
{title: Vue.computed(() => this.$p.t('lehre/studiengang')), field: 'sg_bezeichnung', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('global/bezeichnung')), field: 'bezeichnung', widthGrow: 2},
{title: Vue.computed(() => this.$p.t('lehre/orgform')), field: 'orgform_kurzbz', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('admission/stg_kurz')), field: 'studiengang_kuerzel', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('global/actions')),
field: 'menu', formatter: this.actionFormatter, widthGrow: 1}
{title: Vue.computed(() => this.$p.t('lehre/kurzbz')), field: 'studiengang_kuerzel', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('global/actions')), headerSort: false,
field: 'menu', formatter: this.actionFormatter, widthGrow: 1, tooltip: this.spoofingFunc}
],
persistence: false,
persistenceID: "mylv_2026_04_17"
@@ -47,23 +47,86 @@ export default {
},
methods: {
spoofingFunc() {
// intentionally send empty tooltip info so tabulator tooltip doesnt get rendered but hover event propagates
// to individual button tooltips
return ''
},
c4_target(menuItem) {
if (menuItem.c4_moodle_links?.length > 0) return null;
return menuItem.c4_target ?? null;
},
c4_link(menuItem) {
if (!menuItem) return null;
if (Array.isArray(menuItem.c4_moodle_links) && menuItem.c4_moodle_links.length) {
return '#';
} else {
return menuItem.c4_link ?? null;
}
},
handleUuidDefined(uuid) {
this.tabulatorUuid = uuid
},
tableResolve(resolve) {
this.tableBuiltResolve = resolve
},
actionFormatter() {
return ''
actionFormatter(cell, formatterParams, onRendered) {
let container = document.createElement('div');
container.className = "d-flex gap-2";
const data = cell.getData()
console.log(data)
if(data.menu && data.menu.length) {
const calculatedMinWidth = data.menu.length * 120;
container.style.minWidth = `${calculatedMinWidth}px`;
const abbreviate = (str, limit = 12) =>
str.length > limit + 3 ? `${str.slice(0, limit)}...` : str;
data.menu.forEach((lvLink) => {
let button = document.createElement('button');
button.className = 'btn btn-outline-secondary btn-action';
if (!lvLink.c4_link) {
button.classList.add('unavailable');
}
const icon = lvLink.c4_icon2 ?? 'fa-solid fa-pen-to-square'
button.id = lvLink.name
button.innerHTML = '<i class="'+icon+'"></i>';
button.title = lvLink.phrase ? this.$p.t(lvLink.phrase) : lvLink.name;
button.innerHTML += '<span style="margin-left: 2px;">'+abbreviate(button.title)+'</span>';
button.addEventListener('click', (event) => {
// replicate a tag here
event.preventDefault();
const url = this.c4_link(lvLink);
if (url) {
const target = lvLink.c4_target || '_blank';
if (target === '_blank') {
window.open(url, '_blank', 'noopener,noreferrer');
} else {
window.location.href = url;
}
} else {
console.warn("Link is unavailable for:", lvLink.name);
}
});
container.append(button);
})
}
return container;
},
async setupData() {
this.$refs.mylvTable.tabulator.setData(this.lvs);
},
async setupMounted() {
// console.log('mounted pre table promise')
this.tableBuiltPromise = new Promise(this.tableResolve)
await this.tableBuiltPromise
console.log('mounted post table promise')
this.setupData()
const tableID = this.tabulatorUuid ? ('-' + this.tabulatorUuid) : ''
@@ -85,6 +148,18 @@ export default {
mounted() {
this.setupMounted()
},
watch: {
lvs: {
handler(newVal, oldVal) {
console.log('watcher')
if(this.$refs.mylvTable) {
console.log('watcher inside if ref table clause')
this.$refs.mylvTable.tabulator.setData(newVal);
}
},
deep: true
}
},
template: `
<div class="mylv-semester" v-if="ready">
<core-filter-cmpt