Tabs Component

This commit is contained in:
cgfhtw
2023-12-01 12:43:39 +01:00
parent 776908792a
commit da9002356c
9 changed files with 213 additions and 74 deletions
+14
View File
@@ -0,0 +1,14 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
use CI3_Events as Events;
/**
* NOTE(chris): example:
Events::on('stv_conf_student', function (&$res) {
$res['test'] = [
'title' => 'TEST',
'component' => './Stv/Studentenverwaltung/Details/Notizen.js'
];
});
*/
@@ -0,0 +1,40 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
use CI3_Events as Events;
class Config extends FHC_Controller
{
public function __construct()
{
// TODO(chris): access!
parent::__construct();
}
public function student()
{
$result = [];
$result['details'] = [
'title' => 'Details',
'component' => './Stv/Studentenverwaltung/Details/Details.js'
];
$result['kontakt'] = [
'title' => 'Kontakt',
'component' => './Stv/Studentenverwaltung/Details/Kontakt.js'
];
$result['notizen'] = [
'title' => 'Notizen',
'component' => './Stv/Studentenverwaltung/Details/Notizen.js'
];
Events::trigger('stv_conf_student', $result);
$this->outputJsonSuccess($result);
}
public function students()
{
$this->outputJsonSuccess([]);
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class CI3_Events
{
const PRIORITY_LOW = 200;
const PRIORITY_NORMAL = 100;
const PRIORITY_HIGH = 10;
private static $events = [];
private static $eventsSorted = [];
public static function on($event, $function, $priority = self::PRIORITY_NORMAL)
{
if (!isset(self::$events[$event]))
self::$events[$event] = [];
self::$events[$event][] = [$priority, $function];
if (!isset(self::$eventsSorted[$event]))
self::$eventsSorted[$event] = true;
else
self::$eventsSorted[$event] = false;
}
public static function trigger($event, &...$args)
{
if (!isset(self::$events[$event]))
return;
if (!self::$eventsSorted[$event]) {
usort(self::$events[$event], function ($a, $b) {
return $a[0] - $b[0];
});
self::$eventsSorted[$event] = true;
}
foreach (self::$events[$event] as $conf) {
call_user_func_array($conf[1], $args);
}
}
}
/**
* NOTE(chris): Autoload Events config
*/
require_once(APPPATH.'config/Events.php');
foreach (scandir(APPPATH.'config/extensions') as $dir)
if ($dir[0] != '.' && file_exists(APPPATH.'config/extensions/'.$dir.'/Events.php'))
require_once APPPATH.'config/extensions/'.$dir.'/Events.php';
@@ -91,11 +91,6 @@ export default {
}
}
},
computed: {
lastSelected() {
return this.selected[this.selected.length - 1];
}
},
methods: {
onSelectVerband({link, studiengang_kz}) {
this.studiengangKz = studiengang_kz;
@@ -182,7 +177,7 @@ export default {
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz"></stv-list>
</template>
<template #bottom>
<stv-details :student="lastSelected"></stv-details>
<stv-details :students="selected"></stv-details>
</template>
</vertical-split>
</main>
@@ -1,66 +1,20 @@
import accessibility from "../../../directives/accessibility.js";
import FhcTabs from "../../Tabs.js";
export default {
directives: {
accessibility
components: {
FhcTabs
},
props: {
student: Object
},
data() {
return {
current: this.$route.params.tab || 'details',
tabTemplates: {
details: 'Details',
notizen: 'Notizen',
kontakt: 'Kontakt'
},
tabs: {}
}
},
computed: {
hasNoStudent() {
return !this.student || (Object.keys(this.student).length === 0 && this.student.constructor === Object);
},
currentComponent() {
return this.tabs[this.current].component;
}
},
created() {
this.tabs = Object.fromEntries(Object.entries(this.tabTemplates).map(([key, title]) => {
return [key, {
title,
component: Vue.defineAsyncComponent(() => import("./Details/" + key.charAt(0).toUpperCase() + key.slice(1) + '.js'))
}];
}));
students: Array
},
template: `
<div class="stv-details h-100 pb-3 d-flex flex-column">
<div v-if="hasNoStudent" class="justify-content-center d-flex h-100 align-items-center">Bitte StudentIn auswählen!</div>
<template v-else>
<div class="nav nav-tabs">
<div
v-for="({title}, key) in tabs"
:key="comp"
class="nav-item nav-link"
:class="{active: key == current}"
@click="current=key"
:aria-current="key == current ? 'page' : ''"
v-accessibility:tab
>
{{title}}
</div>
</div>
<div style="flex: 1 1 0%; height: 0%" class="border-bottom border-start border-end overflow-auto p-3">
<keep-alive>
<suspense>
<component :is="currentComponent" :student="student"></component>
<template #fallback>
Loading...
</template>
</suspense>
</keep-alive>
</div>
<div v-if="!students?.length" class="justify-content-center d-flex h-100 align-items-center">
Bitte StudentIn auswählen!
</div>
<template v-else>
<fhc-tabs v-if="students.length == 1" :modelValue="students[0]" config-url="/components/stv/config/student" :default="$route.params.tab"></fhc-tabs>
<fhc-tabs v-else :modelValue="students" config-url="/components/stv/config/students" :default="$route.params.tab"></fhc-tabs>
</template>
</div>`
};
@@ -37,7 +37,7 @@ export default {
}
},
props: {
student: Object
modelValue: Object
},
data() {
return {
@@ -65,7 +65,7 @@ export default {
}
},
watch: {
student(n) {
modelValue(n) {
this.updateStudent(n);
},
data: {
@@ -104,7 +104,7 @@ export default {
},
save() {
CoreRESTClient
.post('components/stv/Student/save/' + this.student.prestudent_id, this.changed)
.post('components/stv/Student/save/' + this.modelValue.prestudent_id, this.changed)
.then(result => result.data)
.then(result => {
this.resetErrors();
@@ -169,7 +169,7 @@ export default {
}
},
created() {
this.updateStudent(this.student);
this.updateStudent(this.modelValue);
},
//TODO(chris): Geburtszeit? Anzahl der Kinder?
template: `
@@ -14,7 +14,7 @@ export default {
PvAutoComplete
},
props: {
student: Object
modelValue: Object
},
data() {
return {
@@ -25,7 +25,7 @@ export default {
},
created(){
CoreRESTClient
.get('components/stv/Kontakt/getAdressen/' + this.student.person_id)
.get('components/stv/Kontakt/getAdressen/' + this.modelValue.person_id)
.then(result => {
this.adressen = result.data;
})
@@ -33,7 +33,7 @@ export default {
console.error(err.response.data || err.message);
});
/* CoreRESTClient
.get('components/stv/Kontakt/getKontakte/' + this.student.person_id)
.get('components/stv/Kontakt/getKontakte/' + this.modelValue.person_id)
.then(result => {
this.kontakte = result.data;
})
@@ -41,7 +41,7 @@ export default {
console.error(err.response.data || err.message);
});
CoreRESTClient
.get('components/stv/Kontakt/getBankverbindung/' + this.student.person_id)
.get('components/stv/Kontakt/getBankverbindung/' + this.modelValue.person_id)
.then(result => {
this.bankverbindungen = result.data;
})
@@ -62,19 +62,19 @@ export default {
<!-- <button type="button" class="btn btn btn-outline-warning" @click="actionNewAdress()">new Adress</button>
<button type="button" class="btn btn btn-outline-warning" @click="actionEditAdress(111444)">edit 111444</button>-->
<address-list ref="adressList" :uid="student.person_id"></address-list>
<address-list ref="adressList" :uid="modelValue.person_id"></address-list>
</fieldset>
<br>
<fieldset class="overflow-hidden">
<legend>Kontakt</legend>
<!-- {{this.kontakte}}-->
<contact-list ref="contactList" :uid="student.person_id"></contact-list>
<contact-list ref="contactList" :uid="modelValue.person_id"></contact-list>
</fieldset>
<br>
<fieldset class="overflow-hidden">
<legend>Bankverbindungen</legend>
<!-- {{this.bankverbindungen}}-->
<bankaccount-list ref="bankaccountList" :uid="student.person_id"></bankaccount-list>
<bankaccount-list ref="bankaccountList" :uid="modelValue.person_id"></bankaccount-list>
</fieldset>
</div>`
};
@@ -1,6 +1,6 @@
export default {
props: {
student: Object
modelValue: Object
},
template: `
<div class="stv-details-details h-100 pb-3">
+85
View File
@@ -0,0 +1,85 @@
import {CoreRESTClient} from '../RESTClient.js';
import accessibility from "../directives/accessibility.js";
export default {
directives: {
accessibility
},
emits: [
'update:modelValue'
],
props: {
configUrl: String,
default: String,
modelValue: [String, Number, Boolean, Array, Object, Date, Function, Symbol]
},
data() {
return {
current: null,
tabs: {}
}
},
computed: {
currentTab() {
if (this.tabs[this.current])
return this.tabs[this.current];
return { component: 'div' };
},
value: {
get() {
return this.modelValue;
},
set(v) {
this.$emit('update:modelValue', v);
}
}
},
created() {
CoreRESTClient
.get(this.configUrl)
.then(result => CoreRESTClient.getData(result.data))
.then(result => {
const tabs = {};
// TODO(chris): check if result is array
Object.entries(result).forEach(([key, config]) => {
if (!config.component)
return console.error('Component missing for ' + key);
tabs[key] = {
component: Vue.defineAsyncComponent(() => import(config.component)),
title: config.title || key,
config: config.config,
key
}
});
if (tabs[this.default])
this.current = this.default;
else
this.current = Object.keys(tabs)[0];
this.tabs = tabs;
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `
<div class="fhc-tabs d-flex flex-column">
<div class="nav nav-tabs">
<div
v-for="tab in tabs"
:key="tab.key"
class="nav-item nav-link"
:class="{active: tab.key == current}"
@click="current=tab.key"
:aria-current="tab.key == current ? 'page' : ''"
v-accessibility:tab
>
{{tab.title}}
</div>
</div>
<div style="flex: 1 1 0%; height: 0%" class="border-bottom border-start border-end overflow-auto p-3">
<keep-alive>
<component :is="currentTab.component" v-model="value" :config="currentTab.config"></component>
</keep-alive>
</div>
</div>`
};