mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-12 15:04:04 +00:00
Compare commits
72
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ecf1845d4 | ||
|
|
07d04d5701 | ||
|
|
3c373aac25 | ||
|
|
4e2254dfc7 | ||
|
|
658c8d6bd4 | ||
|
|
e976c825c9 | ||
|
|
88b5a0af46 | ||
|
|
bf29faa5a7 | ||
|
|
2ada167557 | ||
|
|
a53551b462 | ||
|
|
989ce6d707 | ||
|
|
018169b06a | ||
|
|
cc9d1f983e | ||
|
|
0416a2ce7d | ||
|
|
dde95c8e01 | ||
|
|
7260a0a00e | ||
|
|
5cfa398ea6 | ||
|
|
57c48f2c4a | ||
|
|
6d641bbd06 | ||
|
|
3455aed3f5 | ||
|
|
f84d12277d | ||
|
|
9bf5bc2ad9 | ||
|
|
27765606e3 | ||
|
|
75d2cabdbe | ||
|
|
bae9d6d9a0 | ||
|
|
43d6a882ce | ||
|
|
7f3b4100ab | ||
|
|
9f54ecce54 | ||
|
|
7af0f4a478 | ||
|
|
0f843db33d | ||
|
|
3e59648d5b | ||
|
|
5923991848 | ||
|
|
55a6ec9d0d | ||
|
|
78505589c8 | ||
|
|
0e985113ec | ||
|
|
8d9113d5c8 | ||
|
|
7ce81ad4bd | ||
|
|
ea56ec8daa | ||
|
|
bf5ee2f64b | ||
|
|
55d6d90fa6 | ||
|
|
6adc55a843 | ||
|
|
52c1fba17b | ||
|
|
5e15d4b219 | ||
|
|
00954f680b | ||
|
|
92d27fc242 | ||
|
|
558963cd74 | ||
|
|
d8355ba3f5 | ||
|
|
afb8011347 | ||
|
|
d85a3cb08f | ||
|
|
15beaa9be8 | ||
|
|
574057da88 | ||
|
|
c81b5338de | ||
|
|
c433ff5c3a | ||
|
|
a5797a0bde | ||
|
|
37d5f2da5d | ||
|
|
27c39cfdca | ||
|
|
d66fe72cc9 | ||
|
|
59ac56374e | ||
|
|
a397460e0e | ||
|
|
108f91ea1a | ||
|
|
dc5a846c10 | ||
|
|
06895510e0 | ||
|
|
83b06b6c50 | ||
|
|
bd99f3f927 | ||
|
|
eeb1529b64 | ||
|
|
98c3707802 | ||
|
|
e5c8807f17 | ||
|
|
2f86a1b017 | ||
|
|
75ddbdf7c1 | ||
|
|
d824334538 | ||
|
|
13515b2522 | ||
|
|
c5c29df6cc |
@@ -114,6 +114,7 @@ class LvPlan extends FHCAPI_Controller
|
||||
|
||||
// fetching lvplan events
|
||||
$result = $this->stundenplanlib->getEventsUser($start_date, $end_date, $uid);
|
||||
|
||||
$lvplanEvents = $this->getDataOrTerminateWithError($result);
|
||||
|
||||
// fetching moodle events
|
||||
|
||||
@@ -62,6 +62,8 @@ class OtherLvPlan extends FHCAPI_Controller
|
||||
"foto" => $profileData->data->foto,
|
||||
"vorname" => $profileData->data->vorname,
|
||||
"nachname" => $profileData->data->nachname,
|
||||
"titel" => $profileData->data->titel,
|
||||
"postnomen" => $profileData->data->postnomen,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
@@ -167,7 +167,9 @@ class Studium extends FHCAPI_Controller
|
||||
if($aktuelles_studienplan){
|
||||
$lehrveranstaltungen = $this->computeStudienplanLehrveranstaltungen($aktuelles_studienplan->studienplan_id, $aktuelles_semester);
|
||||
foreach($lehrveranstaltungen as $lehrv){
|
||||
foreach($lehrv->lehrveranstaltungen as $lv){
|
||||
// a lehrveranstaltung without a modul has no children and gets its lektoren directly
|
||||
$lvs = isset($lehrv->lehrveranstaltungen) ? $lehrv->lehrveranstaltungen : [$lehrv];
|
||||
foreach($lvs as $lv){
|
||||
$lvLektoren =$this->computeLektorenFromLehrveranstaltung($lv->lehrveranstaltung_id,$aktuelles_semester, $aktuelles_studiengang->studiengang_kz, $aktuelles_studiensemester->studiensemester_kurzbz);
|
||||
$lv->lektoren = $lvLektoren;
|
||||
}
|
||||
@@ -286,6 +288,7 @@ SELECT tbl_lehrveranstaltung.*,
|
||||
$lehrveranstaltungen = $this->LehrveranstaltungModel->execReadOnlyQuery($query,[$studienplan_id, $semester]);
|
||||
|
||||
$lehrveranstaltungen = $this->getDataOrTerminateWithError($lehrveranstaltungen);
|
||||
|
||||
usort($lehrveranstaltungen, function($a, $b){
|
||||
if($a->lehrtyp_kurzbz == "modul"){
|
||||
return -1;
|
||||
@@ -302,12 +305,17 @@ SELECT tbl_lehrveranstaltung.*,
|
||||
}
|
||||
else{
|
||||
$parent =array_filter($carry, function($item)use($lehrv){
|
||||
return $item->studienplan_lehrveranstaltung_id == $lehrv->studienplan_lehrveranstaltung_id_parent;
|
||||
return isset($item->lehrveranstaltungen)
|
||||
&& $item->studienplan_lehrveranstaltung_id == $lehrv->studienplan_lehrveranstaltung_id_parent;
|
||||
});
|
||||
$parent = current($parent);
|
||||
if($parent){
|
||||
$parent->lehrveranstaltungen[] = $lehrv;
|
||||
}
|
||||
else{
|
||||
// lehrveranstaltung without a modul: keep it on the top level
|
||||
array_push($carry, $lehrv);
|
||||
}
|
||||
}
|
||||
return $carry;
|
||||
}, []);
|
||||
|
||||
@@ -108,6 +108,7 @@ class Students extends FHCAPI_Controller
|
||||
'studiensemester_kurzbz' => $studiensemester_kurzbz
|
||||
]);
|
||||
|
||||
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
|
||||
|
||||
$this->studentlistlib->addJoin(
|
||||
"(
|
||||
@@ -153,7 +154,8 @@ class Students extends FHCAPI_Controller
|
||||
$this->addMeta('ci_params', [
|
||||
'studiensemester_kurzbz' => $studiensemester_kurzbz
|
||||
]);
|
||||
|
||||
|
||||
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
|
||||
|
||||
$this->studentlistlib->addJoin(
|
||||
"(
|
||||
@@ -213,6 +215,7 @@ class Students extends FHCAPI_Controller
|
||||
'studiensemester_kurzbz' => $studiensemester_kurzbz
|
||||
]);
|
||||
|
||||
$studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz);
|
||||
|
||||
$this->studentlistlib->addJoin(
|
||||
"(
|
||||
|
||||
@@ -82,6 +82,21 @@ function generateCSSsInclude($CSSs)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates tags for the style sheets you want to include, the parameter could by a string or an array of strings
|
||||
*/
|
||||
function generatePrintCSS($path)
|
||||
{
|
||||
$cssLink = '<link rel="stylesheet" media="print" href="%s" />';
|
||||
|
||||
$ci =& get_instance();
|
||||
$cachetoken = '?'.$ci->config->item('fhcomplete_build_version');
|
||||
|
||||
if (isset($path)) {
|
||||
echo sprintf($cssLink, base_url($path).$cachetoken) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates global JS-Object to pass parms to other javascripts
|
||||
*/
|
||||
|
||||
@@ -94,13 +94,13 @@ class StundenplanLib
|
||||
if (isError($benutzer_gruppen))
|
||||
return $benutzer_gruppen;
|
||||
$benutzer_gruppen = getData($benutzer_gruppen);
|
||||
|
||||
$this->_ci->addMeta('spezialgruppen', $benutzer_gruppen);
|
||||
// getting the student_lehrverbaende of the student in the different studiensemester
|
||||
$student_lehrverband = $this->fetchStudentlehrverbandFromStudiensemester($student_uid, $semester_range);
|
||||
if (isError($student_lehrverband))
|
||||
return $student_lehrverband;
|
||||
$student_lehrverband = getData($student_lehrverband);
|
||||
|
||||
$this->_ci->addMeta('studentlehrvebaende', $student_lehrverband);
|
||||
$stundenplan_query = $this->_ci->StundenplanModel->getStundenplanQuery(
|
||||
$start,
|
||||
$end,
|
||||
@@ -632,6 +632,7 @@ class StundenplanLib
|
||||
$result->semester = $item->semester;
|
||||
$result->verband = $item->verband;
|
||||
$result->gruppe = $item->gruppe;
|
||||
$result->studiensemester_kurzbz = $item->studiensemester_kurzbz;
|
||||
return $result;
|
||||
},
|
||||
$lehrverband_query_result);
|
||||
|
||||
@@ -28,10 +28,21 @@ class CoreMissingZgvTagLib
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id');
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id');
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
|
||||
|
||||
$this->ci->PrestudentModel->db->group_start();
|
||||
|
||||
$this->ci->PrestudentModel->db->where('zgvdatum', null);
|
||||
$this->ci->PrestudentModel->db->where('typ', 'b');
|
||||
|
||||
$this->ci->PrestudentModel->db->or_group_start();
|
||||
$this->ci->PrestudentModel->db->where('zgvmadatum', null);
|
||||
$this->ci->PrestudentModel->db->where('typ', 'm');
|
||||
$this->ci->PrestudentModel->db->group_end();
|
||||
|
||||
$this->ci->PrestudentModel->db->group_end();
|
||||
|
||||
$result = $this->ci->PrestudentModel-> loadWhere(array(
|
||||
'bn.aktiv' => true, //check if necessary
|
||||
'zgvdatum' => null,
|
||||
'typ' => 'b',
|
||||
'bn.aktiv' => true,
|
||||
'studiensemester_kurzbz' => $semester
|
||||
));
|
||||
|
||||
@@ -60,16 +71,36 @@ class CoreMissingZgvTagLib
|
||||
$semester = $params['studiensemester_kurzbz'];
|
||||
$prestudent_id = $params['id'];
|
||||
|
||||
$this->ci->PrestudentModel->addSelect('prestudent_id');
|
||||
$this->ci->PrestudentModel->addSelect('start as von');
|
||||
$this->ci->PrestudentModel->addSelect('ende as bis');
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id');
|
||||
|
||||
//add studiensemester also here for manual rebuild
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
|
||||
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id');
|
||||
$this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
|
||||
$result = $this->ci->PrestudentModel->loadWhere(array(
|
||||
'bn.aktiv' => true, //check if necessary
|
||||
'zgvdatum' => null,
|
||||
'typ' => 'b',
|
||||
|
||||
$this->ci->PrestudentModel->db->group_start();
|
||||
|
||||
$this->ci->PrestudentModel->db->where('zgvdatum', null);
|
||||
$this->ci->PrestudentModel->db->where('typ', 'b');
|
||||
|
||||
$this->ci->PrestudentModel->db->or_group_start();
|
||||
$this->ci->PrestudentModel->db->where('zgvmadatum', null);
|
||||
$this->ci->PrestudentModel->db->where('typ', 'm');
|
||||
$this->ci->PrestudentModel->db->group_end();
|
||||
|
||||
$this->ci->PrestudentModel->db->group_end();
|
||||
$this->ci->PrestudentModel->db->limit(1);
|
||||
|
||||
$result = $this->ci->PrestudentModel-> loadWhere(array(
|
||||
'bn.aktiv' => true,
|
||||
'studiensemester_kurzbz' => $semester,
|
||||
'prestudent_id' => $prestudent_id
|
||||
));
|
||||
|
||||
if(hasData($result))
|
||||
{
|
||||
return $result;
|
||||
|
||||
@@ -13,6 +13,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
protected $dienstverhaeltnis_id;
|
||||
protected $vertragsbestandteil_id;
|
||||
protected $gehaltstyp_kurzbz;
|
||||
protected $gehaltsanpassungtyp_kurzbz = '';
|
||||
protected $von;
|
||||
protected $bis;
|
||||
protected $anmerkung;
|
||||
@@ -21,6 +22,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
protected $valorisierungssperre;
|
||||
protected $valorisierung;
|
||||
protected $auszahlungen;
|
||||
protected $gehaltstyp_bezeichnung;
|
||||
|
||||
protected $insertamum;
|
||||
protected $insertvon;
|
||||
@@ -39,6 +41,10 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
isset($data->dienstverhaeltnis_id) && $this->setDienstverhaeltnis_id($data->dienstverhaeltnis_id);
|
||||
isset($data->vertragsbestandteil_id) && $this->setVertragsbestandteil_id($data->vertragsbestandteil_id);
|
||||
isset($data->gehaltstyp_kurzbz) && $this->setGehaltstyp_kurzbz($data->gehaltstyp_kurzbz);
|
||||
//isset($data->gehaltsanpassungtyp_kurzbz) && $this->setGehaltsanpassungtyp_kurzbz($data->gehaltsanpassungtyp_kurzbz);
|
||||
if (!empty($data->gehaltsanpassungtyp_kurzbz)) {
|
||||
$this->setGehaltsanpassungtyp_kurzbz($data->gehaltsanpassungtyp_kurzbz);
|
||||
}
|
||||
isset($data->von) && $this->setVon($data->von);
|
||||
isset($data->bis) && $this->setBis($data->bis);
|
||||
isset($data->anmerkung) && $this->setAnmerkung($data->anmerkung);
|
||||
@@ -47,7 +53,8 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
isset($data->valorisierungssperre) && $this->setValorisierungssperre($data->valorisierungssperre);
|
||||
isset($data->valorisierung) && $this->setValorisierung($data->valorisierung);
|
||||
isset($data->auszahlungen) && $this->setAuszahlungen($data->auszahlungen);
|
||||
|
||||
isset($data->gehaltstyp_bezeichnung) && $this->setGehaltstyp_bezeichnung($data->gehaltstyp_bezeichnung);
|
||||
|
||||
isset($data->insertamum) && $this->setInsertamum($data->insertamum);
|
||||
isset($data->insertvon) && $this->setInsertvon($data->insertvon);
|
||||
isset($data->updateamum) && $this->setUpdateamum($data->updateamum);
|
||||
@@ -75,6 +82,11 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
return $this->gehaltstyp_kurzbz;
|
||||
}
|
||||
|
||||
public function getGehaltsanpassungtyp_kurzbz()
|
||||
{
|
||||
return $this->gehaltsanpassungtyp_kurzbz;
|
||||
}
|
||||
|
||||
public function getVon()
|
||||
{
|
||||
return $this->von;
|
||||
@@ -178,6 +190,13 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setGehaltsanpassungtyp_kurzbz($gehaltsanpassungtyp_kurzbz)
|
||||
{
|
||||
$this->markDirty('gehaltsanpassungtyp_kurzbz', $this->gehaltsanpassungtyp_kurzbz, $gehaltsanpassungtyp_kurzbz);
|
||||
$this->gehaltsanpassungtyp_kurzbz = $gehaltsanpassungtyp_kurzbz;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setVon($von)
|
||||
{
|
||||
$this->markDirty('von', $this->von, $von);
|
||||
@@ -206,6 +225,10 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
$grundbetrag = number_format($grundbetrag, 2, '.', '');
|
||||
}
|
||||
$this->markDirty('grundbetrag', $this->grundbetrag, $grundbetrag);
|
||||
if(in_array('grundbetrag', $this->modifiedcolumns))
|
||||
{
|
||||
$this->setBetrag_valorisiert($grundbetrag);
|
||||
}
|
||||
$this->grundbetrag = $grundbetrag;
|
||||
return $this;
|
||||
}
|
||||
@@ -242,6 +265,13 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setGehaltstyp_bezeichnung($gehaltstyp_bezeichnung)
|
||||
{
|
||||
$this->markDirty('gehaltstyp_bezeichnung', $this->gehaltstyp_bezeichnung, $gehaltstyp_bezeichnung);
|
||||
$this->gehaltstyp_bezeichnung = $gehaltstyp_bezeichnung;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInsertamum($insertamum)
|
||||
{
|
||||
$this->markDirty('insertamum', $this->insertamum, $insertamum);
|
||||
@@ -284,6 +314,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
'dienstverhaeltnis_id' => $this->getDienstverhaeltnis_id(),
|
||||
'vertragsbestandteil_id' => $this->getVertragsbestandteil_id(),
|
||||
'gehaltstyp_kurzbz' => $this->getGehaltstyp_kurzbz(),
|
||||
'gehaltsanpassungtyp_kurzbz' => $this->getGehaltsanpassungtyp_kurzbz(),
|
||||
'von' => $this->getVon(),
|
||||
'bis' => $this->getBis(),
|
||||
'anmerkung' => $this->getAnmerkung(),
|
||||
@@ -312,6 +343,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
|
||||
dienstverhaeltnis_id: {$this->getDienstverhaeltnis_id()}
|
||||
vertragsbestandteil_id: {$this->getVertragsbestandteil_id()}
|
||||
gehaltstyp_kurzbz: {$this->getGehaltstyp_kurzbz()}
|
||||
gehaltsanpassungtyp_kurzbz: {$this->getGehaltsanpassungtyp_kurzbz()}
|
||||
von: {$this->getVon()}
|
||||
bis: {$this->getBis()}
|
||||
anmerkung: {$this->getAnmerkung()}
|
||||
|
||||
@@ -204,7 +204,7 @@ class Student_model extends DB_Model
|
||||
|
||||
Events::trigger(
|
||||
'generate_student_uid',
|
||||
function ($value) use ($uid) {
|
||||
function ($value) use (&$uid) {
|
||||
$uid = $value;
|
||||
},
|
||||
$stgkzl,
|
||||
|
||||
@@ -457,6 +457,7 @@ class Studiengang_model extends DB_Model
|
||||
*/
|
||||
public function getLeitung($studiengang_kz = null)
|
||||
{
|
||||
$this->addDistinct();
|
||||
$this->addSelect('uid, studiengang_kz, oe_kurzbz, vorname, nachname, email, titelpre, titelpost, alias');
|
||||
$this->addJoin('public.tbl_benutzerfunktion', 'oe_kurzbz');
|
||||
$this->addJoin('public.tbl_benutzer', 'uid');
|
||||
|
||||
@@ -604,11 +604,17 @@ class Stundenplan_model extends DB_Model
|
||||
{
|
||||
foreach($semester_date_range as $sem_date => $sem_date_range)
|
||||
{
|
||||
if(!array_key_exists($sem_date,$studentlehrverbaende) || count($studentlehrverbaende[$sem_date]) == 0)
|
||||
if(!array_key_exists($sem,$studentlehrverbaende) || count($studentlehrverbaende[$sem]) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach($studentlehrverbaende[$sem_date] as $key=>$lehrverband)
|
||||
$studlvbds = array_filter(
|
||||
$studentlehrverbaende[$sem],
|
||||
function($value) use ($sem_date) {
|
||||
return $value->studiensemester_kurzbz === $sem_date;
|
||||
}
|
||||
);
|
||||
foreach($studlvbds as $key=>$lehrverband)
|
||||
{
|
||||
$query .= "(((sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND sp.verband = ".$this->escape($lehrverband->verband)." AND sp.gruppe = ".$this->escape($lehrverband->gruppe)." AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende).")";
|
||||
// Eintraege fuer den ganzen Verband
|
||||
|
||||
@@ -41,6 +41,7 @@ with gbt as (
|
||||
gb.anmerkung,
|
||||
gb.dienstverhaeltnis_id,
|
||||
gb.gehaltstyp_kurzbz,
|
||||
gb.gehaltsanpassungtyp_kurzbz,
|
||||
gb.valorisierungssperre,
|
||||
gb.valorisierung,
|
||||
gb.grundbetrag as grund_betrag_decrypted,
|
||||
@@ -76,11 +77,13 @@ select
|
||||
gbt.anmerkung,
|
||||
gbt.dienstverhaeltnis_id,
|
||||
gbt.gehaltstyp_kurzbz,
|
||||
gbt.gehaltsanpassungtyp_kurzbz,
|
||||
gbt.valorisierungssperre,
|
||||
gbt.valorisierung,
|
||||
gbt.grund_betrag_decrypted,
|
||||
gbt.betrag_val_decrypted,
|
||||
gt.bezeichnung as gehaltstyp_bezeichnung,
|
||||
ga.bezeichnung as gehaltsanpassungtyp_bezeichnung,
|
||||
vb.vertragsbestandteiltyp_kurzbz,
|
||||
bf.funktion_kurzbz,
|
||||
bf.oe_kurzbz,
|
||||
@@ -93,6 +96,8 @@ from
|
||||
gbt
|
||||
LEFT JOIN
|
||||
hr.tbl_gehaltstyp gt using(gehaltstyp_kurzbz)
|
||||
LEFT JOIN
|
||||
hr.tbl_gehaltsanpassungtyp ga using(gehaltsanpassungtyp_kurzbz)
|
||||
LEFT JOIN
|
||||
hr.tbl_vertragsbestandteil vb using(vertragsbestandteil_id)
|
||||
LEFT JOIN
|
||||
@@ -177,7 +182,8 @@ LEFT JOIN
|
||||
$stichtagclause .= ')';
|
||||
}
|
||||
|
||||
$this->addSelect('*');
|
||||
$this->addSelect('tbl_gehaltsbestandteil.*, tbl_gehaltstyp.bezeichnung AS gehaltstyp_bezeichnung');
|
||||
$this->addJoin('hr.tbl_gehaltstyp', 'gehaltstyp_kurzbz');
|
||||
$where = <<<EOSQL
|
||||
dienstverhaeltnis_id = {$this->escape($dienstverhaeltnis_id)}
|
||||
{$stichtagclause}
|
||||
@@ -202,6 +208,7 @@ SELECT
|
||||
g.dienstverhaeltnis_id,
|
||||
g.vertragsbestandteil_id,
|
||||
g.gehaltstyp_kurzbz,
|
||||
g.gehaltsanpassungtyp_kurzbz,
|
||||
g.von,
|
||||
g.bis,
|
||||
g.anmerkung,
|
||||
@@ -213,9 +220,11 @@ SELECT
|
||||
g.updateamum,
|
||||
g.updatevon,
|
||||
g.valorisierung,
|
||||
g.auszahlungen
|
||||
g.auszahlungen,
|
||||
gt.bezeichnung AS gehaltstyp_bezeichnung
|
||||
FROM
|
||||
hr.tbl_gehaltsbestandteil g
|
||||
hr.tbl_gehaltsbestandteil g
|
||||
JOIN hr.tbl_gehaltstyp gt USING (gehaltstyp_kurzbz)
|
||||
LEFT JOIN
|
||||
hr.tbl_valorisierung_historie vh ON vh.gehaltsbestandteil_id = g.gehaltsbestandteil_id AND vh.valorisierungsdatum = (
|
||||
SELECT
|
||||
@@ -263,7 +272,7 @@ EOSQL;
|
||||
// Note: replaced gb.betrag_valorisiert with vh.betrag_valorisiert!
|
||||
$qry = "
|
||||
SELECT
|
||||
gb.gehaltsbestandteil_id,gb.dienstverhaeltnis_id,gb.vertragsbestandteil_id,gb.gehaltstyp_kurzbz,
|
||||
gb.gehaltsbestandteil_id,gb.dienstverhaeltnis_id,gb.vertragsbestandteil_id,gb.gehaltstyp_kurzbz,gb.gehaltsanpassungtyp_kurzbz,
|
||||
gb.von,gb.bis,gb.anmerkung,gb.grundbetrag as grundbetrag,gb.valorisierungssperre,gb.insertamum,
|
||||
gb.insertvon,gb.updateamum,gb.updatevon,gb.valorisierung,gb.auszahlungen,
|
||||
vh.valorisierungsdatum, vh.betrag_valorisiert as betrag_valorisiert
|
||||
|
||||
@@ -16,7 +16,7 @@ $query = '
|
||||
anrechnung.begruendung_id,
|
||||
anrechnung.dms_id,
|
||||
CASE
|
||||
WHEN stg.typ || stg.kurzbz IN (' . $ORGANISATIONSEINHEITEN_SCHREIBBERECHTIGT . ') THEN TRUE
|
||||
WHEN stg.oe_kurzbz IN (' . $ORGANISATIONSEINHEITEN_SCHREIBBERECHTIGT . ') THEN TRUE
|
||||
ELSE FALSE
|
||||
END "schreibberechtigt",
|
||||
anrechnung.studiensemester_kurzbz,
|
||||
|
||||
@@ -7,6 +7,7 @@ $includesArray = array(
|
||||
'fontawesome6' => true,
|
||||
'axios027' => true,
|
||||
'primevue3' => true,
|
||||
'printCssPath' => 'public/css/Cis4/print.css',
|
||||
'customJSModules' => array_merge([
|
||||
'public/js/apps/Cis/Menu.js'
|
||||
], $customJSModules ?? []),
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
$customJSs = isset($customJSs) ? $customJSs : null;
|
||||
$customJSModules = isset($customJSModules) ? $customJSModules : null;
|
||||
$skipID = isset($skipID) ? $skipID : null;
|
||||
$printCssPath = isset($printCssPath) ? $printCssPath : null;
|
||||
|
||||
?>
|
||||
<!-- Header start -->
|
||||
|
||||
@@ -138,6 +140,9 @@
|
||||
generateCSSsIncludeIfExtensionCssExists('tags.css');
|
||||
}
|
||||
|
||||
// Print CSS
|
||||
if ($printCssPath) generatePrintCSS($printCssPath);
|
||||
|
||||
$extapphelper = ExtendableAppsHelper::getInstance();
|
||||
$extapphelper->init($customCSSs, $customJSs, $customJSModules);
|
||||
|
||||
|
||||
@@ -59,12 +59,21 @@
|
||||
padding: .25em .5em .25em .25em;
|
||||
margin-inline-end: .25rem;
|
||||
}
|
||||
.rangePageContainer .calendar-event-default .event-time {
|
||||
display: none !important;
|
||||
}
|
||||
.weekPageContainer .calendar-event-default .event-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
}
|
||||
.rangePageContainer .calendar-event-default .event-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: start;
|
||||
}
|
||||
.dayPageContainer .calendar-event-default,
|
||||
.listPageContainer .calendar-event-default {
|
||||
display: flex;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
@page {
|
||||
size: landscape;
|
||||
}
|
||||
|
||||
* {
|
||||
background-color: white !important;
|
||||
color: black !important;
|
||||
font-size: x-small !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.fhc-calendar-base-slider > div {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
header,
|
||||
#skiplink,
|
||||
#cis-lvplan-personal-heading,
|
||||
.cis-lvplan-personal hr,
|
||||
.fhc-calendar-base-header,
|
||||
.fhc-calendar-mode-range .grid-allday,
|
||||
.fhc-calendar-mode-range #rangePresetSelector,
|
||||
.fhc-calendar-base-grid-line-background {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fhc-calendar-mode-range .main-header-sunday,
|
||||
.fhc-calendar-mode-range .fhc-calendar-base-grid-line-sunday {
|
||||
border-bottom-width: 0 !important;
|
||||
}
|
||||
|
||||
.fhc-calendar-base-grid-line-sunday,
|
||||
.fhc-calendar-mode-range .main-header-sunday {
|
||||
break-after: page !important;
|
||||
}
|
||||
|
||||
.fhc-calendar-mode-range .fhc-calendar-base-grid {
|
||||
grid-template-rows: auto repeat(var(--fhc-grid-displayed-axis-main-count), auto) !important;
|
||||
gap: 0px !important;
|
||||
}
|
||||
|
||||
.fhc-calendar-mode-range .fhc-calendar-base-grid * {
|
||||
gap: 0px !important;
|
||||
}
|
||||
|
||||
.fhc-calendar-mode-range .main-header:not(.main-header-empty),
|
||||
.fhc-calendar-mode-range .fhc-calendar-base-grid-line {
|
||||
border-top: solid !important;
|
||||
border-top-color: black !important;
|
||||
border-top-width: 1px !important;
|
||||
}
|
||||
@@ -62,3 +62,16 @@
|
||||
top:13%;"
|
||||
z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
|
||||
}*/
|
||||
|
||||
.tag-hover:hover {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.btn-hover {
|
||||
--bs-btn-hover-bg: #e9ecef; /* Neue Hintergrundfarbe beim Hover */
|
||||
--bs-btn-hover-border-color: #e9ecef; /* Neue Rahmenfarbe beim Hover */
|
||||
}
|
||||
|
||||
@@ -104,11 +104,17 @@
|
||||
.fhc-calendar-base-grid-line {
|
||||
grid-gap: var(--fhc-calendar-gap-events, var(--fhc-calendar-gap, 1px));
|
||||
}
|
||||
.fhc-calendar-mode-range .main-header-sunday,
|
||||
.fhc-calendar-mode-range .all-day-events-sunday,
|
||||
.fhc-calendar-mode-range .fhc-calendar-base-grid-line-sunday {
|
||||
border: solid;
|
||||
border-width: 0 0 8px 0;
|
||||
border-color: var(--fhc-calendar-gap);
|
||||
}
|
||||
.fhc-calendar-base-label-time {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: end;
|
||||
}
|
||||
/* Header */
|
||||
.fhc-calendar-base-header .header-picker {
|
||||
|
||||
@@ -145,3 +145,8 @@
|
||||
float: right;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.btn-hover {
|
||||
--bs-btn-hover-bg: #e9ecef;
|
||||
--bs-btn-hover-border-color: #e9ecef;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ const router = VueRouter.createRouter({
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
|
||||
path: `/Cis/MyLvPlan/:mode?/:focus_date?/:range_length?`,
|
||||
name: 'MyLvPlan',
|
||||
component: MyLvPlan,
|
||||
props(route) {
|
||||
|
||||
@@ -93,6 +93,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
btnRange: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
timeGrid: Array,
|
||||
draggableEvents: [Boolean, Array, Function],
|
||||
dropableEvents: [Boolean, Array, Function],
|
||||
@@ -163,8 +167,16 @@ export default {
|
||||
return date.setLocale(this.locale);
|
||||
},
|
||||
set(value) {
|
||||
this.internalDate = value;
|
||||
this.$emit('update:date', value, this.cMode);
|
||||
let date;
|
||||
let rangeLength;
|
||||
if (value instanceof luxon.DateTime) {
|
||||
date = value;
|
||||
} else {
|
||||
date = value.date;
|
||||
rangeLength = value.rangeLength;
|
||||
}
|
||||
this.internalDate = date;
|
||||
this.$emit('update:date', date, this.cMode, rangeLength);
|
||||
}
|
||||
},
|
||||
sMode() {
|
||||
@@ -282,6 +294,7 @@ export default {
|
||||
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
|
||||
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
|
||||
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
|
||||
:btn-range="!!modes['range'] && (btnRange || (showBtns && btnRange !== false))"
|
||||
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
|
||||
>
|
||||
<slot name="actions" />
|
||||
@@ -293,6 +306,7 @@ export default {
|
||||
@update:range="$emit('update:range', $event)"
|
||||
@request-modal-open="showEventModal"
|
||||
@request-modal-close="hideEventModal"
|
||||
@update:date="cDate = $event"
|
||||
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
|
||||
>
|
||||
<template v-slot="slot"><slot v-bind="slot" /></template>
|
||||
|
||||
@@ -53,7 +53,11 @@ export default {
|
||||
flipAxis: Boolean,
|
||||
allDayEvents: Boolean,
|
||||
axisMainCollapsible: Boolean,
|
||||
snapToGrid: Boolean
|
||||
snapToGrid: Boolean,
|
||||
arePartHeadersRepeated: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -110,6 +114,19 @@ export default {
|
||||
return this.axisParts.slice(0, -1);
|
||||
return this.axisParts;
|
||||
},
|
||||
displayedAxisMain() {
|
||||
if (!this.$props.arePartHeadersRepeated)
|
||||
return this.$props.axisMain;
|
||||
|
||||
let displayedAxisMain = [];
|
||||
this.$props.axisMain.forEach((part, index) => {
|
||||
if (part.weekday === 1 && index > 0) {
|
||||
displayedAxisMain.push(null);
|
||||
}
|
||||
displayedAxisMain.push(part);
|
||||
});
|
||||
return displayedAxisMain;
|
||||
},
|
||||
start() {
|
||||
return this.axisPartsWithBreaks[0].start;
|
||||
},
|
||||
@@ -128,15 +145,17 @@ export default {
|
||||
return ends;
|
||||
},
|
||||
axisMainBorders() {
|
||||
return this.axisMain.reduce(
|
||||
(res, curr) => res.concat([curr.plus(this.start), curr.plus(this.end)]),
|
||||
return this.displayedAxisMain.reduce(
|
||||
(res, curr) => res.concat(curr ? [curr.plus(this.start), curr.plus(this.end)] : [null, null]),
|
||||
[]
|
||||
);
|
||||
},
|
||||
eventsAllDay() {
|
||||
if (!this.allDayEvents)
|
||||
return [];
|
||||
return this.mapIntoMainAxis(this.originalEvents.filter(event => event.orig.allDayEvent));
|
||||
|
||||
let eventsAllDay = this.mapIntoMainAxis(this.originalEvents.filter(event => event.orig.allDayEvent));
|
||||
return eventsAllDay;
|
||||
},
|
||||
eventsNormal() {
|
||||
if (!this.allDayEvents)
|
||||
@@ -153,7 +172,7 @@ export default {
|
||||
return this.events.find(e => e.length);
|
||||
},
|
||||
styleGridCols() {
|
||||
let cols = 'repeat(' + this.axisMain.length + ', 1fr)';
|
||||
let cols = 'repeat(' + this.displayedAxisMain.length + ', 1fr)';
|
||||
if (this.axisMainCollapsible) {
|
||||
if (this.hasValidEvents)
|
||||
cols = this.events
|
||||
@@ -181,7 +200,7 @@ export default {
|
||||
});
|
||||
|
||||
this.eventsNormal.forEach((events, mainIndex) => {
|
||||
let day = this.axisMain[mainIndex];
|
||||
let day = this.displayedAxisMain[mainIndex];
|
||||
events.forEach(event => {
|
||||
if (!event.startsHere && !event.endsHere)
|
||||
return;
|
||||
@@ -211,15 +230,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
mapIntoMainAxis(target) {
|
||||
const result = Array.from({length: this.axisMain.length}, () => Array());
|
||||
const result = Array.from({length: this.displayedAxisMain.length}, () => Array());
|
||||
|
||||
target.forEach(event => {
|
||||
const start = event.start || this.axisMainBorders[0].plus(-1);
|
||||
const end = event.end || this.axisMainBorders[this.axisMainBorders.length - 1].plus(1);
|
||||
|
||||
for (var i = 0; i < this.axisMain.length; i++) {
|
||||
let laneStart = this.axisMainBorders[i * 2];
|
||||
let laneEnd = this.axisMainBorders[i * 2 + 1];
|
||||
this.displayedAxisMain.forEach((part, index) => {
|
||||
if (!part) return;
|
||||
|
||||
let laneStart = this.axisMainBorders[index * 2];
|
||||
let laneEnd = this.axisMainBorders[index * 2 + 1];
|
||||
if (event.orig?.allDayEvent) {
|
||||
laneStart = laneStart.startOf('day');
|
||||
laneEnd = laneEnd.endOf('day');
|
||||
@@ -227,13 +248,13 @@ export default {
|
||||
if (start < laneEnd && end > laneStart) {
|
||||
const startsHere = start >= laneStart;
|
||||
const endsHere = end <= laneEnd;
|
||||
result[i].push({
|
||||
result[index].push({
|
||||
...event,
|
||||
startsHere,
|
||||
endsHere
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
@@ -317,22 +338,31 @@ export default {
|
||||
<div
|
||||
class="fhc-calendar-base-grid"
|
||||
style="display:grid;width:100%;height:100%;overflow:auto"
|
||||
:style="'grid-template-' + axisRow + 's:auto' + (allDayEvents ? ' auto ' : ' ') + '1fr;grid-template-' + axisCol + 's:auto ' + styleGridCols"
|
||||
:style="'--fhc-grid-displayed-axis-main-count: ' + displayedAxisMain.length + ';grid-template-' + axisRow + 's:auto' + (allDayEvents ? ' auto ' : ' ') + '1fr;grid-template-' + axisCol + 's:auto ' + styleGridCols"
|
||||
>
|
||||
<div
|
||||
class="grid-header"
|
||||
style="display:grid"
|
||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'"
|
||||
>
|
||||
<div
|
||||
v-for="(date, index) in axisMain"
|
||||
:key="index"
|
||||
class="main-header"
|
||||
:class="{'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length}"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||
>
|
||||
<slot name="main-header" v-bind="{ index, date }" />
|
||||
</div>
|
||||
<template v-for="(date, index) in displayedAxisMain" :key="index">
|
||||
<div
|
||||
v-if="date"
|
||||
class="main-header"
|
||||
:class="{
|
||||
'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length,
|
||||
'main-header-sunday': date.weekday === 7,
|
||||
}"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||
>
|
||||
<slot name="main-header" v-bind="{ index, date }" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="main-header main-header-empty"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||
></div>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
v-if="allDayEvents"
|
||||
@@ -340,26 +370,34 @@ export default {
|
||||
style="display:grid"
|
||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'"
|
||||
>
|
||||
<div
|
||||
v-for="(events, index) in eventsAllDay"
|
||||
:key="index"
|
||||
class="all-day-events"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||
>
|
||||
<grid-line-event
|
||||
v-for="(event, i) in events"
|
||||
:key="i"
|
||||
:event="event"
|
||||
<template v-for="(events, index) in eventsAllDay" :key="index">
|
||||
<div
|
||||
v-if="displayedAxisMain[index]"
|
||||
class="all-day-events"
|
||||
:class="{'all-day-events-sunday': displayedAxisMain[index].weekday === 7}"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||
>
|
||||
<template v-slot="slot">
|
||||
<slot name="event" v-bind="slot" />
|
||||
</template>
|
||||
</grid-line-event>
|
||||
</div>
|
||||
<grid-line-event
|
||||
v-for="(event, i) in events"
|
||||
:key="i"
|
||||
:event="event"
|
||||
>
|
||||
<template v-slot="slot">
|
||||
<slot name="event" v-bind="slot" />
|
||||
</template>
|
||||
</grid-line-event>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="all-day-events"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||
></div>
|
||||
</template>
|
||||
</div>
|
||||
<div
|
||||
ref="scroller"
|
||||
@scrollend="userScroll ? disableAutoScroll() : userScroll = true"
|
||||
id="grid-main-scrollable"
|
||||
style="display:grid;overflow:auto"
|
||||
:style="'grid-' + axisCol + ':1/-1;grid-template-' + axisCol + 's:subgrid'"
|
||||
>
|
||||
@@ -371,9 +409,9 @@ export default {
|
||||
>
|
||||
<div
|
||||
v-for="(part, index) in axisPartsSave"
|
||||
:key="index"
|
||||
:key="index"2
|
||||
class="part-header"
|
||||
:style="'grid-' + axisCol + ':1;grid-' + axisRow + ': ps_' + index + '/pe_' + index"
|
||||
:style="'grid-' + axisCol + ':1;grid-' + axisRow + ': ps_' + index + '/pe_' + index + ';min-width:50px;'"
|
||||
>
|
||||
<slot name="part-header" v-bind="{ index, part }" />
|
||||
</div>
|
||||
@@ -389,43 +427,56 @@ export default {
|
||||
@dragover="dropAllowed ? $event.preventDefault() : null"
|
||||
>
|
||||
<template
|
||||
v-for="(date, index) in axisMain"
|
||||
v-for="(date, index) in displayedAxisMain"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
v-for="(part, i) in axisPartsSave"
|
||||
:key="i"
|
||||
class="part-body"
|
||||
style="position:relative"
|
||||
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||
>
|
||||
<slot name="part-body" v-bind="{ index, part }" />
|
||||
<template v-if="date">
|
||||
<div
|
||||
v-if="snapToGrid && dragging"
|
||||
style="position:absolute;inset:0;z-index:1"
|
||||
v-cal-dnd:dropzone.once="{date: date.plus(part.start || part), ends: ends.slice(ends.findIndex(end => end > date))}"
|
||||
></div>
|
||||
</div>
|
||||
<grid-line
|
||||
:start="date.plus(start)"
|
||||
:end="date.plus(end)"
|
||||
:date="date"
|
||||
:events="eventsNormal[index]"
|
||||
:backgrounds="backgrounds[index]"
|
||||
style="position:relative"
|
||||
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
|
||||
>
|
||||
<template #event="slot">
|
||||
<slot name="event" v-bind="slot" />
|
||||
</template>
|
||||
<template #dropzone>
|
||||
v-for="(part, i) in axisPartsSave"
|
||||
:key="i"
|
||||
class="part-body"
|
||||
style="position:relative"
|
||||
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||
>
|
||||
<slot name="part-body" v-bind="{ index, part }" />
|
||||
<div
|
||||
v-if="!snapToGrid && dragging"
|
||||
v-if="snapToGrid && dragging"
|
||||
style="position:absolute;inset:0;z-index:1"
|
||||
v-cal-dnd:dropzone="evt => getTimestampFromMouse(evt, date)"
|
||||
v-cal-dnd:dropzone.once="{date: date.plus(part.start || part), ends: ends.slice(ends.findIndex(end => end > date))}"
|
||||
></div>
|
||||
</template>
|
||||
</grid-line>
|
||||
</div>
|
||||
<grid-line
|
||||
:start="date.plus(start)"
|
||||
:end="date.plus(end)"
|
||||
:date="date"
|
||||
:events="eventsNormal[index]"
|
||||
:backgrounds="backgrounds[index]"
|
||||
style="position:relative"
|
||||
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
|
||||
>
|
||||
<template #event="slot">
|
||||
<slot name="event" v-bind="slot" />
|
||||
</template>
|
||||
<template #dropzone>
|
||||
<div
|
||||
v-if="!snapToGrid && dragging"
|
||||
style="position:absolute;inset:0;z-index:1"
|
||||
v-cal-dnd:dropzone="evt => getTimestampFromMouse(evt, date)"
|
||||
></div>
|
||||
</template>
|
||||
</grid-line>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="(part, i) in axisPartsSave"
|
||||
:key="i"
|
||||
class="part-header-repeat part-body"
|
||||
style="position:relative"
|
||||
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||
>
|
||||
<slot name="part-header-repeat" v-bind="{ index, part }" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -123,6 +123,9 @@ export default {
|
||||
template: /* html */ `
|
||||
<div
|
||||
class="fhc-calendar-base-grid-line"
|
||||
:class="{
|
||||
'fhc-calendar-base-grid-line-sunday': $props.date.weekday === 7
|
||||
}"
|
||||
style="position:relative;display:grid;grid-auto-flow:dense"
|
||||
:style="'grid-template-' + axisRow + 's:subgrid'"
|
||||
>
|
||||
|
||||
@@ -24,7 +24,8 @@ export default {
|
||||
btnMonth: Boolean,
|
||||
btnWeek: Boolean,
|
||||
btnDay: Boolean,
|
||||
btnList: Boolean
|
||||
btnList: Boolean,
|
||||
btnRange: Boolean,
|
||||
},
|
||||
emits: [
|
||||
"next",
|
||||
@@ -35,7 +36,8 @@ export default {
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
open: false
|
||||
open: false,
|
||||
semesterOptions: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -53,6 +55,15 @@ export default {
|
||||
</div>
|
||||
<div class="header-modes">
|
||||
<div class="d-flex gap-1 justify-content-end" role="group">
|
||||
<button
|
||||
v-if="btnRange"
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
:class="{active: mode === 'range'}"
|
||||
@click="clickMode($event, 'range')"
|
||||
>
|
||||
<i class="fa fa-calendar"></i>
|
||||
</button>
|
||||
<button
|
||||
v-if="btnMonth"
|
||||
type="button"
|
||||
@@ -95,9 +106,10 @@ export default {
|
||||
<div class="header-picker">
|
||||
<div class="btn-group" role="group">
|
||||
<button
|
||||
class="btn btn-outline-secondary border-0"
|
||||
v-if="$props.mode !== 'range'"
|
||||
@click="$emit('prev')"
|
||||
:disabled="open"
|
||||
class="btn btn-outline-secondary border-0"
|
||||
>
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</button>
|
||||
@@ -110,9 +122,10 @@ export default {
|
||||
:list-length="modeOptions.length"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-outline-secondary border-0"
|
||||
v-if="$props.mode !== 'range'"
|
||||
@click="$emit('next')"
|
||||
:disabled="open"
|
||||
class="btn btn-outline-secondary border-0"
|
||||
>
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
|
||||
@@ -5,10 +5,11 @@ export default {
|
||||
components: {
|
||||
VueDatePicker
|
||||
},
|
||||
inject: [
|
||||
"locale",
|
||||
"timezone"
|
||||
],
|
||||
inject: {
|
||||
locale: "locale",
|
||||
timezone: "timezone",
|
||||
rangeLength: {default: 30}
|
||||
},
|
||||
props: {
|
||||
date: {
|
||||
type: luxon.DateTime,
|
||||
@@ -21,7 +22,7 @@ export default {
|
||||
listLength: {
|
||||
type: Number,
|
||||
default: 7
|
||||
}
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"update:date"
|
||||
@@ -38,6 +39,8 @@ export default {
|
||||
return {month: this.convertedDate.month-1, year: this.convertedDate.year};
|
||||
case "list":
|
||||
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
|
||||
case "range":
|
||||
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.rangeLength }).ts - 1];
|
||||
case "week":
|
||||
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
|
||||
case "day":
|
||||
@@ -56,6 +59,8 @@ export default {
|
||||
return this.$p.t('calendar/year_kw', { year, week });
|
||||
case "list":
|
||||
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
|
||||
case "range":
|
||||
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.rangeLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
|
||||
case "day":
|
||||
return this.date.toLocaleString(luxon.DateTime.DATE_FULL);
|
||||
default:
|
||||
@@ -64,11 +69,21 @@ export default {
|
||||
},
|
||||
weekStart() {
|
||||
return luxon.Info.getStartOfWeek(this.date)%7;
|
||||
}
|
||||
},
|
||||
rangeConfig() {
|
||||
if (this.$props.mode === "list") {
|
||||
return { autoRange: listLength - 1 };
|
||||
} else if (this.$props.mode === "range") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
update(value) {
|
||||
let date;
|
||||
let rangeLength;
|
||||
switch (this.mode) {
|
||||
case "month":
|
||||
value.month++;
|
||||
@@ -78,13 +93,18 @@ export default {
|
||||
case "week":
|
||||
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||
break;
|
||||
case "range":
|
||||
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||
let endDate = luxon.DateTime.fromJSDate(value[1]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||
rangeLength = Math.floor(endDate.diff(date, "days").toObject().days) + 1;
|
||||
break;
|
||||
case "day":
|
||||
date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||
break;
|
||||
default:
|
||||
return; // Don't update if the value is invalid!
|
||||
}
|
||||
this.$emit("update:date", date);
|
||||
this.$emit("update:date", { date, rangeLength });
|
||||
},
|
||||
weekNumbers(date) {
|
||||
return luxon.DateTime.fromJSDate(date, { locale: this.locale }).localWeekNumber;
|
||||
@@ -97,7 +117,7 @@ export default {
|
||||
:format="() => title"
|
||||
:month-picker="mode == 'month'"
|
||||
:week-picker="mode == 'week'"
|
||||
:range="mode == 'list' ? { autoRange: listLength - 1 } : false"
|
||||
:range="rangeConfig"
|
||||
:text-input="mode == 'day'"
|
||||
:week-start="weekStart"
|
||||
:week-numbers="{ type: weekNumbers }"
|
||||
|
||||
@@ -26,7 +26,11 @@ export default {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
alignItemsClassSuffix: {
|
||||
type: String,
|
||||
default: "end",
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
sanitizedTimestamps() {
|
||||
@@ -49,7 +53,7 @@ export default {
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="fhc-calendar-base-label-time">
|
||||
<div class="fhc-calendar-base-label-time" :class="'align-items-' + $props.alignItemsClassSuffix">
|
||||
<span v-if="start">{{ start }}</span>
|
||||
<span v-if="end">-</span>
|
||||
<span v-if="end">{{ end }}</span>
|
||||
|
||||
@@ -9,13 +9,21 @@ import ModeDay from './Mode/Day.js';
|
||||
import ModeWeek from './Mode/Week.js';
|
||||
import ModeMonth from './Mode/Month.js';
|
||||
import ModeList from './Mode/List.js';
|
||||
import ModeRange from './Mode/Range.js';
|
||||
|
||||
export default {
|
||||
name: "CalendarLvPlan",
|
||||
components: {
|
||||
FhcCalendar
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
inject: {
|
||||
isMobile: {
|
||||
default: false,
|
||||
},
|
||||
rangeLength: {
|
||||
default: 30,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
date: {
|
||||
type: [Date, String, Number, luxon.DateTime],
|
||||
@@ -28,7 +36,11 @@ export default {
|
||||
getPromiseFunc: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
shouldIncludeRangeMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
@@ -68,31 +80,37 @@ export default {
|
||||
backgrounds() {
|
||||
let now = luxon.DateTime.now().setZone(this.timezone);
|
||||
|
||||
if (this.mode == 'Month')
|
||||
if (this.mode == 'Month') {
|
||||
return [
|
||||
{
|
||||
class: 'background-past',
|
||||
end: now.startOf('day')
|
||||
}
|
||||
];
|
||||
|
||||
return [
|
||||
{
|
||||
class: 'background-past',
|
||||
end: now,
|
||||
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
||||
}
|
||||
];
|
||||
} else if (this.mode == 'Range') {
|
||||
return [];
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
class: 'background-past',
|
||||
end: now,
|
||||
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
||||
}
|
||||
];
|
||||
}
|
||||
},
|
||||
modes() {
|
||||
let modes = {
|
||||
day: Vue.markRaw(ModeDay),
|
||||
month: Vue.markRaw(ModeMonth),
|
||||
};
|
||||
if (this.isMobile) {
|
||||
modes.list = Vue.markRaw(ModeList);
|
||||
} else {
|
||||
if (!this.isMobile) {
|
||||
modes.week = Vue.markRaw(ModeWeek);
|
||||
if (this.$props.shouldIncludeRangeMode) {
|
||||
modes.range = Vue.markRaw(ModeRange);
|
||||
}
|
||||
} else {
|
||||
modes.list = Vue.markRaw(ModeList);
|
||||
}
|
||||
|
||||
return modes;
|
||||
@@ -163,7 +181,7 @@ export default {
|
||||
:backgrounds="backgrounds"
|
||||
:time-grid="teachingunits"
|
||||
show-btns
|
||||
@update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)"
|
||||
@update:date="(newDate, newMode, newRangeLength) => $emit('update:date', newDate, newMode, newRangeLength)"
|
||||
@update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)"
|
||||
@update:range="updateRange"
|
||||
>
|
||||
@@ -174,6 +192,9 @@ export default {
|
||||
>
|
||||
{{ $p.t('lehre/noLvFound') }}
|
||||
</div>
|
||||
<div v-else-if="!renderers || !renderers[event.type]" class="placeholder-glow">
|
||||
<span class="placeholder col-12"></span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
:class="'event-type-' + event.type + ' ' + mode + 'PageContainer'"
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import BaseSlider from "../Base/Slider.js";
|
||||
import RangeView from "./Range/View.js";
|
||||
import FormInput from "../../Form/Input.js";
|
||||
|
||||
export default {
|
||||
name: "ModeRange",
|
||||
components: {
|
||||
BaseSlider,
|
||||
RangeView,
|
||||
FormInput,
|
||||
},
|
||||
inject: {
|
||||
rangeLength: {
|
||||
default: 30,
|
||||
},
|
||||
rangeViewPresets: {
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
currentDate: {
|
||||
type: luxon.DateTime,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:currentDate", "update:range", "click", "requestModalOpen"],
|
||||
data() {
|
||||
return {
|
||||
selectedRangePreset: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
range() {
|
||||
let first = this.$props.currentDate;
|
||||
let last = first.plus({ days: this.rangeLength });
|
||||
|
||||
return luxon.Interval.fromDateTimes(first, last);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentDate() {
|
||||
this.$emit("update:range", this.range);
|
||||
},
|
||||
rangeLength() {
|
||||
this.$emit("update:range", this.range);
|
||||
},
|
||||
selectedRangePreset() {
|
||||
if (!this.selectedRangePreset) return;
|
||||
|
||||
const preset = this.rangeViewPresets.presets.find(
|
||||
(preset) => preset.name === this.selectedRangePreset,
|
||||
);
|
||||
if (!preset) return;
|
||||
|
||||
this.$emit("update:date", {
|
||||
date: preset.startDate,
|
||||
rangeLength:
|
||||
preset.endDate.diff(preset.startDate, "days").days + 1,
|
||||
});
|
||||
|
||||
this.selectedRangePreset = null;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
viewAttrs() {
|
||||
const day = this.$props.currentDate.startOf("day");
|
||||
const rangeLength = parseInt(this.rangeLength);
|
||||
return { ...this.$attrs, day, rangeLength };
|
||||
},
|
||||
handleClickDefaults(evt) {
|
||||
switch (evt.detail.source) {
|
||||
case "day":
|
||||
// default: Set current-date
|
||||
this.$emit("update:currentDate", {
|
||||
date: evt.detail.value,
|
||||
});
|
||||
break;
|
||||
case "event":
|
||||
// default: Request Modal
|
||||
this.$emit("requestModalOpen", { event: evt.detail.value });
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit("update:range", this.range);
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div
|
||||
class="fhc-calendar-mode-range flex-grow-1 position-relative d-flex flex-column"
|
||||
@cal-click-default.capture="handleClickDefaults"
|
||||
>
|
||||
<div
|
||||
v-if="rangeViewPresets?.presets?.length"
|
||||
id="rangePresetSelector"
|
||||
class="w-100 d-flex flex-row gap-2 justify-content-center align-items-center py-2"
|
||||
>
|
||||
<span>{{ rangeViewPresets.label }}</span>
|
||||
<form-input
|
||||
name="rangePresetSelector"
|
||||
type="select"
|
||||
v-model="selectedRangePreset"
|
||||
>
|
||||
<option
|
||||
v-for="preset in rangeViewPresets.presets"
|
||||
:key="preset.name"
|
||||
:value="preset.name"
|
||||
>
|
||||
{{ preset.name }}
|
||||
</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<base-slider ref="slider" v-slot="slot">
|
||||
<range-view ref="view" v-bind="viewAttrs()">
|
||||
<template v-slot="slot"><slot v-bind="slot" mode="range" /></template>
|
||||
</range-view>
|
||||
</base-slider>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
import CalendarGrid from '../../Base/Grid.js';
|
||||
import LabelDay from '../../Base/Label/Day.js';
|
||||
import LabelDow from '../../Base/Label/Dow.js';
|
||||
import LabelTime from '../../Base/Label/Time.js';
|
||||
|
||||
export default {
|
||||
name: "RangeView",
|
||||
components: {
|
||||
CalendarGrid,
|
||||
LabelDay,
|
||||
LabelDow,
|
||||
LabelTime
|
||||
},
|
||||
inject: {
|
||||
timeGrid: "timeGrid",
|
||||
timezone: "timezone"
|
||||
},
|
||||
props: {
|
||||
day: {
|
||||
type: luxon.DateTime,
|
||||
required: true
|
||||
},
|
||||
collapseEmptyDays: Boolean,
|
||||
rangeLength: Number,
|
||||
},
|
||||
computed: {
|
||||
axisMain() {
|
||||
return Array.from({ length: this.$props.rangeLength }, (e, i) => this.$props.day.plus({ days: i }));
|
||||
},
|
||||
axisParts() {
|
||||
if (this.timeGrid) {
|
||||
// create {start, end} array
|
||||
return this.timeGrid.map(tu => {
|
||||
return {
|
||||
start: luxon.Duration.fromISOTime(tu.start),
|
||||
end: luxon.Duration.fromISOTime(tu.end)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// create 07:00-23:00
|
||||
return Array.from({ length: 17 }, (e, i) => luxon.Duration.fromObject({ hours: i + 7 }));
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isToday(date) {
|
||||
return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day');
|
||||
}
|
||||
},
|
||||
template: /* html */`
|
||||
<div class="fhc-calendar-mode-range-view h-100">
|
||||
<calendar-grid
|
||||
ref="grid"
|
||||
:axis-main="axisMain"
|
||||
:axis-parts="axisParts"
|
||||
:axis-main-collapsible="collapseEmptyDays"
|
||||
:snap-to-grid="!!timeGrid"
|
||||
:flip-axis="true"
|
||||
:arePartHeadersRepeated="true"
|
||||
all-day-events
|
||||
>
|
||||
<template #main-header="{ date }">
|
||||
<div :class="{ today: isToday(date) }">
|
||||
<label-dow
|
||||
v-bind="{ date }"
|
||||
@cal-click="evt => evt.detail.source = 'day'"
|
||||
/>
|
||||
<label-day
|
||||
v-bind="{ date }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #part-header="{ part }">
|
||||
<label-time v-bind="{ part }" :alignItemsClassSuffix="'center'" />
|
||||
</template>
|
||||
<template #part-header-repeat="{ part }">
|
||||
<label-time v-bind="{ part }" :alignItemsClassSuffix="'center'" />
|
||||
</template>
|
||||
<template #event="slot">
|
||||
<div v-if="slot.event.type == 'loading'" class="placeholder-glow h-100 opacity-50">
|
||||
<span class="placeholder w-100 h-100" />
|
||||
</div>
|
||||
<slot v-else v-bind="slot" />
|
||||
</template>
|
||||
</calendar-grid>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
@@ -79,6 +79,9 @@ export default {
|
||||
>
|
||||
{{ $p.t('lehre/noLvFound') }}
|
||||
</div>
|
||||
<div v-else-if="!renderers || !renderers[event.type]" class="placeholder-glow">
|
||||
<span class="placeholder col-12"></span>
|
||||
</div>
|
||||
<component
|
||||
v-else-if="mode == 'eventheader'"
|
||||
:is="renderers[event.type]?.modalTitle"
|
||||
|
||||
@@ -2,6 +2,7 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
|
||||
|
||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||
import ApiStudiensemester from '../../../api/factory/studiensemester.js';
|
||||
|
||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
|
||||
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
|
||||
@@ -23,9 +24,23 @@ export default {
|
||||
isMitarbeiter: false,
|
||||
isStudent: false,
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
semesterRangePresets: {
|
||||
label: null,
|
||||
presets: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
provide() {
|
||||
return {
|
||||
rangeLength: Vue.computed(() => {
|
||||
if (!this.$route.params.range_length) return 30;
|
||||
else if (this.$route.params.range_length > 365) return 365;
|
||||
else return this.$route.params.range_length;
|
||||
}),
|
||||
rangeViewPresets: Vue.computed(() => this.semesterRangePresets)
|
||||
};
|
||||
},
|
||||
computed:{
|
||||
currentDay() {
|
||||
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
||||
@@ -34,7 +49,11 @@ export default {
|
||||
},
|
||||
currentMode() {
|
||||
let validModes = ["day", "month"];
|
||||
validModes.push(this.isMobile ? "list" : "week");
|
||||
if (!this.isMobile) {
|
||||
validModes.push("week", "range");
|
||||
} else {
|
||||
validModes.push("list");
|
||||
}
|
||||
|
||||
const defaultMode = this.isMobile
|
||||
? DEFAULT_MODE_LVPLAN_MOBILE
|
||||
@@ -94,10 +113,10 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChangeDate(day, newMode) {
|
||||
return this.handleChangeMode(newMode, day);
|
||||
handleChangeDate(day, newMode, rangeLength) {
|
||||
return this.handleChangeMode(newMode, day, rangeLength);
|
||||
},
|
||||
handleChangeMode(newMode, day) {
|
||||
handleChangeMode(newMode, day, range_length = null) {
|
||||
const mode = newMode[0].toUpperCase() + newMode.slice(1)
|
||||
const focus_date = day.toISODate();
|
||||
|
||||
@@ -105,7 +124,8 @@ export default {
|
||||
name: "MyLvPlan",
|
||||
params: {
|
||||
mode,
|
||||
focus_date
|
||||
focus_date,
|
||||
range_length,
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -134,13 +154,32 @@ export default {
|
||||
this.isMitarbeiter = authInfo.isMitarbeiter;
|
||||
this.isStudent = authInfo.isStudent;
|
||||
},
|
||||
async fetchSemesters() {
|
||||
const semestersResponse = await this.$api.call(ApiStudiensemester.getAll());
|
||||
if (semestersResponse.meta.status === "success") {
|
||||
this.semesterRangePresets = {
|
||||
label: "View specific semester",
|
||||
presets: semestersResponse.data.map((semester) => {
|
||||
let startDate = luxon.DateTime.fromISO(semester.start);
|
||||
let endDate = luxon.DateTime.fromISO(semester.ende);
|
||||
return {
|
||||
startDate,
|
||||
endDate,
|
||||
name: semester.studiensemester_kurzbz,
|
||||
description: semester.bezeichnung,
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
await this.fetchAuthInfo();
|
||||
await this.fetchSemesters();
|
||||
},
|
||||
template: /*html*/`
|
||||
<div class="cis-lvplan-personal d-flex flex-column h-100">
|
||||
<h2>
|
||||
<h2 id="cis-lvplan-personal-heading">
|
||||
{{ $p.t('lehre/stundenplan') }}
|
||||
<span v-if="studiensemester_kurzbz" class="ps-3">
|
||||
{{ studiensemester_kurzbz }}
|
||||
@@ -154,6 +193,7 @@ export default {
|
||||
:get-promise-func="getPromiseFunc"
|
||||
:date="currentDay"
|
||||
:mode="currentMode"
|
||||
:shouldIncludeRangeMode="true"
|
||||
@update:date="handleChangeDate"
|
||||
@update:mode="handleChangeMode"
|
||||
@update:range="updateRange"
|
||||
|
||||
@@ -32,7 +32,7 @@ export default {
|
||||
listGroup: [],
|
||||
rangeIntervalFirst: null,
|
||||
otherPersonData: {
|
||||
fullName: "",
|
||||
fullNameWithTitle: "",
|
||||
photo: "",
|
||||
},
|
||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||
@@ -210,10 +210,17 @@ export default {
|
||||
this.isOtherPersonMitarbeiter =
|
||||
!!viewData?.user_data?.is_mitarbeiter;
|
||||
this.isOtherPersonStudent = !!viewData?.user_data?.is_student;
|
||||
this.otherPersonData.fullName =
|
||||
viewData?.user_data?.vorname +
|
||||
" " +
|
||||
viewData?.user_data?.nachname;
|
||||
let fullNameWithTitleFragments = [
|
||||
viewData?.user_data?.titel,
|
||||
viewData?.user_data?.vorname,
|
||||
viewData?.user_data?.nachname,
|
||||
viewData?.user_data?.postnomen,
|
||||
];
|
||||
fullNameWithTitleFragments = fullNameWithTitleFragments.filter(
|
||||
(fragment) => !!fragment,
|
||||
);
|
||||
this.otherPersonData.fullNameWithTitle =
|
||||
fullNameWithTitleFragments.join(" ");
|
||||
this.otherPersonData.photo = viewData?.user_data?.foto;
|
||||
},
|
||||
async redirectToMyLvPlanIfAuthUid() {
|
||||
@@ -225,12 +232,21 @@ export default {
|
||||
this.$router.push({ name: "MyLvPlan" });
|
||||
}
|
||||
},
|
||||
copyFullNameWithTitleToClipboard() {
|
||||
navigator.clipboard.writeText(
|
||||
this.otherPersonData.fullNameWithTitle,
|
||||
);
|
||||
this.$fhcAlert.alertSuccess(
|
||||
this.$p.t("profil/name_title_clipboard_copy_confirmation"),
|
||||
);
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
await this.redirectToMyLvPlanIfAuthUid();
|
||||
await this.fetchViewData();
|
||||
await this.$p.loadCategory(["profil"]);
|
||||
},
|
||||
template: `
|
||||
template: /*html*/ `
|
||||
<div class="d-flex flex-column h-100">
|
||||
<h2>
|
||||
<div class="d-flex flex-row justify-content-between align-items-center">
|
||||
@@ -238,8 +254,11 @@ export default {
|
||||
{{ $p.t('lehre/stundenplan') + (studiensemester_kurzbz ? " " + studiensemester_kurzbz : "") }}
|
||||
</span>
|
||||
<div @click="this.$router.push({name: 'ProfilView', params: {uid: propsViewData.otherUid}})" type="button" class="d-flex flex-row align-items-center gap-3">
|
||||
<span v-if="otherPersonData.fullName?.length">
|
||||
{{ otherPersonData.fullName }}
|
||||
<span v-if="otherPersonData.fullNameWithTitle?.length">
|
||||
{{ otherPersonData.fullNameWithTitle }}
|
||||
<span @click.stop="copyFullNameWithTitleToClipboard()" :title="$p.t('LvPlan/copy')" class="fs-4 h-100">
|
||||
<i class="fa-regular fa-copy"></i>
|
||||
</span>
|
||||
</span>
|
||||
<img v-if="otherPersonData.photo?.length" alt="profile picture" class=" img-thumbnail " style=" max-height:60px; " :src="get_image_base64_src"/>
|
||||
</div>
|
||||
|
||||
@@ -67,6 +67,21 @@ export default {
|
||||
return res;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
copyFullNameWithTitleToClipboard() {
|
||||
let fragments = [
|
||||
this.$props.data.Titel,
|
||||
this.$props.data.Vorname,
|
||||
this.$props.data.Nachname,
|
||||
this.$props.data.Postnomen,
|
||||
];
|
||||
fragments = fragments.filter((fragment) => !!fragment);
|
||||
navigator.clipboard.writeText(fragments.join(" "));
|
||||
this.$fhcAlert.alertSuccess(
|
||||
this.$p.t("profil/name_title_clipboard_copy_confirmation"),
|
||||
);
|
||||
},
|
||||
},
|
||||
template: /*html*/ `
|
||||
|
||||
<div class="card h-100">
|
||||
@@ -124,6 +139,16 @@ export default {
|
||||
<span class="form-underline-content">{{wert?wert:'-'}} </span>
|
||||
</div>
|
||||
</div>
|
||||
<a
|
||||
@click="copyFullNameWithTitleToClipboard()"
|
||||
href="#"
|
||||
class="col-12 fhc-link-color d-flex flex-row gap-2 align-items-center justify-content-center mt-3"
|
||||
>
|
||||
<span>
|
||||
<i class="fa-regular fa-copy ms-2 text-decoration-none"></i>
|
||||
</span>
|
||||
<span>{{ $p.t("profil/name_title_copy") }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -206,6 +206,8 @@ export default {
|
||||
});
|
||||
|
||||
this.lehrveranstaltungen.forEach((lehrveranstaltung)=>{
|
||||
// a lehrveranstaltung without a modul has no children
|
||||
if(!Array.isArray(lehrveranstaltung.lehrveranstaltungen)) return;
|
||||
lehrveranstaltung.lehrveranstaltungen.sort((lv1,lv2)=>{
|
||||
if (lv1.bezeichnung.toLowerCase() > lv2.bezeichnung.toLowerCase()) {
|
||||
return 1;
|
||||
@@ -393,6 +395,16 @@ export default {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card" v-else-if="!Array.isArray(lehrveranstaltung.lehrveranstaltungen)">
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="d-flex list-group-item">
|
||||
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lehrveranstaltung)">{{isGermanLanguage ? lehrveranstaltung.bezeichnung : lehrveranstaltung.bezeichnung_english}}</a>
|
||||
<p>{{lehrveranstaltung.lehrform_kurzbz}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -62,6 +62,9 @@ export default {
|
||||
|
||||
return [type, result];
|
||||
}));
|
||||
},
|
||||
hiddenWidgets() {
|
||||
return this.widgets.filter(widget => widget.hidden);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -69,6 +72,9 @@ export default {
|
||||
this.$refs.widgetpicker
|
||||
.getWidget()
|
||||
.then(widget_id => {
|
||||
if (widget_id.hidden)
|
||||
this.widgetUnhide(widget_id, widget.place);
|
||||
|
||||
widget.widget = widget_id;
|
||||
// NOTE(chris): min size
|
||||
widget.place = Object.fromEntries(Object.entries(widget.place).map(([key, value]) => {
|
||||
@@ -131,17 +137,6 @@ export default {
|
||||
this.widgets.forEach((widget, i) => {
|
||||
if (failed.includes(widget.id)) {
|
||||
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
|
||||
/** NOTE(chris): if you wanna hide or unhide a
|
||||
* preset and it fails: switch around the hidden
|
||||
* value to revert it properly (checkboxes can't
|
||||
* really handle it otherwise)
|
||||
*/
|
||||
if (payload[widget.id].hidden !== undefined) {
|
||||
this.widgets[i].hidden = payload[widget.id].hidden;
|
||||
this.$nextTick(() => {
|
||||
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
|
||||
});
|
||||
}
|
||||
} else if (payload[widget.id]) {
|
||||
payload[widget.id].id = widget.id;
|
||||
payload[widget.id].index = widget.index;
|
||||
@@ -160,6 +155,31 @@ export default {
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
widgetUnhide(orig, place) {
|
||||
place = Object.fromEntries(Object.entries(place).map(([key, value]) => {
|
||||
value.w = this.sizeLimits[orig.widget].width.min;
|
||||
value.h = this.sizeLimits[orig.widget].height.min;
|
||||
// NOTE(chris): unpin pinned widgets because they got a new placement
|
||||
value.pinned = false;
|
||||
return [key, value];
|
||||
}));
|
||||
// NOTE(chris): clear placement in other screensizes
|
||||
Object.keys(orig.place).forEach(key => {
|
||||
if (!place[key]) {
|
||||
place[key] = {
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
pinned: false
|
||||
};
|
||||
}
|
||||
});
|
||||
let payload = {};
|
||||
payload[orig.id] = {
|
||||
hidden: false,
|
||||
place
|
||||
};
|
||||
return this.widgetUpdate(payload);
|
||||
},
|
||||
async fetchViewData() {
|
||||
let viewDataResult = await this.$api.call(ApiDashboard.getViewData());
|
||||
const viewData = viewDataResult.data;
|
||||
@@ -204,15 +224,23 @@ export default {
|
||||
},
|
||||
template: /* html */`
|
||||
<div class="core-dashboard">
|
||||
<h3>
|
||||
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
|
||||
<button
|
||||
class="btn ms-2"
|
||||
aria-label="edit dashboard"
|
||||
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/edit') }"
|
||||
@click="editMode = !editMode"
|
||||
><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
|
||||
</h3>
|
||||
<header class="d-flex justify-content-between align-items-baseline">
|
||||
<h3>
|
||||
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
|
||||
</h3>
|
||||
<div class="form-check form-check-reverse form-switch">
|
||||
<label class="form-check-label" for="switchEditMode">
|
||||
{{ $p.t('dashboard/edit') }}
|
||||
</label>
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
id="switchEditMode"
|
||||
v-model="editMode"
|
||||
>
|
||||
</div>
|
||||
</header>
|
||||
<dashboard-section
|
||||
name="general"
|
||||
:widgets="widgets"
|
||||
@@ -223,6 +251,7 @@ export default {
|
||||
<dashboard-widget-picker
|
||||
ref="widgetpicker"
|
||||
:widgets="widgetsSetup"
|
||||
:hidden-widgets="hiddenWidgets"
|
||||
></dashboard-widget-picker>
|
||||
</div>`
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ export default {
|
||||
"width",
|
||||
"height",
|
||||
"custom",
|
||||
"hidden",
|
||||
"editMode",
|
||||
"loading", // widget got added and is waiting for backend to save in db
|
||||
"item_data",
|
||||
@@ -115,14 +114,6 @@ export default {
|
||||
},
|
||||
ready() {
|
||||
return this.component && this.arguments !== null;
|
||||
},
|
||||
visible: {
|
||||
get() {
|
||||
return !this.hidden;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('remove', this.hidden);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -202,10 +193,8 @@ export default {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<article
|
||||
v-if="!hidden || editMode"
|
||||
class="dashboard-item card overflow-hidden h-100 position-relative"
|
||||
:class="{
|
||||
'hidden-widget': hidden,
|
||||
[arguments?.className]: arguments && arguments.className
|
||||
}"
|
||||
>
|
||||
@@ -299,9 +288,9 @@ export default {
|
||||
>
|
||||
<i class="fa-solid fa-gear" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!-- delete button -->
|
||||
<!-- delete/hide button -->
|
||||
<a
|
||||
v-if="custom && editMode"
|
||||
v-if="editMode"
|
||||
href="#"
|
||||
class="col-auto px-1"
|
||||
:aria-label="$p.t('dashboard/widget_delete')"
|
||||
@@ -310,19 +299,6 @@ export default {
|
||||
>
|
||||
<i class="fa-solid fa-trash" aria-hidden="true"></i>
|
||||
</a>
|
||||
<!-- hide button -->
|
||||
<Transition>
|
||||
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
v-model="visible"
|
||||
class="form-check-input ms-0"
|
||||
:value="true"
|
||||
:aria-label="$p.t('dashboard/widget_toggle_visibility')"
|
||||
>
|
||||
</div>
|
||||
</Transition>
|
||||
</header>
|
||||
<!-- TODO Manu rename/remove-->
|
||||
<div v-if="ready" class="card-body overflow-hidden p-0">
|
||||
|
||||
@@ -124,8 +124,6 @@ export default {
|
||||
return { ...item, ...placement, weight };
|
||||
});
|
||||
|
||||
if (this.editModeIsActive)
|
||||
return placedItems;
|
||||
return placedItems.filter(item => !item.hidden);
|
||||
}
|
||||
},
|
||||
@@ -141,22 +139,12 @@ export default {
|
||||
handleConfigClosed() {
|
||||
this.configOpened = false
|
||||
},
|
||||
removeWidget(item, revert) {
|
||||
removeWidget(item) {
|
||||
if (item.custom) {
|
||||
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', item.id, this.name));
|
||||
} else {
|
||||
let update = {};
|
||||
update[item.id] = { hidden: !revert };
|
||||
|
||||
if (!revert) {
|
||||
// NOTE(chris): move to last line
|
||||
update[item.id].place = [];
|
||||
let y = this.gridHeight;
|
||||
if (this.additionalRow)
|
||||
y--;
|
||||
update[item.id].place[this.gridWidth] = { x: 0, y };
|
||||
}
|
||||
|
||||
update[item.id] = { hidden: true };
|
||||
this.updatePreset(update);
|
||||
}
|
||||
},
|
||||
@@ -251,13 +239,12 @@ export default {
|
||||
:loading="item.loading"
|
||||
:config="item.config"
|
||||
:custom="item.custom"
|
||||
:hidden="item.hidden"
|
||||
:editMode="editModeIsActive"
|
||||
:place="item.place[gridWidth]"
|
||||
:widget-template="indexedWidgetsTemplates[item.widget]"
|
||||
:source="adminMode ? null : item.source || 'custom'"
|
||||
@change="saveConfig($event, item)"
|
||||
@remove="removeWidget(item, $event)"
|
||||
@remove="removeWidget(item)"
|
||||
@config-opened="handleConfigOpened"
|
||||
@config-closed="handleConfigClosed"
|
||||
@pin-item="updatePositions"
|
||||
|
||||
@@ -7,11 +7,21 @@ export default {
|
||||
WidgetIcon,
|
||||
},
|
||||
props: [
|
||||
"widgets"
|
||||
"widgets",
|
||||
"hiddenWidgets"
|
||||
],
|
||||
data: () => ({
|
||||
callbacks: {}
|
||||
}),
|
||||
computed: {
|
||||
hasAnyWidgets() {
|
||||
if (!this.widgets)
|
||||
return false;
|
||||
if (!this.widgets.length && !this.hiddenWidgets?.length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getWidget() {
|
||||
return new Promise((resolve,reject) => {
|
||||
@@ -37,23 +47,46 @@ export default {
|
||||
<bs-modal
|
||||
ref="modal"
|
||||
class="fade"
|
||||
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0 }"
|
||||
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': hasAnyWidgets }"
|
||||
@hiddenBsModal="close"
|
||||
>
|
||||
<template v-slot:title>{{ $p.t('dashboard/createWidget') }}</template>
|
||||
<template v-slot:default>
|
||||
<div v-if="widgets" class="row g-2">
|
||||
<div v-if="!widgets.length">
|
||||
{{ $p.t('dashboard/noWidgetsAvailable') }}
|
||||
</div>
|
||||
<template v-if="widgets && hiddenWidgets">
|
||||
<div
|
||||
v-for="widget in widgets"
|
||||
:key="widget.widget_id"
|
||||
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
|
||||
v-if="!widgets.length && !hiddenWidgets.length"
|
||||
class="row g-2"
|
||||
>
|
||||
<widget-icon @select="pick" :widget="widget"></widget-icon>
|
||||
<div>{{ $p.t('dashboard/noWidgetsAvailable') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-if="hiddenWidgets.length"
|
||||
class="row g-2"
|
||||
:class="widgets.length ? 'border-bottom pb-3 mb-3' : ''"
|
||||
>
|
||||
<div
|
||||
v-for="widget in hiddenWidgets"
|
||||
:key="widget.widget_id"
|
||||
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
|
||||
>
|
||||
<widget-icon @select="pick(widget)" :widget="widgets.find(w => w.widget_id == widget.widget)"></widget-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="widgets.length"
|
||||
class="row g-2"
|
||||
>
|
||||
<div
|
||||
v-for="widget in widgets"
|
||||
:key="widget.widget_id"
|
||||
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
|
||||
>
|
||||
<widget-icon @select="pick" :widget="widget"></widget-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<div v-else class="text-center">
|
||||
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
|
||||
</div>
|
||||
|
||||
@@ -400,32 +400,37 @@ export default {
|
||||
|
||||
<div v-if="headerData.length == 1">
|
||||
<div v-if="!isLoading" class="d-flex align-items-center gap-3">
|
||||
<h2 class="h4">
|
||||
<h2 class="h4 mt-2">
|
||||
{{headerData[0].titelpre}}
|
||||
{{headerData[0].vorname}}
|
||||
{{headerData[0].nachname}}
|
||||
<span v-if="headerData[0].titelpost">, </span>
|
||||
{{headerData[0].titelpost}}
|
||||
</h2>
|
||||
<core-tag ref="tagComponent"
|
||||
v-if="tagsEnabled"
|
||||
:endpoint="tagEndpoint"
|
||||
:values="prestudentIds"
|
||||
@added="addedTag"
|
||||
@deleted="deletedTag"
|
||||
@updated="updatedTag"
|
||||
zuordnung_typ="prestudent_id"
|
||||
></core-tag>
|
||||
<div
|
||||
role="button"
|
||||
v-if="tagsEnabled"
|
||||
@click="rebuildPrestudentTags"
|
||||
class="btn btn-outline btn-light mb-1"
|
||||
:title="'Automatische Tags fuer ' + currentSemester + ' neu laden'"
|
||||
>
|
||||
<i class="fa-solid fa-refresh pe-1"></i>
|
||||
<span>{{currentSemester}}</span>
|
||||
|
||||
<div class="d-flex border rounded align-items-center ps-1">
|
||||
<core-tag ref="tagComponent"
|
||||
v-if="tagsEnabled"
|
||||
:endpoint="tagEndpoint"
|
||||
:values="prestudentIds"
|
||||
:show-hover="true"
|
||||
@added="addedTag"
|
||||
@deleted="deletedTag"
|
||||
@updated="updatedTag"
|
||||
zuordnung_typ="prestudent_id"
|
||||
></core-tag>
|
||||
<div
|
||||
role="button"
|
||||
v-if="tagsEnabled"
|
||||
@click="rebuildPrestudentTags"
|
||||
class="btn btn-outline btn-sm btn-hover m-1"
|
||||
:title="$p.t('tag','rebuild_tags') + ' ' + currentSemester"
|
||||
>
|
||||
<i class="fa-solid fa-refresh pe-1"></i>
|
||||
<span>{{currentSemester}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
|
||||
</div>
|
||||
<div v-else class="d-flex align-items-center gap-3">
|
||||
|
||||
@@ -233,6 +233,14 @@ export default {
|
||||
if (value == oldValue)
|
||||
return;
|
||||
|
||||
/** NOTE(chris): tempPositionUpdates depends on the cols not to be
|
||||
* updated. So if they do we reset to get a clean value. In case the
|
||||
* drag and drop operation is still motion on the next tick this
|
||||
* will be correct again and if there is no such operation we clean
|
||||
* up.
|
||||
*/
|
||||
this.tempPositionUpdates = null;
|
||||
|
||||
this.reinitGrid();
|
||||
},
|
||||
rows: {
|
||||
@@ -246,6 +254,11 @@ export default {
|
||||
},
|
||||
indexedItems: {
|
||||
handler(value) {
|
||||
/** NOTE(chris): there was an update of the items, so in case
|
||||
* it came from a drag and drop operation we need to clean up
|
||||
*/
|
||||
this.tempPositionUpdates = null;
|
||||
|
||||
this.reinitGrid();
|
||||
},
|
||||
immediate: true,
|
||||
|
||||
+6
-2
@@ -553,8 +553,12 @@ export default {
|
||||
}
|
||||
|
||||
if (!this.formData.akadgrad_id && this.arrAkadGrad.length > 0) {
|
||||
if(this.student.geschlecht == 'w')
|
||||
this.formData.akadgrad_id = this.arrAkadGrad[1].akadgrad_id;
|
||||
const akadGradByGeschlecht = this.arrAkadGrad.find((item) => {
|
||||
return item.geschlecht === this.student.geschlecht;
|
||||
});
|
||||
|
||||
if(akadGradByGeschlecht)
|
||||
this.formData.akadgrad_id = akadGradByGeschlecht.akadgrad_id;
|
||||
else
|
||||
this.formData.akadgrad_id = this.arrAkadGrad[0].akadgrad_id;
|
||||
}
|
||||
|
||||
@@ -317,6 +317,7 @@ export default {
|
||||
"$p.user_language.value"(n, o) {
|
||||
if (n !== o && o !== undefined && this.$refs.table.tableBuilt) {
|
||||
this.translateTabulator();
|
||||
this.$reloadList();
|
||||
}
|
||||
},
|
||||
"tagFilterState.selectedOptions": {
|
||||
@@ -842,35 +843,39 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<core-tag ref="tagComponent"
|
||||
v-if="tagsEnabled"
|
||||
:endpoint="tagEndpoint"
|
||||
:values="selectedColumnValues"
|
||||
@added="addedTag"
|
||||
@deleted="deletedTag"
|
||||
@updated="updatedTag"
|
||||
zuordnung_typ="prestudent_id"
|
||||
></core-tag>
|
||||
<template #tags>
|
||||
<div class="d-flex border rounded align-items-center ps-1">
|
||||
<core-tag ref="tagComponent"
|
||||
v-if="tagsEnabled"
|
||||
:endpoint="tagEndpoint"
|
||||
:values="selectedColumnValues"
|
||||
@added="addedTag"
|
||||
@deleted="deletedTag"
|
||||
@updated="updatedTag"
|
||||
zuordnung_typ="prestudent_id"
|
||||
show-hover
|
||||
></core-tag>
|
||||
|
||||
<button
|
||||
v-if="!selected.length"
|
||||
class="btn btn-outline btn-light mb-1"
|
||||
@click="rebuild(selected)"
|
||||
:title="$p.t('tag','rebuild_tags') + ' Stg ' + currentSemester"
|
||||
>
|
||||
<i class="fa-solid fa-refresh pe-1"></i> STG
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-outline btn-light mb-1"
|
||||
@click="rebuild(selected)"
|
||||
:title="$p.t('tag','rebuild_tags') + ' ' + $p.t('ui','selection') + ' ' + currentSemester"
|
||||
>
|
||||
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
|
||||
</button>
|
||||
<button
|
||||
v-if="!selected.length"
|
||||
class="btn btn-outline btn-sm m-1 btn-hover"
|
||||
@click="rebuild(selected)"
|
||||
:title="$p.t('tag','rebuild_tags') + ' Stg ' + currentSemester"
|
||||
>
|
||||
<i class="fa-solid fa-refresh pe-1"></i> STG
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-outline btn-sm m-1 btn-hover"
|
||||
@click="rebuild(selected)"
|
||||
:title="$p.t('tag','rebuild_tags') + ' ' + $p.t('ui','selection') + ' ' + currentSemester"
|
||||
>
|
||||
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="filter.length || headerFilterActive">
|
||||
<template v-if="filter.length || headerFilterActive">
|
||||
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
|
||||
<p class="text-danger mb-0">
|
||||
<strong>{{$p.t('filter','filterActive')}}</strong>
|
||||
@@ -884,8 +889,6 @@ export default {
|
||||
<span class="fa-solid fa-filter-circle-xmark"></span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<template #filter>
|
||||
@@ -895,6 +898,7 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</core-filter-cmpt>
|
||||
</div>
|
||||
<list-new ref="new" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz"></list-new>
|
||||
|
||||
@@ -28,6 +28,10 @@ export default {
|
||||
confirmLimit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
showHover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -228,7 +232,8 @@ export default {
|
||||
<span :title="values.length === 0 ? 'Bitte Zeilen markieren' : ''">
|
||||
<button @mouseover="showList = true"
|
||||
:disabled="!values || values.length === 0"
|
||||
class="btn btn-sm">
|
||||
class="btn btn-sm"
|
||||
:class="{'btn-hover': showHover}">
|
||||
<i class="fa-solid fa-tag fa-xl"></i>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
@@ -1094,7 +1094,7 @@ export const CoreFilterCmpt = {
|
||||
<div :id="'filterCollapsables' + idExtra">
|
||||
|
||||
<div class="d-flex flex-row justify-content-between flex-wrap">
|
||||
<div v-if="newBtnShow || reload || $slots.search || $slots.actions" class="d-flex gap-2 align-items-baseline flex-wrap">
|
||||
<div v-if="newBtnShow || reload || $slots.search || $slots.actions || $slots.tags" class="d-flex gap-2 align-items-baseline flex-wrap">
|
||||
<button v-if="newBtnShow" class="btn btn-primary" :class="newBtnClass" :title="newBtnLabel ? undefined : 'New'" :aria-label="newBtnLabel ? undefined : 'New'" @click="$emit('click:new', $event)" :disabled="newBtnDisabled">
|
||||
<span class="fa-solid fa-plus" aria-hidden="true"></span>
|
||||
{{ newBtnLabel }}
|
||||
@@ -1107,6 +1107,7 @@ export const CoreFilterCmpt = {
|
||||
<span v-else id="selected-info-text"> Mit {{ selectedData.length }} ausgewählten:</span>
|
||||
</span>
|
||||
<slot name="actions" v-bind="{selected: tabulatorHasSelector ? selectedData : []}"></slot>
|
||||
<slot name="tags"></slot>
|
||||
<slot name="search"></slot>
|
||||
</div>
|
||||
<div class="d-flex gap-1 align-items-baseline flex-grow-1 justify-content-end">
|
||||
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
query: Vue.computed(() => this.lastQuery),
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
inject: { isMobile: { default: false } },
|
||||
data: function () {
|
||||
return {
|
||||
searchtimer: null,
|
||||
|
||||
@@ -100,6 +100,7 @@ require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
|
||||
require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php');
|
||||
require_once('dbupdate_3.4/76160_lvv_favorites.php');
|
||||
require_once('dbupdate_3.4/77080_tabulator_presets_table.php');
|
||||
require_once('dbupdate_3.4/78292_gehaltsanpassungtyp.php');
|
||||
|
||||
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
|
||||
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
if (! defined('DB_NAME')) exit('No direct script access allowed');
|
||||
|
||||
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_gehaltsanpassungtyp' AND table_schema='hr'"))
|
||||
{
|
||||
if ($db->db_num_rows($result) == 0)
|
||||
{
|
||||
$qry = "
|
||||
|
||||
ALTER TABLE
|
||||
hr.tbl_gehaltsbestandteil
|
||||
ADD COLUMN IF NOT EXISTS
|
||||
gehaltsanpassungtyp_kurzbz character varying(32);
|
||||
|
||||
COMMENT ON COLUMN hr.tbl_gehaltsbestandteil.gehaltsanpassungtyp_kurzbz IS E'Ersteinstufung, indiv. Erhöhung, etc.';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hr.tbl_gehaltsanpassungtyp
|
||||
(
|
||||
gehaltsanpassungtyp_kurzbz character varying(32) NOT NULL,
|
||||
bezeichnung varchar(256),
|
||||
sort smallint,
|
||||
aktiv boolean NOT NULL DEFAULT true,
|
||||
CONSTRAINT tbl_gehaltsanpassungtyp_pk PRIMARY KEY (gehaltsanpassungtyp_kurzbz)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE hr.tbl_gehaltsanpassungtyp IS E'Key-Table of Salary Adaption Types';
|
||||
|
||||
ALTER TABLE hr.tbl_gehaltsbestandteil DROP CONSTRAINT IF EXISTS tbl_gehaltsanpassungtyp_fk;
|
||||
ALTER TABLE hr.tbl_gehaltsbestandteil ADD CONSTRAINT tbl_gehaltsanpassungtyp_fk FOREIGN KEY (gehaltsanpassungtyp_kurzbz)
|
||||
REFERENCES hr.tbl_gehaltsanpassungtyp (gehaltsanpassungtyp_kurzbz) MATCH FULL
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
GRANT SELECT, UPDATE, INSERT, DELETE ON hr.tbl_gehaltsanpassungtyp TO vilesci;
|
||||
|
||||
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('ersteinstufung','Ersteinstufung', 1, true);
|
||||
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('individuelle_erhoehung','Indiv. Gehaltserhöhung', 2, true);
|
||||
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('strukturelle_anpassung','Strukturelle Anpassung', 3, true);
|
||||
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('funktionsaenderung','Funktionsänderung', 4, true);
|
||||
|
||||
";
|
||||
|
||||
if (! $db->db_query($qry))
|
||||
echo '<strong>Gehaltsanpassungtyp: ' . $db->db_last_error() . '</strong><br>';
|
||||
else
|
||||
echo 'Tabelle und Feld für Gehaltsanpassungtyp wurden neu erstellt<br>';
|
||||
}
|
||||
}
|
||||
@@ -29742,6 +29742,46 @@ array(
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'profil',
|
||||
'phrase' => 'name_title_clipboard_copy_confirmation',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Name mit Titel in die Zwischenablage kopiert.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Full name with title copied to clipboard.',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'profil',
|
||||
'phrase' => 'name_title_copy',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Name mit Titel kopieren',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Copy full name with title',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'profil',
|
||||
@@ -60852,6 +60892,26 @@ I have been informed that I am under no obligation to consent to the transmissio
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'app' => 'core',
|
||||
'category' => 'LvPlan',
|
||||
'phrase' => 'copy',
|
||||
'insertvon' => 'system',
|
||||
'phrases' => array(
|
||||
array(
|
||||
'sprache' => 'German',
|
||||
'text' => 'Kopieren',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
),
|
||||
array(
|
||||
'sprache' => 'English',
|
||||
'text' => 'Copy',
|
||||
'description' => '',
|
||||
'insertvon' => 'system'
|
||||
)
|
||||
)
|
||||
),
|
||||
// ### LvPlanStgOrg END ###
|
||||
// DMS-Link Phrasen Start
|
||||
array(
|
||||
|
||||
Reference in New Issue
Block a user