WIP abgabetool mitarbeiter

This commit is contained in:
Johann Hoffmann
2025-04-25 09:59:41 +02:00
parent e28067259b
commit 97d83d3acf
10 changed files with 272 additions and 78 deletions
@@ -43,7 +43,9 @@ class Lehre extends FHCAPI_Controller
'getStudentProjektabgaben' => self::PERM_LOGGED,
'postStudentProjektarbeitZwischenabgabe' => self::PERM_LOGGED,
'postStudentProjektarbeitEndupload' => self::PERM_LOGGED,
'getMitarbeiterProjektarbeiten' => self::PERM_LOGGED
'getMitarbeiterProjektarbeiten' => self::PERM_LOGGED,
'postProjektarbeitAbgabe' => self::PERM_LOGGED,
'deleteProjektarbeitAbgabe' => self::PERM_LOGGED
]);
$this->load->library('PhrasesLib');
@@ -448,9 +450,6 @@ class Lehre extends FHCAPI_Controller
public function getMitarbeiterProjektarbeiten() {
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
// if (!isset($uid) || isEmptyString($uid))
// $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
$boolParamStr = $this->input->get('showall');
$trueStrings = ['true', '1'];
$falseStrings = ['false', '0'];
@@ -473,5 +472,76 @@ class Lehre extends FHCAPI_Controller
$this->terminateWithSuccess(array($projektarbeiten, DOMAIN));
}
public function postProjektarbeitAbgabe() {
$projektarbeit_id = $_POST['projektarbeit_id'];
$paabgabe_id = $_POST['paabgabe_id'];
$paabgabetyp_kurzbz = $_POST['paabgabetyp_kurzbz'];
$datum = $_POST['datum'];
$fixtermin = $_POST['fixtermin'];
$kurzbz = $_POST['kurzbz'];
if (!isset($projektarbeit_id) || isEmptyString($projektarbeit_id)
|| !isset($paabgabe_id) || isEmptyString($paabgabe_id)
|| !isset($datum) || isEmptyString($datum)
|| !isset($datum) || isEmptyString($datum)
|| !isset($paabgabetyp_kurzbz) || isEmptyString($paabgabetyp_kurzbz))
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
if($paabgabe_id == -1) {
$result = $this->PaabgabeModel->insert(
array(
'projektarbeit_id' => $projektarbeit_id,
'paabgabetyp_kurzbz' => $paabgabetyp_kurzbz,
'fixtermin' => $fixtermin,
'datum' => $datum,
'kurzbz' => $kurzbz,
'insertvon' => getAuthUID(),
'insertamum' => date('Y-m-d H:i:s')
)
);
$this->terminateWithSuccess($result);
} else {
$result = $this->PaabgabeModel->update(
$paabgabe_id,
array(
'paabgabetyp_kurzbz' => $paabgabetyp_kurzbz,
'datum' => $datum,
'kurzbz' => $kurzbz,
'updatevon' => getAuthUID(),
'updateamum' => date('Y-m-d H:i:s')
)
);
$this->terminateWithSuccess($result);
}
}
public function deleteProjektarbeitAbgabe() {
$paabgabe_id = $_POST['paabgabe_id'];
if (!isset($paabgabe_id) || isEmptyString($paabgabe_id))
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
$this->load->model('education/Paabgabe_model', 'PaabgabeModel');
$result = $this->PaabgabeModel->load($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result);
if(count($result) == 0)
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
// TODO: berechtigung?
if($result[0]->insertvon === getAuthUID()) {
$result = $this->PaabgabeModel->delete($paabgabe_id);
$result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
$this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general');
}
}
@@ -31,15 +31,4 @@ class Paabgabe_model extends DB_Model
return $this->execQuery($qry, array($projektarbeit_id));
}
public function updatePaabgabe($paabgabe_id) {
$qry="
UPDATE campus.tbl_paabgabe SET
abgabedatum = now(),
updatevon = ?,
updateamum = now()
WHERE paabgabe_id= ?";
return $this->execQuery($qry, array(getAuthUID(), $paabgabe_id));
}
}
@@ -169,7 +169,8 @@ class Projektarbeit_model extends DB_Model
campus.tbl_paabgabe.datum,
campus.tbl_paabgabetyp.paabgabetyp_kurzbz,
campus.tbl_paabgabetyp.bezeichnung,
campus.tbl_paabgabe.abgabedatum
campus.tbl_paabgabe.abgabedatum,
campus.tbl_paabgabe.insertvon
FROM campus.tbl_paabgabe JOIN campus.tbl_paabgabetyp USING(paabgabetyp_kurzbz)
WHERE campus.tbl_paabgabe.projektarbeit_id = ?
ORDER BY campus.tbl_paabgabe.datum";
+23
View File
@@ -54,5 +54,28 @@ export default {
`/api/frontend/v1/Lehre/getMitarbeiterProjektarbeiten?showall=${all}`
, {}
);
},
postProjektarbeitAbgabe(termin) {
const payload = {
paabgabe_id: termin.paabgabe_id,
paabgabetyp_kurzbz: termin.bezeichnung.paabgabetyp_kurzbz,
datum: termin.datum,
fixtermin: termin.fixtermin,
insertvon: termin.insertvon,
kurzbz: termin.kurzbz,
projektarbeit_id: termin.projektarbeit_id
}
const url = '/api/frontend/v1/Lehre/postProjektarbeitAbgabe';
return this.$fhcApi.post(url, payload, null)
},
deleteProjektarbeitAbgabe(paabgabe_id) {
const payload = {
paabgabe_id
}
const url = '/api/frontend/v1/Lehre/deleteProjektarbeitAbgabe';
return this.$fhcApi.post(url, payload, null)
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ const router = VueRouter.createRouter({
props: true
},
{
path: `/Cis/Abgabetool/Student`,
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
name: 'AbgabetoolStudent',
component: AbgabetoolStudent,
props: true
@@ -8,7 +8,8 @@ export const AbgabeMitarbeiterDetail = {
InputNumber: primevue.inputnumber,
Checkbox: primevue.checkbox,
Dropdown: primevue.dropdown,
Textarea: primevue.textarea
Textarea: primevue.textarea,
VueDatePicker
},
props: {
projektarbeit: {
@@ -21,6 +22,29 @@ export const AbgabeMitarbeiterDetail = {
eidAkzeptiert: false,
enduploadTermin: null,
allActiveLanguages: FHC_JS_DATA_STORAGE_OBJECT.server_languages,
// TODO: fetch types
allAbgabeTypes: [
{
paabgabetyp_kurzbz: 'abstract',
bezeichnung: 'Entwurf'
},
{
paabgabetyp_kurzbz: 'zwischen',
bezeichnung: 'Zwischenabgabe'
},
{
paabgabetyp_kurzbz: 'note',
bezeichnung: 'Benotung'
},
{
paabgabetyp_kurzbz: 'end',
bezeichnung: 'Endupload'
},
{
paabgabetyp_kurzbz: 'enda',
bezeichnung: 'Endabgabe im Sekretariat'
}
],
form: Vue.reactive({
sprache: '',
abstract: '',
@@ -36,11 +60,52 @@ export const AbgabeMitarbeiterDetail = {
openZusatzdatenModal(termin) {
},
save(termin) {
// TODO: api speichern termin
saveTermin(termin) {
const paabgabe_id = termin.paabgabe_id
this.$fhcApi.factory.lehre.postProjektarbeitAbgabe(termin).then( (res) => {
if(res?.meta?.status == 'success') {
this.$fhcAlert.alertSuccess(this.$p.t('ui/gespeichert'))
if(paabgabe_id === -1) { // new abgabe has been inserted
termin.paabgabe_id = res?.data?.retval
this.projektarbeit.abgabetermine.push({ // new abgatermin row
'paabgabe_id': -1,
'projektarbeit_id': this.projektarbeit.projektarbeit_id,
'fixtermin': false,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'paabgabetyp_kurzbz': '',
'bezeichnung': '',
'abgabedatum': null,
'insertvon': this.viewData?.uid ?? '',
'allowedToSave': true,
'allowedToDelete': true
})
}
} else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError()
}
})
},
delete(termin) {
// TODO: api delete termin
deleteTermin(termin) {
debugger
this.$fhcApi.factory.lehre.deleteProjektarbeitAbgabe(termin.paabgabe_id).then( (res) => {
if(res?.meta?.status == 'success') {
this.$fhcAlert.alertSuccess(this.$p.t('ui/genericDeleted', [this.$p.t('abgabetool/abgabe')]))
// this.$p.t('global/tooltipLektorDeleteKontrolle', [this.$entryParams.permissions.kontrolleDeleteMaxReach ])
const deletedTerminIndex = this.projektarbeit.abgabetermine.findIndex(t => t.paabgabe_id === termin.paabgabe_id)
this.projektarbeit.abgabetermine.splice(deletedTerminIndex, 1)
} else if(res?.meta?.status == 'error'){
this.$fhcAlert.alertError()
}
})
},
validate: function(termin) {
if(!termin.file.length) {
@@ -84,8 +149,25 @@ export const AbgabeMitarbeiterDetail = {
openBeurteilungLink(link) {
window.open(link, '_blank')
},
getOptionLabel(option) {
getOptionLabelSprache(option) {
return option.sprache
},
getOptionLabelAbgabetyp(option){
return option.bezeichnung
},
formatDate(dateParam) {
const date = new Date(dateParam)
// handle missing leading 0
const padZero = (num) => String(num).padStart(2, '0');
const month = padZero(date.getMonth() + 1); // Months are zero-based
const day = padZero(date.getDate());
const year = date.getFullYear();
return `${day}.${month}.${year}`;
},
openStudentPage() {
// TODO: do it
}
},
watch: {
@@ -119,39 +201,58 @@ export const AbgabeMitarbeiterDetail = {
<h5>{{$p.t('abgabetool/c4abgabeMitarbeiterbereich')}}</h5>
<div class="row">
<p> {{projektarbeit?.student}}</p>
<p> {{projektarbeit?.titel}}</p>
<p v-if="projektarbeit?.zweitbegutachter"> {{projektarbeit?.zweitbegutachter}}</p>
<div class="col-8">
<p> {{projektarbeit?.student}}</p>
<p> {{projektarbeit?.titel}}</p>
<p v-if="projektarbeit?.zweitbegutachter"> {{projektarbeit?.zweitbegutachter}}</p>
</div>
<div>
<button class="btn btn-primary border-0" @click="openStudentPage">
Speichern
<i style="margin-left: 8px" class="fa-solid fa-floppy-disk"></i>
</button>
</div>
</div>
<div id="uploadWrapper">
<div class="row" style="margin-bottom: 12px;">
<div style="width: 100px">{{$p.t('abgabetool/c4fixtermin')}}</div>
<div class="col-1">{{$p.t('abgabetool/c4zieldatum')}}</div>
<div class="col-1">{{$p.t('abgabetool/c4abgabetyp')}}</div>
<div class="col-2">{{$p.t('abgabetool/c4abgabekurzbz')}}</div>
<div class="col-2">{{$p.t('abgabetool/c4zieldatum')}}</div>
<div class="col-2">{{$p.t('abgabetool/c4abgabetyp')}}</div>
<div class="col-4">{{$p.t('abgabetool/c4abgabekurzbz')}}</div>
<div class="col-1">{{$p.t('abgabetool/c4abgabedatum')}}</div>
<div class="col">
</div>
</div>
<!-- TODO: show some nothing found placeholder when abgabetermine are empty -->
<div v-if="!projektarbeit?.abgabetermine?.length">keine Termine gefunden!</div>
<div class="row" v-for="termin in projektarbeit.abgabetermine">
<div style="width: 100px" class="d-flex justify-content-center align-items-center">
<p class="fhc-bullet" :class="{ 'fhc-bullet-red': termin.fixtermin, 'fhc-bullet-green': !termin.fixtermin }"></p>
</div>
<div class="col-1 d-flex justify-content-center align-items-center">
<div class="col-2 d-flex justify-content-center align-items-center">
<div :style="getDateStyle(termin)">
<!-- TODO: date input-->
{{ termin.datum?.split("-").reverse().join(".") }}
<VueDatePicker
v-model="termin.datum"
:clearable="false"
:disabled="!termin.allowedToSave"
:enable-time-picker="false"
:format="formatDate"
:text-input="true"
auto-apply>
</VueDatePicker>
</div>
</div>
<div class="col-1 d-flex justify-content-center align-items-center">
<!-- TODO: type dropdown select -->
{{ termin.bezeichnung }}
</div>
<div class="col-2 d-flex justify-content-center align-items-center">
<!-- TODO: abgabe kurzbz input -->
{{ termin.kurzbz }}
<Dropdown
:style="{'width': '100%'}"
:disabled="!termin.allowedToSave"
v-model="termin.bezeichnung"
:options="allAbgabeTypes"
:optionLabel="getOptionLabelAbgabetyp">
</Dropdown>
</div>
<div class="col-4 d-flex justify-content-center align-items-center">
<Textarea style="margin-bottom: 4px;" v-model="termin.kurzbz" rows="3" cols="60" :disabled="!termin.allowedToSave"></Textarea>
</div>
<div class="col-1 d-flex justify-content-center align-items-center">
{{ termin.abgabedatum?.split("-").reverse().join(".") }}
@@ -159,22 +260,16 @@ export const AbgabeMitarbeiterDetail = {
<i class="fa-solid fa-file-pdf"></i>
</a>
</div>
<div class="col-6">
<div class="col-2 align-content-center">
<div class="row">
<div class="col-3">
<button class="btn btn-primary border-0" @click="save(termin)" :disabled="!termin.allowedToUpload">
<div class="col-6">
<button v-if="termin.allowedToSave" class="btn btn-primary border-0" @click="saveTermin(termin)">
Speichern
<i style="margin-left: 8px" class="fa-solid fa-floppy-disk"></i>
</button>
</div>
<div class="col-3">
<button class="btn btn-primary border-0" @click="delete(termin)" :disabled="!termin.allowedToUpload">
Löschen
<i style="margin-left: 8px" class="fa-solid fa-trash"></i>
</button>
</div>
<div v-if="termin.endupload && hasFile" class="col-3">
<button class="btn btn-primary border-0" @click="openZusatzdatenModal(termin)" :disabled="!termin.allowedToUpload">
<div class="col-6">
<button v-if="termin.allowedToDelete && termin.paabgabe_id > 0" class="btn btn-primary border-0" @click="deleteTermin(termin)">
Löschen
<i style="margin-left: 8px" class="fa-solid fa-trash"></i>
</button>
@@ -208,7 +303,7 @@ export const AbgabeMitarbeiterDetail = {
:style="{'width': '100%'}"
v-model="form.sprache"
:options="allActiveLanguages"
:optionLabel="getOptionLabel">
:optionLabel="getOptionLabelSprache">
</Dropdown>
</div>
</div>
@@ -16,6 +16,10 @@ export const AbgabeStudentDetail = {
projektarbeit: {
type: Object,
default: null
},
viewMode: {
type: Boolean,
default: false
}
},
data() {
@@ -217,10 +221,10 @@ export const AbgabeStudentDetail = {
<div class="col-6">
<div class="row">
<div class="col-6">
<Upload v-if="termin && termin.allowedToUpload" accept=".pdf" v-model="termin.file"></Upload>
<Upload v-if="termin && termin.allowedToUpload" :disabled="viewMode" accept=".pdf" v-model="termin.file"></Upload>
</div>
<div class="col-6">
<button class="btn btn-primary border-0" @click="upload(termin)" :disabled="!termin.allowedToUpload">
<button class="btn btn-primary border-0" @click="upload(termin)" :disabled="!termin.allowedToUpload || viewMode">
Upload
<i style="margin-left: 8px" class="fa-solid fa-upload"></i>
</button>
@@ -7,7 +7,7 @@ export const AbgabetoolMitarbeiter = {
components: {
CoreFilterCmpt,
AbgabeDetail,
VerticalSplit
VerticalSplit,
},
props: {
viewData: {
@@ -33,7 +33,7 @@ export const AbgabetoolMitarbeiter = {
tableBuiltResolve: null,
tableBuiltPromise: null,
abgabeTableOptions: {
height: 300,
height: 700,
index: 'projektarbeit_id',
layout: 'fitDataStretch',
placeholder: this.$p.t('global/noDataAvailable'),
@@ -111,6 +111,7 @@ export const AbgabetoolMitarbeiter = {
},
toggleShowAll(showall) {
this.showAll = showall
// TODO: debug tabulator row render
this.loadProjektarbeiten(showall, () => { this.$refs.abgabeTable?.tabulator.redraw(true) })
},
addSeries() {
@@ -120,23 +121,31 @@ export const AbgabetoolMitarbeiter = {
return new Date(date) < new Date(Date.now())
},
setDetailComponent(details){
debugger
this.loadAbgaben(details).then((res)=> {
debugger
const pa = this.projektarbeiten?.retval?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
pa.abgabetermine = res.data.retval
pa.abgabetermine.push({ // new abgatermin row
'paabgabe_id': -1,
'projektarbeit_id': pa.projektarbeit_id,
'fixtermin': false,
'kurzbz': '',
'datum': new Date().toISOString().split('T')[0],
'paabgabetyp_kurzbz': '',
'bezeichnung': '',
'abgabedatum': null,
'insertvon': this.viewData?.uid ?? ''
})
pa.abgabetermine.forEach(termin => {
termin.file = []
termin.allowedToUpload = true
// TODO: fixtermin logic?
if(termin.bezeichnung == 'Endupload' && this.isPastDate(termin.datum)) {
// termin.allowedToUpload = false
} else {
// termin.allowedToUpload = true
termin.allowedToSave = termin.insertvon == this.viewData?.uid && pa.betreuerart_kurzbz != 'Zweitbegutachter'
termin.allowedToDelete = termin.allowedToSave && !termin.abgabedatum
termin.bezeichnung = {
bezeichnung: termin.bezeichnung,
paabgabetyp_kurzbz: termin.paabgabetyp_kurzbz
}
})
pa.betreuer = this.buildBetreuer(pa)
pa.student_uid = details.student_uid
@@ -192,6 +201,7 @@ export const AbgabetoolMitarbeiter = {
return (projekt.typ + projekt.kurzbz)?.toUpperCase()
},
buildBetreuer(abgabe) {
// TODO: preload and insert own titled name of betreuer somehow
return abgabe.betreuerart_beschreibung + ': ' + (abgabe.btitelpre ? abgabe.btitelpre + ' ' : '') + abgabe.bvorname + ' ' + abgabe.bnachname + (abgabe.btitelpost ? ' ' + abgabe.btitelpost : '')
},
setupData(data){
@@ -275,14 +285,14 @@ export const AbgabetoolMitarbeiter = {
this.setupMounted()
},
template: `
<div class="h-100">
<h2>{{$p.t('abgabetool/abgabetoolTitle')}}</h2>
<hr>
<vertical-split ref="verticalsplit">
<vertical-split ref="verticalsplit">
<template #top>
<h2>{{$p.t('abgabetool/abgabetoolTitle')}}</h2>
<hr>
<core-filter-cmpt
:title="''"
@uuidDefined="handleUuidDefined"
@@ -310,6 +320,7 @@ export const AbgabetoolMitarbeiter = {
</button>
</template>
</core-filter-cmpt>
</template>
<template #bottom>
<div v-show="selectedProjektarbeit" ref="selProj">
@@ -318,7 +329,6 @@ export const AbgabetoolMitarbeiter = {
</template>
</vertical-split>
</div>
`,
};
@@ -4,11 +4,13 @@ import AbgabeDetail from "./AbgabeStudentDetail.js";
export const AbgabetoolStudent = {
name: "AbgabetoolStudent",
components: {
VueDatePicker,
CoreFilterCmpt,
AbgabeDetail
},
props: {
student_uid_prop: {
default: null
},
viewData: {
type: Object,
required: true,
@@ -177,7 +179,7 @@ export const AbgabetoolStudent = {
this.$refs.abgabeTable.tabulator.setData(d);
},
loadProjektarbeiten() {
this.$fhcApi.factory.lehre.getStudentProjektarbeiten(this.viewData?.uid ?? null)
this.$fhcApi.factory.lehre.getStudentProjektarbeiten(this.student_uid_prop ?? this.viewData?.uid ?? null)
.then(res => {
if(res?.data) this.setupData(res.data)
})
@@ -205,7 +207,9 @@ export const AbgabetoolStudent = {
},
computed: {
isViewMode() {
return this.student_uid !== null
}
},
created() {
@@ -228,7 +232,7 @@ export const AbgabetoolStudent = {
<hr>
<div v-show="selectedProjektarbeit">
<AbgabeDetail :projektarbeit="selectedProjektarbeit"></AbgabeDetail>
<AbgabeDetail :viewMode="isViewMode" :projektarbeit="selectedProjektarbeit"></AbgabeDetail>
</div>
`,
};
-2
View File
@@ -1,7 +1,5 @@
import FhcAlert from './FhcAlert.js';
import FhcApiFactory from '../api/fhcapifactory.js';
export default {
install: (app, options) => {
if (app.config.globalProperties.$fhcApi) {