Compare commits

..
Author SHA1 Message Date
Harald Bamberger 424ed0c5bb do not load Model from Extension in Core 2026-10-13 12:32:49 +02:00
Harald Bamberger b0f1572576 Merge branch 'cis40_2026-09_ma_rc' 2026-10-13 11:10:32 +02:00
Harald Bamberger 03efc55c93 remove checked in symlink that is generated by composer 2026-10-13 10:37:25 +02:00
Harald Bamberger 95c4dc2734 use Compat component for gesamtnote 2026-10-13 10:26:19 +02:00
Harald Bamberger 94c019888a use legacy link for gesamtnote for the moment 2026-10-13 09:50:04 +02:00
Harald Bamberger ed83b5e617 zeitsperren: add Mailphrases, mailstatus handling backend and frontend 2026-10-13 07:56:59 +02:00
Harald Bamberger 510ce22cc9 Merge branch 'cis40_2026-09_ma_rc_bugfix-83851-cms-content-table-formatting' into cis40_2026-09_ma_rc 2026-10-12 17:54:07 +02:00
Harald Bamberger 53049504a7 modify Zeitsperren API Endpoints to send sancho Mails, modify update check so that a zeitsperre does not block an update on itself when overlapping, add phrases for sancho mail subject to ci phrase system 2026-10-12 17:13:52 +02:00
Harald Bamberger 3e06740758 add permissions cis/eigene_zeitsperren and cis/andere_zeitsperren and use them for zeitsperren and zeitsperrenma applications and api endpoints 2026-10-12 14:25:31 +02:00
adisposkofh 73d22e14e5 cms content tabulator formatting fix 2026-10-12 12:39:43 +02:00
Harald Bamberger bca2884aa2 CisMenuApp fetch Authinfo, Cis search use isMitarbeiter flag to conditinally render Zeitsperren Button in search result 2026-10-12 11:37:45 +02:00
Harald Bamberger 63d0bfc69b Merge branch 'cis40_2026-09_ma_rc_bugfix-84008-lvplan-with-lvid-shows-reservations' into cis40_2026-09_ma_rc 2026-10-12 10:30:29 +02:00
Harald Bamberger ab05f32fde Merge branch 'cis40_2026-09_ma_rc' into cis40_2026-09_ma_rc_bugfix-84008-lvplan-with-lvid-shows-reservations 2026-10-12 10:21:03 +02:00
adisposkofh cdd36f61b1 in lv plan not fetching reservations if lv id query param is present 2026-10-09 12:10:32 +02:00
48 changed files with 436 additions and 325 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ class Zeitsperren extends Auth_Controller
public function __construct()
{
parent::__construct([
'index' => ['basis/cis:r'],
'index' => ['cis/eigene_zeitsperren:r'],
]);
// Load Libraries
@@ -13,7 +13,7 @@ class Zeitsperrenma extends Auth_Controller
public function __construct()
{
parent::__construct([
'index' => ['basis/cis:r']
'index' => ['cis/andere_zeitsperren:r']
]);
}
@@ -379,7 +379,8 @@ class LvMenu extends FHCAPI_Controller
'name'=>$this->p->t('lehre', 'gesamtnote'),
'phrase' => 'lehre/gesamtnote',
'c4_icon'=> base_url('skin/images/button_endnote.png'),
'c4_link'=> site_url('Cis/Benotungstool/'.urlencode($lvid).'/'.urlencode($angezeigtes_stsem))
'c4_link'=> base_url('cis.php/Cis/Compat/legacy/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
//'c4_link'=> site_url('Cis/Benotungstool/'.urlencode($lvid).'/'.urlencode($angezeigtes_stsem))
//'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
//'c4_link'=> base_url('benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
);
@@ -7,14 +7,14 @@ class MaZeitsperren extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getAllActiveZeitsperren' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenFixeMa' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenLector' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenOes' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'getZeitsperrenAss' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'getAllActiveZeitsperren' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenFixeMa' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenLector' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getAllZeitsperrenOes' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getZeitsperrenAss' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getStgLectors' => self::PERM_LOGGED,
'loadZeitsperrenLectorStg' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'loadZeitsperrenMa' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'loadZeitsperrenLectorStg' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'loadZeitsperrenMa' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getDetailsMa' => self::PERM_LOGGED
]);
@@ -7,14 +7,14 @@ class Zeitsperren extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getZeitsperrenUser' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'getZeitsperrenUser' => ['cis/eigene_zeitsperren:r', 'admin:r', 'assistenz:r'],
'getTypenZeitsperren' => self::PERM_LOGGED,
'getTypenErreichbarkeit' => self::PERM_LOGGED,
'getStunden' => self::PERM_LOGGED,
'loadZeitsperre' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'add' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'update' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'delete' => ['basis/mitarbeiter:r', 'admin:r', 'assistenz:r'],
'loadZeitsperre' => ['cis/eigene_zeitsperren:r', 'admin:r', 'assistenz:r'],
'add' => ['cis/eigene_zeitsperren:rw', 'admin:rw', 'assistenz:rw'],
'update' => ['cis/eigene_zeitsperren:rw', 'admin:rw', 'assistenz:rw'],
'delete' => ['cis/eigene_zeitsperren:rw', 'admin:rw', 'assistenz:rw'],
]);
// Load Libraries
@@ -179,6 +179,21 @@ class Zeitsperren extends FHCAPI_Controller
);
$data = $this->getDataOrTerminateWithError($result);
if($zeitsperretyp_kurzbz == 'ZA')
{
$this->sendSanchoMailToVorgesetzten(
'Sancho_Content_Zeitausgleich',
array(
'beschreibung' =>$zeitsperretyp_kurzbz,
'vonDatum' => (new DateTime($vondatum))->format('d.m.Y'),
'bisDatum' => (new DateTime($bisdatum))->format('d.m.Y'),
'Saldo'=> ''
),
$this->p->t('zeitsperren', 'zeitausgleichNeu'),
$mitarbeiter_uid
);
}
$this->terminateWithSuccess($data);
}
@@ -285,9 +300,13 @@ class Zeitsperren extends FHCAPI_Controller
$zeitsperretyp_kurzbz = array_key_exists('zeitsperretyp_kurzbz', $post) ? $update['zeitsperretyp_kurzbz'] : $data->zeitsperretyp_kurzbz;
$result = $this->ZeitsperreModel->getSperreByDate($mitarbeiter_uid, $vondatum, $vonstunde, true);
$data = $this->getDataOrTerminateWithError($result);
$data = (array) $this->getDataOrTerminateWithError($result);
if(hasData($result))
$data = array_filter($data, function($zeitsperre) use ($zeitsperre_id) {
return $zeitsperre->zeitsperre_id != $zeitsperre_id;
});
if(count($data) > 0)
{
$this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitsperre', ['typ'=> current($data)->zeitsperretyp_kurzbz]), self::ERROR_TYPE_GENERAL);
}
@@ -309,6 +328,21 @@ class Zeitsperren extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
if($zeitsperretyp_kurzbz == 'ZA')
{
$this->sendSanchoMailToVorgesetzten(
'Sancho_Content_ZA_Aendern',
array(
'beschreibung' =>$zeitsperretyp_kurzbz,
'vonDatum' => (new DateTime($vondatum))->format('d.m.Y'),
'bisDatum' => (new DateTime($bisdatum))->format('d.m.Y'),
'Saldo'=> ''
),
$this->p->t('zeitsperren', 'zeitausgleichGeaendert'),
$mitarbeiter_uid
);
}
$this->terminateWithSuccess($data);
}
else
@@ -327,7 +361,8 @@ class Zeitsperren extends FHCAPI_Controller
$loggedInUser = getAuthUID();
$result = $this->ZeitsperreModel->load($zeitsperre_id);
$data = $this->getDataOrTerminateWithError($result);
$uid = current($data)->mitarbeiter_uid;
$zeitsperre = (is_array($data) && count($data) > 0) ? current($data) : null;
$uid = (!is_null($zeitsperre)) ? $zeitsperre->mitarbeiter_uid : '';
if($uid != $loggedInUser)
$this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL);
@@ -339,6 +374,21 @@ class Zeitsperren extends FHCAPI_Controller
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
if($zeitsperre->zeitsperretyp_kurzbz == 'ZA')
{
$res = $this->sendSanchoMailToVorgesetzten(
'Sancho_Mail_ZA_loeschen',
array(
'beschreibung' =>$zeitsperre->zeitsperretyp_kurzbz,
'vonDatum' => (new DateTime($zeitsperre->vondatum))->format('d.m.Y'),
'bisDatum' => (new DateTime($zeitsperre->bisdatum))->format('d.m.Y')
),
$this->p->t('zeitsperren', 'zeitausgleichGeloescht'),
$zeitsperre->mitarbeiter_uid
);
}
$this->terminateWithSuccess((getData($result) ?: []));
}
@@ -363,5 +413,57 @@ class Zeitsperren extends FHCAPI_Controller
return $tage <= 14;
}
protected function sendSanchoMailToVorgesetzten($sancho_vorlage_kurzbz,
$sancho_vorlage_data, $subject, $mauid)
{
$this->load->helper('hlp_sancho_helper');
$this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('person/Person_model', 'PersonModel');
$fullname = getData($this->PersonModel->getFullName($mauid));
$vorgesetzte = (array) getData($this->MitarbeiterModel->getVorgesetzte($mauid));
$to = array();
$vorgesetzte_namen = array();
$mailsent = false;
$msg = $this->p->t('zeitsperren', 'konnteKeinFreigabemailVersendetWerden');
if(count($vorgesetzte) > 0)
{
foreach($vorgesetzte as $vg)
{
$to[] = $vg->vorgesetzter .'@' . DOMAIN;
$vorgesetzter_fullname = getData($this->PersonModel->getFullName($vg->vorgesetzter));
$vorgesetzte_namen[] = $vorgesetzter_fullname;
}
$sancho_vorlage_data['vorgesetzter'] = implode(', ', $vorgesetzte_namen);
$sancho_vorlage_data['nameMitarbeiter'] = $fullname;
$mailsent = sendSanchoMail(
$sancho_vorlage_kurzbz,
$sancho_vorlage_data,
implode(',', $to),
$subject
);
if($mailsent)
{
$msg = $this->p->t('zeitsperren', 'VorgesetzteInformiert',
array('vorgesetzte' => $sancho_vorlage_data['vorgesetzter']));
}
else
{
$msg = $this->p->t('zeitsperren', 'fehlerBeimSendenAufgetreten',
array('vorgesetzte' => $sancho_vorlage_data['vorgesetzter']));
}
}
$res = (object) array(
'mailsent' => $mailsent,
'msg' => $msg
);
$this->addMeta('mailstatus', $res);
}
}
@@ -32,7 +32,6 @@ class Funktionen extends FHCAPI_Controller
]);
// Load models
$this->load->model('extensions/FHC-Core-Personalverwaltung/Api_model', 'ApiModel');
$this->load->model('ressource/Funktion_model', 'FunktionModel');
$this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
+3 -9
View File
@@ -57,11 +57,6 @@
transition: transform 0.15s ease;
}
/* dark mode replaces the yellow cell background with the dark cell background */
html.dark #notentable .tabulator-tableholder .tabulator-editable[tabulator-field="note_vorschlag"]::after {
color: var(--fhc-text);
}
/* rotate the indicator up while the dropdown editor is open */
#notentable .tabulator-tableholder .tabulator-cell.tabulator-editing[tabulator-field="note_vorschlag"]::after {
transform: translateY(-50%) rotate(180deg);
@@ -144,9 +139,8 @@ html.dark #notentable .tabulator-tableholder .tabulator-editable[tabulator-field
}
.pruefung-badge.attempt-t1::before { background: #0072B2; }
/* white text fails on the lighter badge colors */
.pruefung-badge.attempt-t2::before { background: #E69F00; color: #000; }
.pruefung-badge.attempt-t3::before { background: #CC79A7; color: #000; }
.pruefung-badge.attempt-k::before { background: #D55E00; color: #000; }
.pruefung-badge.attempt-t2::before { background: #E69F00; }
.pruefung-badge.attempt-t3::before { background: #CC79A7; }
.pruefung-badge.attempt-k::before { background: #D55E00; }
/* Termin ohne verbrauchten Antritt (entschuldigt, nicht beurteilt) - traegt keine Nummer */
.pruefung-badge.attempt-none::before { background: #767676; }
+9 -17
View File
@@ -31,7 +31,7 @@ body.compat main#cis-main {
}
.fhc-primary-color{
color: var(--fhc-primary-text) !important;
color: var(--fhc-primary) !important;
}
.fhc-primary-bg {
@@ -46,17 +46,6 @@ body.compat main#cis-main {
color: var(--fhc-primary-highlight) !important;
}
/* icon buttons in table action cells, white icon on the gray hover fill and in dark mode */
.fhc-action-btn i {
color: var(--fhc-primary);
}
.fhc-action-btn:hover i,
.fhc-action-btn:active i,
html.dark .fhc-action-btn i {
color: var(--fhc-white-10);
}
.fhc-body-bg{
background-color:var(--fhc-background) !important;
}
@@ -139,7 +128,7 @@ html.dark .fhc-action-btn i {
--fhc-cis-menu-lvl-1-bg: var(--fhc-primary);
--fhc-cis-menu-lvl-1-color: var(--fhc-light);
--fhc-cis-menu-lvl-1-bg-hover: var(--fhc-primary-shade);
--fhc-cis-menu-lvl-1-bg-hover: var(--fhc-primary-highlight);
--fhc-cis-menu-lvl-1-color-hover: var(--fhc-light);
--fhc-cis-menu-lvl-2-bg: var(--fhc-secondary);
@@ -167,6 +156,10 @@ html.dark .fhc-action-btn i {
--fhc-offcanvas-zindex: 1045;
}
#themeSwitch i{
color: var(fhc-light) !important;
}
@media (min-width: 992px) {
:root {
--fhc-cis-menu-width: 250px;
@@ -192,13 +185,11 @@ html.dark .fhc-action-btn i {
}
#cis-menu .menu-entry:hover .submenu{
background-color: var(--fhc-primary-shade);
background-color: var(--fhc-primary-highlight);
color: inherit;
}
/* dark text needs a light background, the shade is too dark for it */
#cis-menu .menu-entry:hover .submenu:hover{
background-color: var(--fhc-light);
color: var(--fhc-dark);
}
@@ -530,6 +521,7 @@ html.dark .fhc-action-btn i {
}
#nav-main-toggle .collapsed .fa-chevron-left {
transform: scaleX(-1);
color: var(--fhc-black-40);
}
#nav-search {
position: relative;
@@ -645,7 +637,7 @@ html.dark .fhc-action-btn i {
}
}
.fhc-header > h1 > small {
color: var(--bs-secondary-color);
color: var(--fhc-tertiary);
font-size: 1rem;
}
.fhc-header > h1 > small:before {
+1
View File
@@ -78,6 +78,7 @@
#studiengang-info-col.collapsed #studiengang-info-toggle .fa-chevron-right {
transform: scaleX(-1);
color: var(--fhc-black-40);
}
#studiengang-info {
-5
View File
@@ -37,8 +37,3 @@
.is-info {
border-color: #0d6efd;
}
/* #0d6efd is too dark for text on the dark background */
html.dark .info-feedback {
color: var(--fhc-primary-text);
}
+1 -1
View File
@@ -10,7 +10,7 @@
font-size: calc(1.325rem + .9vw);
}
.fhc-header > :first-child > small {
color: var(--bs-secondary-color);
color: var(--fhc-tertiary);
font-size: .65em;
padding-inline-start: 1em;
}
+1 -1
View File
@@ -122,7 +122,7 @@
}
.tabulator-row.tabulator-unselectable:not(.tabulator-calcs) {
color: #666 !important;
color: #777 !important;
}
.tabulator-cell .btn {
+1 -1
View File
@@ -2,7 +2,7 @@
:root {
--fhc-sprachen-color: var(--fhc-text, white);
--fhc-sprachen-background: var(--fhc-primary, #005585);
--fhc-sprachen-background-highlight: var(--fhc-primary-shade, #00649c);
--fhc-sprachen-background-highlight: var(--fhc-primary-highlight, #00649c);
}
.sprachen-entry{
@@ -255,50 +255,6 @@
box-shadow: 0 2px 6px rgba(0,0,0,0.12);
}
/* Status icons: fixed color per background, the inherited text color fails on the 50-70 steps */
.verpasst-header,
.nichtbestanden-header,
.erledigt-header,
.abgegeben-header,
.bestanden-header {
--abgabe-status-icon: var(--fhc-white-10);
color: var(--abgabe-status-icon);
}
.abzugeben-header,
.beurteilungerforderlich-header,
.verspaetet-header,
.standard-header {
--abgabe-status-icon: var(--fhc-black-30);
color: var(--abgabe-status-icon);
}
/* white on green-50 is below 3:1 */
.p-accordion-tab-active > .abgegeben-header,
.p-accordion-tab-active > .bestanden-header {
--abgabe-status-icon: var(--fhc-black-30);
}
/* the accordion link sets its own text color, so the icon reads the variable */
.abgabe-status-icon {
color: var(--abgabe-status-icon);
}
/* dark mode: the PrimeVue hover makes the link transparent, so the label would sit on the status color */
html.dark .p-accordion .p-accordion-header:is(
.verpasst-header,
.nichtbestanden-header,
.erledigt-header,
.abzugeben-header,
.beurteilungerforderlich-header,
.verspaetet-header,
.abgegeben-header,
.bestanden-header,
.standard-header
):hover .p-accordion-header-link {
background: var(--fhc-black-70);
}
#abgabetoolroot .modal-header{
background-color: var(--fhc-blue-primary);
color: var(--fhc-white-50);
-2
View File
@@ -55,8 +55,6 @@
}
.fhc-calendar-mode-month .fhc-calendar-base-grid-line-event.event-header {
--event-bg: transparent;
/* the day row is an .event, without this it gets the black event text color */
--event-color: var(--fhc-text, #000);
font-weight: bold;
font-size: var(--fhc-calendar-fontsize-month-day, 1rem);
}
+24 -3
View File
@@ -3,6 +3,7 @@
@import './dashboard/LvPlan.css';
:root {
--fhc-dashboard-danger: var(--fhc-danger, #842029);
--fhc-dashboard-grid-size: 4;
--fhc-dashboard-link: var(--fhc-link, #0a57ca);
--fhc-dashboard-gridrow-background: var(--fhc-background, white);
@@ -11,8 +12,8 @@
--fhc-dashboard-item-overlay-background: var(--fhc-background-highlight, rgba(0, 100, 156, 1));
--fhc-dashboard-item-overly-border-color: var(--fhc-border, lightgray);
--fhc-dashboard-denied-dragging-animation-color: var(--fhc-danger, red);
--fhc-dashboard-pin-pinned-hover-color: var(--fhc-disabled, rgb(153, 116, 116));
--fhc-dashboard-section-info-color: var(--fhc-primary-text, #00649c);
--fhc-dashboard-pin-pinned-hover-color: var(--fhc-fifth-highlight, rgb(153, 116, 116));
--fhc-dashboard-section-info-color: var(--fhc-primary, #00649c);
--fhc-dashboard-section-info-color-hover: var(--fhc-primary-highlight, #005585);
}
@@ -63,7 +64,7 @@
}
.newGridRow:hover {
color: var(--fhc-text);
color: white;
background-color: var(--fhc-dashboard-gridrow-background-highlight);
}
@@ -71,6 +72,11 @@
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-500 -500 1448 1512"><path fill="rgb(211, 211, 211)" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 344V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h64V168c0-13.3 10.7-24 24-24s24 10.7 24 24v64h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H248v64c0 13.3-10.7 24-24 24s-24-10.7-24-24z"/></svg>');
}
.alert-danger .form-check-input:checked {
border-color: var(--fhc-dashboard-danger);
background-color: var(--fhc-dashboard-danger);
}
@media(max-width: 1200px) {
:root {
--fhc-dashboard-grid-size: 3;
@@ -156,6 +162,10 @@
border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;
}
#deleteBookmark i {
color: var(--fhc-dashboard-danger);
}
.pin:hover {
cursor: pointer;
}
@@ -201,3 +211,14 @@
}
}
.hidden-widget {
background: var(--fhc-disabled-background);
opacity: 40%;
}
.hidden-widget .card,
.hidden-widget .card-body,
.hidden-widget .card-body * {
background: inherit !important;
}
+2 -2
View File
@@ -1,7 +1,7 @@
:root{
--fhc-news-widget-height: 1;
--fhc-news-widget-bg: var(--fhc-primary);
--fhc-news-widget-highlight-color: var(--fhc-primary-shade);
--fhc-news-widget-highlight-color: var(--fhc-primary-highlight);
--fhc-news-widget-border: var(--fhc-border);
--fhc-news-widget-border-highlight: var(--fhc-border-highlight);
--fhc-news-widget-text-light: var(--fhc-light);
@@ -171,7 +171,7 @@
.fhc-news-items-lg .row .news-truncate span{
color: var(--fhc-primary-text);
color: var(--fhc-primary);
cursor: pointer;
}
+1 -1
View File
@@ -15,7 +15,7 @@
background-color: var(--fhc-switches-unchecked-bg) !important;
border-color: transparent;
background-position: left center !important;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='white'/%3e%3c/svg%3e") !important;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%238e99a3'/%3e%3c/svg%3e") !important;
}
.fhc-switches:focus {
-21
View File
@@ -36,27 +36,6 @@
--dp-hover-icon-color: rgba(var(--bs-black-rgb), .75);
--dp-range-between-dates-text-color: var(--dp-hover-text-color, var(--text-color));
}
/* dark mode: the picker always uses its light theme class, and --text-color (PrimeVue) turns white there */
html.dark .dp__theme_light {
--dp-background-color: var(--fhc-background);
--dp-text-color: var(--fhc-text);
--dp-hover-color: var(--fhc-tertiary);
--dp-hover-text-color: var(--fhc-text);
--dp-primary-color: var(--fhc-primary);
--dp-primary-text-color: var(--fhc-white-10);
--dp-secondary-color: var(--fhc-disabled);
--dp-border-color: var(--fhc-border);
--dp-menu-border-color: var(--fhc-border);
--dp-border-color-hover: var(--fhc-border-highlight);
--dp-icon-color: rgba(var(--bs-white-rgb), .5);
--dp-hover-icon-color: rgba(var(--bs-white-rgb), .75);
--dp-disabled-color: var(--fhc-secondary);
--dp-disabled-color-text: var(--fhc-disabled);
--dp-scroll-bar-background: var(--fhc-background);
--dp-scroll-bar-color: var(--fhc-tertiary-highlight);
--dp-tooltip-color: var(--fhc-tertiary);
--dp-highlight-color: rgb(0 92 178 / 20%);
}
.dp__theme_light .form-control.is-invalid {
--dp-border-color-hover: var(--bs-danger);
}
-6
View File
@@ -24,12 +24,6 @@
border-color: #DEE2E6 !important;
}
/* the light fill above stays light in dark mode */
html.dark .bg-secondary-subtle {
background-color: var(--fhc-tertiary) !important;
border-color: var(--fhc-border) !important;
}
/* html attribute disabled and title don't work together, sets the pointer-event to auto in order to see the title toolip */
.btn:disabled {
+1 -1
View File
@@ -15,7 +15,7 @@
}
#protocoltbl td:nth-child(1), .cellbg {
background-color: var(--fhc-secondary, #f5f5f5);
background-color: #f5f5f5;
}
/* table date and time cell widths */
+13 -132
View File
@@ -145,9 +145,6 @@
/* fhc base colors */
--fhc-primary: var(--fhc-blue-primary);
--fhc-primary-highlight: var(--fhc-blue-primary-highlight);
--fhc-primary-text: var(--fhc-primary);
/* darker primary for hover and selected backgrounds with light text */
--fhc-primary-shade: var(--fhc-blue-50);
--fhc-secondary: var(--fhc-white-20);
--fhc-secondary-highlight: var(--fhc-white-40);
@@ -181,11 +178,12 @@
--fhc-link: var(--fhc-blue-10);
/* disabled */
--fhc-disabled: var(--fhc-gray-20);
--fhc-disabled: var(--fhc-gray-90);
--fhc-disabled-background: var(--fhc-gray-30);
/* status colors */
--fhc-danger: var(--fhc-red-60);
--fhc-success: var(--fhc-green-70);
--fhc-danger: var(--fhc-red-10);
--fhc-success: var(--fhc-green-10);
--fhc-dark: var(--fhc-black-30);
--fhc-light: var(--fhc-white-50);
@@ -197,9 +195,6 @@
/* fhc base colors */
--fhc-primary: var(--fhc-blue-primary);
--fhc-primary-highlight: var(--fhc-blue-primary-highlight);
/* --fhc-primary is too dark for text on the dark background */
--fhc-primary-text: var(--fhc-blue-40);
--fhc-primary-shade: var(--fhc-blue-50);
--fhc-secondary: var(--fhc-black-50);
--fhc-secondary-highlight: var(--fhc-black-70);
@@ -207,12 +202,7 @@
--fhc-tertiary: var(--fhc-black-70);
--fhc-tertiary-highlight: var(--fhc-gray-40);
--fhc-fourth: var(--fhc-gray-40);
--fhc-fourth-highlight: var(--fhc-gray-30);
/* lighter steps fail with --fhc-text, so level 5 repeats level 4 */
--fhc-fifth: var(--fhc-gray-40);
--fhc-fifth-highlight: var(--fhc-gray-30);
--fhc-danger: var(--fhc-red-10);
/*background*/
--fhc-background: var(--fhc-black-30);
@@ -235,16 +225,14 @@
/* disabled */
--fhc-disabled: var(--fhc-gray-10);
--fhc-disabled-background: var(--fhc-gray-120);
/* status colors */
--fhc-danger: var(--fhc-red-30);
--fhc-success: var(--fhc-green-30);
--fhc-danger: var(--fhc-red-10);
--fhc-success: var(--fhc-green-10);
--fhc-dark: var(--fhc-black-50);
--fhc-light: var(--fhc-white-70);
/* calendar: beige-10 is too bright behind light day numbers */
--fhc-calendar-bg-markings-past: var(--fhc-beige-20);
}
:root.purple {
@@ -265,8 +253,6 @@
/* fhc base colors */
--fhc-primary: var(--fhc-purple-primary-highlight);
--fhc-primary-highlight: var(--fhc-purple-primary);
--fhc-primary-text: var(--fhc-primary);
--fhc-primary-shade: var(--fhc-primary-highlight);
--fhc-secondary: var(--fhc-purple-20);
--fhc-secondary-highlight: var(--fhc-purple-40);
@@ -274,13 +260,6 @@
--fhc-tertiary: var(--fhc-purple-30);
--fhc-tertiary-highlight: var(--fhc-purple-50);
--fhc-fourth: var(--fhc-purple-40);
--fhc-fourth-highlight: var(--fhc-purple-50);
/* darker steps fail with --fhc-text, so level 5 repeats level 4 */
--fhc-fifth: var(--fhc-purple-40);
--fhc-fifth-highlight: var(--fhc-purple-50);
/* background */
--fhc-background: var(--fhc-purple-5);
--fhc-background-highlight: var(--fhc-purple-10);
@@ -304,8 +283,8 @@
--fhc-disabled: var(--fhc-purple-20);
/* status colors */
--fhc-danger: var(--fhc-red-60);
--fhc-success: var(--fhc-green-70);
--fhc-danger: var(--fhc-red-10);
--fhc-success: var(--fhc-green-10);
--fhc-dark: var(--fhc-black-20);
--fhc-light: var(--fhc-white-70);
@@ -335,12 +314,11 @@ html.dark .border-primary {
}
/* fhc danger*/
/* dark --fhc-danger is a text color, white text needs a dark fill */
html.dark .bg-danger,
html.dark .btn-danger,
html.dark .btn-outline-danger {
background-color: var(--fhc-red-70) !important;
border-color: var(--fhc-red-70) !important;
background-color: var(--fhc-danger) !important;
border-color: var(--fhc-danger) !important;
color: white;
}
@@ -357,61 +335,6 @@ html.dark .border-danger {
border-color: var(--fhc-danger) !important;
}
/* btn-light stays light in dark mode, so it needs the dark red */
html.dark .btn-light.text-danger {
color: var(--fhc-red-70) !important;
}
/* fhc success */
html.dark .text-success {
color: var(--fhc-success) !important;
}
/* Bootstrap keeps #6c757d for secondary text and outline buttons in dark mode */
html.dark .text-secondary {
color: var(--bs-secondary-color) !important;
}
html.dark .btn-outline-secondary {
--bs-btn-color: var(--fhc-white-90);
--bs-btn-border-color: var(--fhc-white-90);
}
/* btn-outline-dark is dark on the dark background, use the theme text color */
html.dark .btn-outline-dark {
--bs-btn-color: var(--fhc-text);
--bs-btn-border-color: var(--fhc-text);
--bs-btn-hover-color: var(--fhc-background);
--bs-btn-hover-bg: var(--fhc-text);
--bs-btn-hover-border-color: var(--fhc-text);
--bs-btn-active-color: var(--fhc-background);
--bs-btn-active-bg: var(--fhc-text);
--bs-btn-active-border-color: var(--fhc-text);
--bs-btn-disabled-color: var(--fhc-text);
--bs-btn-disabled-border-color: var(--fhc-text);
}
/* btn-dark fills with the page color in dark mode, mirror it so it stays distinct from btn-outline-dark */
html.dark .btn-dark {
--bs-btn-color: var(--fhc-background);
--bs-btn-bg: var(--fhc-text);
--bs-btn-border-color: var(--fhc-text);
--bs-btn-hover-color: var(--fhc-background);
--bs-btn-hover-bg: var(--fhc-light);
--bs-btn-hover-border-color: var(--fhc-light);
--bs-btn-active-color: var(--fhc-background);
--bs-btn-active-bg: var(--fhc-light);
--bs-btn-active-border-color: var(--fhc-light);
--bs-btn-disabled-color: var(--fhc-background);
--bs-btn-disabled-bg: var(--fhc-text);
--bs-btn-disabled-border-color: var(--fhc-text);
}
/* SB Admin 2 fills #page-wrapper white (Pruefungsprotokoll, Anrechnung admin) */
html.dark #page-wrapper {
background-color: var(--fhc-background);
}
html.dark .tabulator,
html.dark .tabulator .tabulator-header,
@@ -428,52 +351,10 @@ html.dark .tabulator .tabulator-cell {
border-color: var(--fhc-border) !important;
}
/* selected rows: the vendor #9abcea is too dark for the red and green cell content */
.tabulator .tabulator-row.tabulator-selected .tabulator-cell {
background-color: var(--bs-primary-bg-subtle);
}
/* the gray outline button text fails on any row tint, so give it an opaque fill */
.tabulator .tabulator-row.tabulator-selected .btn-outline-secondary {
--bs-btn-bg: var(--fhc-background);
}
/* selected rows: dark blue keeps the status text colors readable */
html.dark .tabulator .tabulator-row.tabulator-selected .tabulator-cell {
background-color: var(--fhc-indigo-90) !important;
}
html.dark .tabulator .tabulator-header .tabulator-col:hover {
background-color: var(--fhc-tertiary) !important;
}
/* tabulator_simple: fixed light colors for the placeholder, sort arrows and menus */
html.dark .tabulator .tabulator-tableholder .tabulator-placeholder span {
color: var(--fhc-text);
}
html.dark .tabulator .tabulator-col[aria-sort="ascending"] .tabulator-arrow {
border-bottom-color: var(--fhc-text) !important;
}
html.dark .tabulator .tabulator-col[aria-sort="descending"] .tabulator-arrow {
border-top-color: var(--fhc-text) !important;
}
/* both modes: the vendor #bbb for unsorted columns is too light on white */
.tabulator .tabulator-col[aria-sort="none"] .tabulator-arrow {
border-bottom-color: var(--fhc-disabled) !important;
}
html.dark .tabulator-popup-container.tabulator-menu {
background: var(--fhc-background);
color: var(--fhc-text);
}
html.dark .tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover {
background: var(--fhc-tertiary);
}
.p-paginator{
background-color: var(--fhc-background) !important;
}
@@ -490,7 +371,7 @@ html.dark .tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled
.p-paginator .p-paginator-prev,
.p-paginator .p-paginator-first,
.p-paginator .p-paginator-last {
color: var(--fhc-primary-text) !important;
color: var(--fhc-primary) !important;
}
.p-link:focus {
+11 -1
View File
@@ -2,6 +2,8 @@ import CisMenu from "../../components/Cis/Menu.js";
import PluginsPhrasen from '../../plugins/Phrasen.js';
import Theme from "../../plugins/Theme.js";
import ApiAuthinfo from "../../api/factory/authinfo.js";
const app = Vue.createApp({
name: 'CisMenuApp',
components: {
@@ -9,11 +11,15 @@ const app = Vue.createApp({
},
data: () => ({
windowWidth: 0,
isStudent: null,
isMitarbeiter: null,
}),
provide() {
return {
isNarrow: Vue.computed(() => this.windowWidth < 992),
isMobile: Vue.computed(() => this.windowWidth < 767),
isStudent: Vue.computed(() => this.isStudent),
isMitarbeiter: Vue.computed(() => this.isMitarbeiter)
}
},
methods: {
@@ -21,8 +27,12 @@ const app = Vue.createApp({
this.windowWidth = window.innerWidth;
},
},
created() {
async created() {
this.windowWidth = window.innerWidth;
await this.$api.call(ApiAuthinfo.getAuthInfo()).then((res) => {
this.isMitarbeiter = res.data.isMitarbeiter;
this.isStudent = res.data.isStudent;
});
},
mounted() {
window.addEventListener("resize", this.handleWindowResize);
@@ -764,7 +764,7 @@ export const AbgabeMitarbeiterDetail = {
<template #header>
<div class="d-flex flex-nowrap align-items-center w-100">
<div class="abgabe-status-icon flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -68px;">
<div class="flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -68px;">
<i v-if="termin.dateStyle == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
<i v-else-if="termin.dateStyle == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
<i v-else-if="termin.dateStyle == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
@@ -450,7 +450,7 @@ export const AbgabeStudentDetail = {
<AccordionTab :headerClass="termin.dateStyle + '-header'">
<template #header>
<div class="d-flex flex-nowrap align-items-center w-100">
<div class="abgabe-status-icon flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -68px;">
<div class="flex-shrink-0 d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; margin-left: -68px;">
<i v-if="termin.dateStyle == 'verspaetet'" v-tooltip.right="getTooltipVerspaetet" class="fa-solid fa-triangle-exclamation"></i>
<i v-else-if="termin.dateStyle == 'verpasst'" v-tooltip.right="getTooltipVerpasst" class="fa-solid fa-calendar-xmark"></i>
<i v-else-if="termin.dateStyle == 'abzugeben'" v-tooltip.right="getTooltipAbzugeben" class="fa-solid fa-hourglass-half"></i>
@@ -98,7 +98,7 @@ export const AbgabeStudentTimeline = {
<div v-for="projekt in projekte" :key="projekt.projektarbeit_id" class="mb-2">
<button
class="btn btn-sm w-100 text-start d-flex align-items-center gap-2 rounded bg-body-tertiary border-0 py-2 px-3"
class="btn btn-sm w-100 text-start d-flex align-items-center gap-2 rounded bg-light border-0 py-2 px-3"
@click="toggleProject(projekt.projektarbeit_id)"
>
<i
@@ -548,10 +548,10 @@ export const AbgabetoolAssistenz = {
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: var(--fhc-background); color: var(--fhc-text); appearance: none; caret-color: transparent;';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: var(--fhc-background); color: var(--fhc-text); border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
// mapping evaluated at render time, not at column definition time
@@ -814,10 +814,10 @@ export const AbgabetoolAssistenz = {
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: var(--fhc-background); color: var(--fhc-text); appearance: none; caret-color: transparent;';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: var(--fhc-background); color: var(--fhc-text); border: 1px solid; z-index: 9999; min-width: 220px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 220px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
const updateDisplay = () => {
display.value = options
@@ -912,10 +912,10 @@ export const AbgabetoolAssistenz = {
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: var(--fhc-background); color: var(--fhc-text); appearance: none; caret-color: transparent;';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: var(--fhc-background); color: var(--fhc-text); border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
options.forEach(opt => {
const row = document.createElement('label');
@@ -1669,7 +1669,7 @@ export const AbgabetoolAssistenz = {
const createButton = (iconClass, titleKey, clickHandler) => {
const btn = document.createElement('button');
btn.className = 'btn btn-outline-secondary fhc-action-btn';
btn.className = 'btn btn-outline-secondary';
btn.style.display = "flex";
btn.style.alignItems = "center"; // center icon vertically
btn.style.justifyContent = "center"; // center icon horizontally
@@ -1677,7 +1677,7 @@ export const AbgabetoolAssistenz = {
btn.style.aspectRatio = "1 / 1"; // keep square shape (optional)
btn.style.padding = "0"; // remove extra padding for compactness
if(iconClass == 'fa fa-timeline') btn.style.transform = "rotate(90deg)";
btn.innerHTML = `<i class="${iconClass}" style="font-size:1.1rem;"></i>`;
btn.innerHTML = `<i class="${iconClass}" style="color:#00649C; font-size:1.1rem;"></i>`;
btn.title = this.$capitalize(this.$p.t(titleKey));
btn.addEventListener('click', (e) => {
e.stopPropagation();
@@ -1964,28 +1964,28 @@ export const AbgabetoolAssistenz = {
let icon = ''
switch(dateStyle) {
case 'verspaetet':
icon = '<i class="fa-solid fa-triangle-exclamation"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-triangle-exclamation"></i>'
break
case 'verpasst':
icon = '<i class="fa-solid fa-calendar-xmark"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-calendar-xmark"></i>'
break
case 'abzugeben':
icon = '<i class="fa-solid fa-hourglass-half"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-hourglass-half"></i>'
break
case 'standard':
icon = '<i class="fa-solid fa-clock"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-clock"></i>'
break
case 'abgegeben':
icon = '<i class="fa-solid fa-paperclip"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-paperclip"></i>'
break
case 'beurteilungerforderlich':
icon = '<i class="fa-solid fa-list-check"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-list-check"></i>'
break
case 'bestanden':
icon = '<i class="fa-solid fa-check"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-check"></i>'
break
case 'nichtbestanden':
icon = '<i class="fa-solid fa-circle-exclamation"></i>'
icon = '<i style="color: #000000 !important;" class="fa-solid fa-circle-exclamation"></i>'
break
}
@@ -312,7 +312,7 @@ export const AbgabetoolMitarbeiter = {
const createButton = (iconClass, titleKey, clickHandler) => {
const btn = document.createElement('button');
btn.className = 'btn btn-outline-secondary fhc-action-btn';
btn.className = 'btn btn-outline-secondary';
btn.style.display = "flex";
btn.style.alignItems = "center"; // center icon vertically
btn.style.justifyContent = "center"; // center icon horizontally
@@ -320,7 +320,7 @@ export const AbgabetoolMitarbeiter = {
btn.style.aspectRatio = "1 / 1"; // keep square shape (optional)
btn.style.padding = "0"; // remove extra padding for compactness
if(iconClass == 'fa fa-timeline') btn.style.transform = "rotate(90deg)";
btn.innerHTML = `<i class="${iconClass}" style="font-size:1.1rem;"></i>`;
btn.innerHTML = `<i class="${iconClass}" style="color:#00649C; font-size:1.1rem;"></i>`;
btn.title = this.$capitalize(this.$p.t(titleKey));
btn.addEventListener('click', (e) => {
e.stopPropagation();
@@ -395,10 +395,10 @@ export const AbgabetoolMitarbeiter = {
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: var(--fhc-background); color: var(--fhc-text); appearance: none; caret-color: transparent;';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: var(--fhc-background); color: var(--fhc-text); border: 1px solid; z-index: 9999; min-width: 220px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 220px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
const updateDisplay = () => {
display.value = options
@@ -493,10 +493,10 @@ export const AbgabetoolMitarbeiter = {
const display = document.createElement('input');
display.readOnly = true;
display.placeholder = '';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: var(--fhc-background); color: var(--fhc-text); appearance: none; caret-color: transparent;';
display.style.cssText = 'padding: 4px; width: 100%; box-sizing: border-box; cursor: default; border: 1px solid; outline: none; background: #fff; appearance: none; caret-color: transparent;';
const dropdown = document.createElement('div');
dropdown.style.cssText = 'display: none; position: fixed; background: var(--fhc-background); color: var(--fhc-text); border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
dropdown.style.cssText = 'display: none; position: fixed; background: #fff; border: 1px solid; z-index: 9999; min-width: 180px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);';
options.forEach(opt => {
const row = document.createElement('label');
@@ -1195,7 +1195,7 @@ export const AbgabetoolMitarbeiter = {
},
detailFormatter(cell) {
return '<div style="display: flex; justify-content: start; align-items: center; height: 100%">' +
'<a><i class="fa fa-folder-open fhc-primary-color"></i></a></div>'
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
},
pkzTextFormatter(cell) {
const val = cell.getValue()
@@ -222,22 +222,22 @@ export const AbgabetoolStudent = {
if(val.mode === 'detailTermine') {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a><i class="fa fa-folder-open fhc-primary-color"></i></a></div>'
'<a><i class="fa fa-folder-open" style="color:#00649C"></i></a></div>'
} else if (val.mode === 'beurteilungDownload') {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a><i class="fa fa-file-pdf fhc-primary-color"></i></a></div>'
'<a><i class="fa fa-file-pdf" style="color:#00649C"></i></a></div>'
}
},
mailFormatter(cell) {
const val = cell.getValue()
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a href='+val+'><i class="fa fa-envelope fhc-primary-color"></i></a></div>'
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
beurteilungFormatter(cell) {
const val = cell.getValue()
if(val) {
return '<div style="display: flex; justify-content: center; align-items: center; height: 100%;">' +
'<a><i class="fa fa-file-pdf fhc-primary-color"></i></a></div>'
'<a><i class="fa fa-file-pdf" style="color:#00649C"></i></a></div>'
} else return '-'
},
buildMailToLink(projekt) {
@@ -518,7 +518,7 @@ export const AbgabetoolStudent = {
<div class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{ getErstbetreuerEmailLabel(projektarbeit) }}</div>
<div class="col-8 col-md-9">
<a :href="getMailLink(projektarbeit)"><i class="fa fa-envelope fhc-primary-color"></i></a>
<a :href="getMailLink(projektarbeit)"><i class="fa fa-envelope" style="color:#00649C"></i></a>
</div>
</div>
<div v-if="projektarbeit.zweitbetreuer_person_id || projektarbeit.zweitbetreuer" class="row mt-2">
@@ -530,7 +530,7 @@ export const AbgabetoolStudent = {
<div v-if="projektarbeit.zweitbetreuer_person_id || projektarbeit.zweitbetreuer" class="row mt-2">
<div class="col-4 col-md-3 fw-bold">{{ getZweitbetreuerEmailLabel(projektarbeit) }}</div>
<div class="col-8 col-md-9">
<a v-if="projektarbeit.zweitbetreuer_mail" :href="getZweitbetreuerMailLink(projektarbeit)"><i class="fa fa-envelope fhc-primary-color"></i></a>
<a v-if="projektarbeit.zweitbetreuer_mail" :href="getZweitbetreuerMailLink(projektarbeit)"><i class="fa fa-envelope" style="color:#00649C"></i></a>
<span v-else>-</span>
</div>
</div>
@@ -1291,7 +1291,7 @@ export const Benotungstool = {
const data = cell.getData()
if(field == 'note' && data.note && data.note != data.lv_note) {
style += 'color: var(--fhc-danger, red); border-color: var(--fhc-danger, red); border-style:solid; border-width:1px;'
style += 'color:red; border-color:red; border-style:solid; border-width:1px;'
}
const match = this.notenOptions.find(opt => opt.note == value)
@@ -1310,7 +1310,7 @@ export const Benotungstool = {
'<div data-cy="freigabe-state" data-state="' + value + '" style="' + style + '">' + icon + '</div>'
if(value === 'ok') {
return wrap('<i class="fa fa-circle-check" style="color: var(--fhc-success, green)"></i>')
return wrap('<i class="fa fa-circle-check" style="color:green"></i>')
} else if (value === 'offen') {
return wrap('<i class="fa-regular fa-circle"></i>')
} else if (value === 'changed') {
@@ -1604,7 +1604,7 @@ export const Benotungstool = {
let style = 'display: flex; justify-content: center; align-items: center; height: 100%;'
return '<div style="'+style+'">' +
'<a href='+val+'><i class="fa fa-envelope fhc-primary-color"></i></a></div>'
'<a href='+val+'><i class="fa fa-envelope" style="color:#00649C"></i></a></div>'
},
percentFormatter(cell) {
const data = cell.getData()
@@ -1682,7 +1682,7 @@ export const Benotungstool = {
// some moodle noten are numeric, some are strings like "Sehr Gut", "Bestanden" etc...
const notenOption = this.notenOptions.find(n=>n.note == g.grade || n.bezeichnung == g.grade)
if(notenOption.positiv) s.teilnote += ('<span>'+g.text +'</span>'+ '<br/>')
else s.teilnote += ('<span style="color: var(--fhc-danger, red);">'+g.text +'</span>'+ '<br/>')
else s.teilnote += ('<span style="color: red;">'+g.text +'</span>'+ '<br/>')
})
this.recalculateSelectable(s)
@@ -67,7 +67,7 @@ export default {
new Tabulator(table, {
index: index,
layout: "fitDataFill",
layout: "fitColumns",
columnDefaults: {
formatter: "html",
@@ -68,7 +68,7 @@ export default {
new Tabulator(table, {
index: index,
layout: "fitDataFill",
layout: "fitColumns",
columnDefaults: {
formatter: "html",
@@ -74,7 +74,7 @@ export default {
for (let table of tables) {
this.sanitizeLegacyTables(table)
new Tabulator(table, {
layout: "fitDataStretch",
layout: "fitColumns",
columnDefaults: {
formatter: "html",
@@ -83,10 +83,26 @@ export default {
});
},
getPromiseFunc(start, end) {
return [
this.$api.call(ApiLvPlan.eventsLv(this.propsViewData.lv_id, start.toISODate(), end.toISODate())),
this.$api.call(ApiLvPlan.getLvPlanReservierungen(start.toISODate(), end.toISODate()))
let functions = [
this.$api.call(
ApiLvPlan.eventsLv(
this.propsViewData.lv_id,
start.toISODate(),
end.toISODate(),
),
),
];
if (!this.$props.propsViewData.lv_id?.length) {
functions.push(
this.$api.call(
ApiLvPlan.getLvPlanReservierungen(
start.toISODate(),
end.toISODate(),
),
),
);
}
return functions;
}
},
created() {
+5 -1
View File
@@ -65,6 +65,10 @@ export default {
label: Vue.computed(() => this.$p.t("profil/zeitsperren")),
icon: "fas fa-calendar-days",
type: "link",
isMitarbeiter: Vue.computed(() => this.isMitarbeiter),
renderif: function() {
return this.isMitarbeiter;
},
action: function (data) {
const uid = JSON.parse(data.data).uid;
const link =
@@ -163,7 +167,7 @@ export default {
openMenuHierarchy: [],
};
},
inject: ["isNarrow", "isMobile"],
inject: ["isNarrow", "isMobile", "isMitarbeiter", "isStudent"],
provide(){
return{
setActiveEntry: this.setActiveEntry,
@@ -74,7 +74,7 @@ export const MyReservation = {
const createButton = (iconClass, titleKey, clickHandler) => {
const btn = document.createElement('button');
btn.className = 'btn btn-outline-secondary fhc-action-btn';
btn.className = 'btn btn-outline-secondary';
btn.style.display = "flex";
btn.style.alignItems = "center"; // center icon vertically
btn.style.justifyContent = "center"; // center icon horizontally
@@ -82,7 +82,7 @@ export const MyReservation = {
btn.style.aspectRatio = "1 / 1"; // keep square shape (optional)
btn.style.padding = "0"; // remove extra padding for compactness
if(iconClass == 'fa fa-timeline') btn.style.transform = "rotate(90deg)";
btn.innerHTML = `<i class="${iconClass}" style="font-size:1.1rem;"></i>`;
btn.innerHTML = `<i class="${iconClass}" style="color:#00649C; font-size:1.1rem;"></i>`;
btn.title = this.$capitalize(this.$p.t(titleKey));
btn.addEventListener('click', (e) => {
e.stopPropagation();
@@ -117,8 +117,8 @@ export default {
v-show="!loading"
ref="modalContainer"
v-bind="$props"
header-class="bg-body-tertiary"
footer-class="bg-body-tertiary"
header-class="bg-light"
footer-class="bg-light"
body-class=""
dialog-class="modal-lg"
class="bootstrap-alert"
@@ -48,7 +48,7 @@ export const ProjektabgabeUebersicht = {
container.className = "d-flex gap-2";
let downloadButton = document.createElement('button');
downloadButton.className = 'btn btn-outline-secondary fhc-action-btn';
downloadButton.className = 'btn btn-outline-secondary';
downloadButton.innerHTML = '<i class="fa fa-download"></i>';
downloadButton.title = this.$p.t('ui', 'downloadDok');
downloadButton.addEventListener('click', evt => {
@@ -60,7 +60,7 @@ export const ProjektabgabeUebersicht = {
if (this.showEdit)
{
let editButton = document.createElement('button');
editButton.className = 'btn btn-outline-secondary fhc-action-btn';
editButton.className = 'btn btn-outline-secondary';
editButton.innerHTML = '<i class="fa fa-edit"></i>';
//editButton.addEventListener('click', () =>
//this.$refs.edit.open(cell.getData())
@@ -136,7 +136,7 @@ export default {
:''
}}</th>
<td>
<a v-if="event.ort_content_id" :aria-label="$p.t('global','raum')" :title="$p.t('global','raum')" :href="getOrtContentLink"><i class="fa fa-arrow-up-right-from-square me-1 fhc-primary-color" aria-hidden="true"></i></a>
<a v-if="event.ort_content_id" :aria-label="$p.t('global','raum')" :title="$p.t('global','raum')" :href="getOrtContentLink"><i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true" style="color:#00649C"></i></a>
{{event.ort_kurzbz}}
</td>
</tr>
@@ -157,7 +157,7 @@ export default {
<td>
<div id="lektorenContainer">
<div v-for="lektor in event.lektor" class="d-block">
<a v-if="lektorenLinks[lektor.kurzbz]" :aria-label="$p.t('lehre','lektor')" :title="$p.t('lehre','lektor')" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1 fhc-primary-color" aria-hidden="true"></i></a>
<a v-if="lektorenLinks[lektor.kurzbz]" :aria-label="$p.t('lehre','lektor')" :title="$p.t('lehre','lektor')" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C" aria-hidden="true"></i></a>
{{lektor.kurzbz}}
</div>
</div>
@@ -87,7 +87,7 @@ export default {
:''
}}</th>
<td>
<a v-if="event.ort_content_id" :aria-label="$p.t('global','raum')" :title="$p.t('global','raum')" :href="getOrtContentLink"><i class="fa fa-arrow-up-right-from-square me-1 fhc-primary-color" aria-hidden="true"></i></a>
<a v-if="event.ort_content_id" :aria-label="$p.t('global','raum')" :title="$p.t('global','raum')" :href="getOrtContentLink"><i class="fa fa-arrow-up-right-from-square me-1" style="color:#00649C" aria-hidden="true"></i></a>
{{event.ort_kurzbz}}
</td>
</tr>
@@ -107,7 +107,7 @@ export default {
}}</th>
<td>
<div v-for="lektor in event.lektor" class="d-block">
<a v-if="lektorenLinks[lektor.kurzbz]" :aria-label="$p.t('lehre','lektor')" :tooltip="$p.t('lehre','lektor')" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1 fhc-primary-color" aria-hidden="true"></i></a>
<a v-if="lektorenLinks[lektor.kurzbz]" :aria-label="$p.t('lehre','lektor')" :tooltip="$p.t('lehre','lektor')" :href="lektorenLinks[lektor.kurzbz]"><i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true" style="color:#00649C"></i></a>
{{lektor.kurzbz}}
</div>
</td>
@@ -91,6 +91,15 @@ export default {
.then(() => this.deleteZeitsperre(zeitsperre_id))
.catch(this.$fhcAlert.handleSystemError);
},
alertMailstatus(response) {
if(response?.meta?.mailstatus) {
if(response.meta.mailstatus.mailsent) {
this.$fhcAlert.alertInfo(response.meta.mailstatus.msg);
} else {
this.$fhcAlert.alertWarning(response.meta.mailstatus.msg);
}
}
},
saveZeitsperre(zeitsperreId = null) {
const isNew = !zeitsperreId;
@@ -109,8 +118,9 @@ export default {
return this.$refs.dataZeitsperre
.call(request)
.then(() => {
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.alertMailstatus(response);
this.reset();
this.reload();
@@ -122,6 +132,7 @@ export default {
.call(ApiTimelocks.deleteZeitsperre(zeitsperre_id))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
this.alertMailstatus(response);
})
.catch(this.$fhcAlert.handleSystemError)
.finally(()=> {
+1 -1
View File
@@ -275,7 +275,7 @@ export default {
:aria-label="$p.t('dashboard/widget_pin')"
@click="pinItem"
>
<i class="fa-solid fa-thumbtack" aria-hidden="true" style="color: var(--fhc-disabled, lightgray);"></i>
<i class="fa-solid fa-thumbtack" aria-hidden="true" style="color:lightgray;"></i>
</div>
</template>
<!-- widget link -->
@@ -148,7 +148,7 @@ export default {
<div class="d-flex">
<div class="text-muted small me-auto"><small>{{$p.t('ampeln','ampelnDeadline',{value:getDate(ampel.deadline)})}}</small></div>
<div v-if="(new Date() > new Date(ampel.deadline)) && !ampel.bestaetigt "><span class="badge bg-danger"><i class="fa fa-solid fa-bolt"></i></span></div>
<div v-if="ampel.verpflichtend"><span class="badge text-bg-warning ms-1"><i class="fa fa-solid fa-triangle-exclamation"></i></span></div>
<div v-if="ampel.verpflichtend"><span class="badge bg-warning ms-1"><i class="fa fa-solid fa-triangle-exclamation"></i></span></div>
<div v-if="ampel.bestaetigt"><span class="badge bg-success ms-1"><i class="fa fa-solid fa-circle-check"></i></span></div>
</div>
</div>
@@ -232,7 +232,7 @@ export default {
</span>
</div>
<div v-if="ampel.verpflichtend">
<span class="badge text-bg-warning ms-1">
<span class="badge bg-warning ms-1">
<i class="fa fa-solid fa-triangle-exclamation"></i>
</span>
</div>
+1 -1
View File
@@ -297,7 +297,7 @@ export default {
<span
v-if="hasTags(link)"
:title="hasTags(link)"
style="color: var(--fhc-disabled, silver);"
style="color: silver;"
>
<i
class="fa fa-solid fa-tag text-gray-500"
-1
View File
@@ -1 +0,0 @@
../vendor
+1
View File
@@ -105,6 +105,7 @@ require_once('dbupdate_3.4/78292_gehaltsanpassungtyp.php');
require_once('dbupdate_3.4/54225_reihungstest_ablauf_fuer_quereinsteiger.php');
require_once('dbupdate_3.4/77757_widgets_berechtigungen.php');
require_once('dbupdate_3.4/77048_cis4_mitarbeiter_studstatus_widget.php');
require_once('dbupdate_3.4/83538_cis4_employees.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -0,0 +1,35 @@
<?php
// Add permission: cis/eigene_zeitsperren
if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'cis/eigene_zeitsperren';"))
{
if($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('cis/eigene_zeitsperren', 'CI/VueJs Applikation Eigene Zeitsperren');";
if(!$db->db_query($qry))
{
echo '<strong>system.tbl_berechtigung '.$db->db_last_error().'</strong><br>';
}
else
{
echo 'system.tbl_berechtigung: Added permission "cis/eigene_zeitsperren"<br>';
}
}
}
// Add permission: cis/andere_zeitsperren
if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'cis/andere_zeitsperren';"))
{
if($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('cis/andere_zeitsperren', 'CI/VueJs Applikation Zeitsperren anderer MA anzeigen');";
if(!$db->db_query($qry))
{
echo '<strong>system.tbl_berechtigung '.$db->db_last_error().'</strong><br>';
}
else
{
echo 'system.tbl_berechtigung: Added permission "cis/andere_zeitsperren"<br>';
}
}
}
+122
View File
@@ -68077,6 +68077,128 @@ I have been informed that I am under no obligation to consent to the transmissio
)
),
// MYLV PHRASES END
// Zeitsperren Sancho Subjects begin
array(
'app' => 'core',
'category' => 'zeitsperren',
'phrase' => 'zeitausgleichNeu',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zeitausgleich wurde neu eingetragen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Compensatory time was entered',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'zeitsperren',
'phrase' => 'zeitausgleichGeaendert',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zeitausgleich wurde geändert',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Compensatory time was changed',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'zeitsperren',
'phrase' => 'zeitausgleichGeloescht',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zeitausgleich wurde gelöscht',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Compensatory time was deleted',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'zeitsperren',
'phrase' => 'VorgesetzteInformiert',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Email wurde an {vorgesetzte} versandt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Email was sent to {vorgesetzte}',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'zeitsperren',
'phrase' => 'fehlerBeimSendenAufgetreten',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Fehler beim Senden des Freigabemails an {vorgesetzte}',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Error occured sending mail to {vorgesetzte}',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'zeitsperren',
'phrase' => 'konnteKeinFreigabemailVersendetWerden',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Es konnte kein Freigabemail versendet werden, da kein Vorgesetzter eingetragen ist!',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'An approval mail could not be sent because the “Superior” field was left blank!',
'description' => '',
'insertvon' => 'system'
)
)
),
// Zeitsperren Sancho Subjects end
);