diff --git a/application/controllers/api/frontend/fas/studstatus/Wiederholung.php b/application/controllers/api/frontend/fas/studstatus/Wiederholung.php new file mode 100644 index 000000000..c6e5a4fa9 --- /dev/null +++ b/application/controllers/api/frontend/fas/studstatus/Wiederholung.php @@ -0,0 +1,161 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + + +/** + * This controller operates between (interface) the JS (FAS) and the AntragLib (back-end) + * This controller works with calls on the HTTP GET or POST and the output is always RDF + */ +class Wiederholung extends Auth_Controller +{ + + /** + * Calls the parent's constructor and loads the FilterCmptLib + */ + public function __construct() + { + parent::__construct([ + 'getLvs' => ['student/studierendenantrag:r', 'student/noten:r'], + 'moveLvsToZeugnis' => ['student/studierendenantrag:w', 'student/noten:w'] + ]); + + // Libraries + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'global', + 'studierendenantrag' + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getLvs($prestudent_id) + { + // header für no cache + $this->output->set_header("Cache-Control: no-cache"); + $this->output->set_header("Cache-Control: post-check=0, pre-check=0", false); + $this->output->set_header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + $this->output->set_header("Pragma: no-cache"); + $this->output->set_header("Content-type: application/xhtml+xml"); + + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + $sem_akt = $this->variablelib->getVar('semester_aktuell'); + + + $result = $this->antraglib->getLvsForPrestudent($prestudent_id, $sem_akt); + $lvs = $this->getDataOrTerminateWithError($result) ?: []; + + $rdf_url = 'http://www.technikum-wien.at/antragnote'; + + $this->load->view('lehre/Antrag/Wiederholung/getLvs.rdf.php', [ + 'url' => $rdf_url, + 'lvs' => $lvs + ]); + } + + public function moveLvsToZeugnis() + { + $anzahl = $this->input->post('anzahl'); + $student_uid = $this->input->post('student_uid'); + $this->load->model('education/Studierendenantraglehrveranstaltung_model', 'StudierendenantraglehrveranstaltungModel'); + $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + + $errormsg = array(); + + for($i=0; $i<$anzahl; $i++) + { + $id = $this->input->post('studierendenantrag_lehrveranstaltung_id_' . $i); + $result =$this->StudierendenantraglehrveranstaltungModel->load($id); + if(isError($result)) + { + $errormsg[] = getError($result); + } + elseif(!hasData($result)) + { + $errormsg[] = $this->p->t('studierendenantrag', 'error_no_lv_in_application'); + } + else + { + $antragLv = getData($result)[0]; + $result= $this->ZeugnisnoteModel->load([ + 'lehrveranstaltung_id'=> $antragLv->lehrveranstaltung_id, + 'student_uid'=> $student_uid, + 'studiensemester_kurzbz' => $antragLv->studiensemester_kurzbz + ]); + if(isError($result)) + { + $errormsg[] = getError($result); + } + else + { + if (hasData($result)) + { + $result = $this->ZeugnisnoteModel->update( + [ + 'lehrveranstaltung_id'=> $antragLv->lehrveranstaltung_id, + 'student_uid'=> $student_uid, + 'studiensemester_kurzbz' => $antragLv->studiensemester_kurzbz + ], + [ + 'note'=> $antragLv->note, + 'uebernahmedatum' => date('c'), + 'benotungsdatum' => $antragLv->insertamum, + 'updateamum' => date('c'), + 'bemerkung'=>$antragLv->anmerkung, + 'updatevon'=>getAuthUID() + ] + ); + } + else + { + $result = $this->ZeugnisnoteModel->insert([ + 'lehrveranstaltung_id'=> $antragLv->lehrveranstaltung_id, + 'student_uid'=> $student_uid, + 'studiensemester_kurzbz' => $antragLv->studiensemester_kurzbz, + 'note'=> $antragLv->note, + 'uebernahmedatum' => date('c'), + 'benotungsdatum' => $antragLv->insertamum, + 'insertamum' => date('c'), + 'bemerkung'=>$antragLv->anmerkung, + 'insertvon'=>getAuthUID() + ]); + } + if(isError($result)) + { + $errormsg[] = getError($result); + } + } + } + } + + if($errormsg) + $return = false; + else + $return = true; + + $this->load->view('lehre/Antrag/Wiederholung/moveLvs.rdf.php', [ + 'return' => $return, + 'errormsg' => $errormsg + ]); + } +} diff --git a/application/controllers/api/frontend/v1/Filter.php b/application/controllers/api/frontend/v1/Filter.php new file mode 100644 index 000000000..45838fc5f --- /dev/null +++ b/application/controllers/api/frontend/v1/Filter.php @@ -0,0 +1,231 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the FilterCmptLib (back-end) + * Provides data to the ajax get calls about the filter component + * Listens to ajax post calls to change the filter data + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Filter extends FHCAPI_Controller +{ + const FILTER_UNIQUE_ID = 'filterUniqueId'; // Name of the filter cmpt unique id (mandatory) + const FILTER_TYPE = 'filterType'; // The filter type (PHP filter definition) used (mandatory) + const FILTER_ID = 'filterId'; // The id of the used filter (optional) + + /** + * Calls the parent's constructor and loads the FilterCmptLib + */ + public function __construct() + { + // NOTE: FilterCmpt has its own permissions checks + parent::__construct([ + 'getFilter' => self::PERM_LOGGED, + 'removeFilterField' => self::PERM_LOGGED, + 'addFilterField' => self::PERM_LOGGED, + 'applyFilterFields' => self::PERM_LOGGED, + 'removeCustomFilter' => self::PERM_LOGGED, + 'saveCustomFilter' => self::PERM_LOGGED, + 'reloadDataset' => self::PERM_LOGGED + ]); + + // Loads the FiltersModel + $this->load->model('system/Filters_model', 'FiltersModel'); + + // Loads the FilterCmptLib with HTTP GET/POST parameters + $this->_startFilterCmptLib(); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Retrieves data about the current filter from the session and will be written on the output in JSON format + */ + public function getFilter() + { + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $session = $this->filtercmptlib->getSession(); + if (is_object($session)) { + // If stdClass it is an retval object + $session = $this->getDataOrTerminateWithError($session); + } + $this->terminateWithSuccess($session); + } + + /** + * Remove an applied filter (SQL where condition) from the current filter + */ + public function removeFilterField() + { + $this->form_validation->set_rules('filterField', 'filterField', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->removeFilterField($this->input->post('filterField')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Field removed'); + } + + /** + * Add a filter (SQL where clause) to be applied to the current filter + */ + public function addFilterField() + { + $this->form_validation->set_rules('filterField', 'filterField', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->addFilterField($this->input->post('filterField')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Field added'); + } + + /** + * Apply the filter changes + */ + public function applyFilterFields() + { + $this->form_validation->set_rules('filterFields', 'filterFields', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->applyFilterFields($this->input->post('filterFields')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Applied'); + } + + /** + * Save the current filter as a custom filter for this user with the given description + */ + public function saveCustomFilter() + { + $this->form_validation->set_rules('customFilterName', 'customFilterName', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->saveCustomFilter($this->input->post('customFilterName')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Saved'); + } + + /** + * Remove a custom filter by its filterId + */ + public function removeCustomFilter() + { + $this->form_validation->set_rules('filterId', 'filterId', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->removeCustomFilter($this->input->post('filterId')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Removed'); + } + + /** + * Reloads the dataset + */ + public function reloadDataset() + { + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $this->filtercmptlib->reloadDataset(); + + $this->terminateWithSuccess('Success'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Private methods + + /** + * Loads the FilterCmptLib with the FILTER_UNIQUE_ID parameter + * If the parameter FILTER_UNIQUE_ID is not given then the execution of the controller is terminated and + * an error message is printed + */ + private function _startFilterCmptLib() + { + $filterUniqueId = null; + $filterType = null; + $filterId = null; + + $validations = [ + [ + 'field' => self::FILTER_UNIQUE_ID, + 'label' => self::FILTER_UNIQUE_ID, + 'rules' => 'required' + ], + [ + 'field' => self::FILTER_TYPE, + 'label' => self::FILTER_TYPE, + 'rules' => 'required' + ], + ]; + + $this->load->library('form_validation'); + + if ($this->input->method() == 'get') + $this->form_validation->set_data($this->input->get()); + $this->form_validation->set_rules($validations); + + if ($this->form_validation->run()) { + $filterUniqueId = $this->input->post_get(self::FILTER_UNIQUE_ID); + $filterType = $this->input->post_get(self::FILTER_TYPE); + $filterId = $this->input->post_get(self::FILTER_ID); + + // Loads the FilterCmptLib that contains all the used logic + $this->load->library( + 'FilterCmptLib', + array( + 'filterUniqueId' => $filterUniqueId, + 'filterType' => $filterType, + 'filterId' => $filterId + ) + ); + + // Start the component + $this->filtercmptlib->start(); + } + } +} + diff --git a/application/controllers/api/frontend/v1/Navigation.php b/application/controllers/api/frontend/v1/Navigation.php new file mode 100644 index 000000000..6cbbbd385 --- /dev/null +++ b/application/controllers/api/frontend/v1/Navigation.php @@ -0,0 +1,101 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the NavigationLib (back-end) + * Provides data to the ajax get calls about the filter + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Navigation extends FHCAPI_Controller +{ + const NAVIGATION_PAGE_PARAM = 'navigation_page'; // Navigation page parameter name + + /** + * Loads the NavigationLib where the used logic lies + */ + public function __construct() + { + parent::__construct([ + 'menu' => self::PERM_LOGGED, + 'header' => self::PERM_LOGGED + ]); + + $this->_loadNavigationLib(); // Loads the NavigationLib with parameters + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * This function creates the left Menu for each Page + * @param NAVIGATION_PAGE_PARAM GET Parameter witch holds the currently called Page + * @return JSON object with the Menu Entries + */ + public function menu() + { + $menuArray = $this->navigationlib->getMenuArray($this->input->get(self::NAVIGATION_PAGE_PARAM)); + + $this->terminateWithSuccess($menuArray); + } + + /** + * This function creates the Top Menu for each Page + * @param NAVIGATION_PAGE_PARAM GET Parameter witch holds the currently called Page + * @return JSON object with the Menu Entries + */ + public function header() + { + $headerArray = $this->navigationlib->getHeaderArray($this->input->get(self::NAVIGATION_PAGE_PARAM)); + + $this->terminateWithSuccess($headerArray); + } + + //------------------------------------------------------------------------------------------------------------------ + // Private methods + + /** + * Loads the NavigationLib with the NAVIGATION_PAGE_PARAM parameter + * If the parameter NAVIGATION_PAGE_PARAM is not given then the execution of the controller is terminated and + * an error message is printed + */ + private function _loadNavigationLib() + { + // If the parameter NAVIGATION_PAGE_PARAM is present in the HTTP GET or POST + if (isset($_GET[self::NAVIGATION_PAGE_PARAM]) || isset($_POST[self::NAVIGATION_PAGE_PARAM])) + { + // If it is present in the HTTP GET + if (isset($_GET[self::NAVIGATION_PAGE_PARAM])) + { + $navigationPage = $this->input->get(self::NAVIGATION_PAGE_PARAM); // is retrieved from the HTTP GET + } + elseif (isset($_POST[self::NAVIGATION_PAGE_PARAM])) // Else if it is present in the HTTP POST + { + $navigationPage = $this->input->post(self::NAVIGATION_PAGE_PARAM); // is retrieved from the HTTP POST + } + + // Loads the NavigationLib that contains all the used logic + $this->load->library('NavigationLib', array(self::NAVIGATION_PAGE_PARAM => $navigationPage)); + } + else // Otherwise an error will be written in the output + { + show_error('Parameter "' . self::NAVIGATION_PAGE_PARAM . '" not provided!'); + } + } +} diff --git a/application/controllers/api/frontend/v1/Phrasen.php b/application/controllers/api/frontend/v1/Phrasen.php new file mode 100644 index 000000000..472308d2b --- /dev/null +++ b/application/controllers/api/frontend/v1/Phrasen.php @@ -0,0 +1,46 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the PhrasesLib (back-end) + * Provides data to the ajax get calls about the Phrasen plugin + * This controller works with JSON calls on the HTTP GET and the output is always JSON + */ +class Phrasen extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'loadModule' => self::PERM_ANONYMOUS + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * @param string $module + */ + public function loadModule($module) + { + $this->load->library('PhrasesLib', [$module], 'pj'); + $this->terminateWithSuccess(json_decode($this->pj->getJSON())); + } +} diff --git a/application/controllers/api/frontend/v1/Searchbar.php b/application/controllers/api/frontend/v1/Searchbar.php new file mode 100644 index 000000000..8b383e042 --- /dev/null +++ b/application/controllers/api/frontend/v1/Searchbar.php @@ -0,0 +1,69 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the SearchBarLib (back-end) + * Provides data to the ajax get calls about the searchbar component + * This controller works with JSON calls on the HTTP GET and the output is always JSON + */ +class Searchbar extends FHCAPI_Controller +{ + const SEARCHSTR_PARAM = 'searchstr'; + const TYPES_PARAM = 'types'; + + /** + * Object initialization + */ + public function __construct() + { + // NOTE(chris): additional permission checks will be done in SearchBarLib + parent::__construct([ + 'search' => self::PERM_LOGGED + ]); + + // Load the library SearchBarLib + $this->load->library('SearchBarLib'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Gets a JSON body via HTTP POST and provides the parameters + */ + public function search() + { + $this->load->library('form_validation'); + + // Checks if the searchstr and the types parameters are in the POSTed JSON + $this->form_validation->set_rules(self::SEARCHSTR_PARAM, null, 'required'); + $this->form_validation->set_rules(self::TYPES_PARAM . '[]', null, 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithError(SearchBarLib::ERROR_WRONG_JSON, self::ERROR_TYPE_GENERAL); + + // Convert to json the result from searchbarlib->search + $result = $this->searchbarlib->search($this->input->post(self::SEARCHSTR_PARAM), $this->input->post(self::TYPES_PARAM)); + if (property_exists($result, 'error')) + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + $this->terminateWithSuccess($result); + } +} + diff --git a/application/controllers/api/frontend/v1/studstatus/Abmeldung.php b/application/controllers/api/frontend/v1/studstatus/Abmeldung.php new file mode 100644 index 000000000..875b6484c --- /dev/null +++ b/application/controllers/api/frontend/v1/studstatus/Abmeldung.php @@ -0,0 +1,187 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +use \REST_Controller as REST_Controller; +use \Studierendenantrag_model as Studierendenantrag_model; + +/** + * This controller operates between (interface) the JS (GUI) and the AntragLib (back-end) + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Abmeldung extends FHCAPI_Controller +{ + + /** + * Calls the parent's constructor and loads the AntragLib + */ + public function __construct() + { + parent::__construct([ + 'getDetailsForNewAntrag' => self::PERM_LOGGED, + 'getDetailsForAntrag' => self::PERM_LOGGED, + 'createAntrag' => self::PERM_LOGGED, + 'cancelAntrag' => self::PERM_LOGGED + ]); + + // Libraries + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Retrieves data of the current studiengang for the current user + */ + + public function getDetailsForNewAntrag($prestudent_id) + { + if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, true)) + $this->terminateWithError('Forbidden', self::ERROR_TYPE_AUTH, REST_Controller::HTTP_FORBIDDEN); + + $result = $this->antraglib->getPrestudentAbmeldeBerechtigt($prestudent_id); + $result = $this->getDataOrTerminateWithError($result); + + if (!$result) { + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_no_student'), + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + } elseif ($result == -3) { + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_stg_blacklist'), + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + } elseif ($result == -1) { + $result = $this->antraglib->getDetailsForLastAntrag( + $prestudent_id, + [ + Studierendenantrag_model::TYP_ABMELDUNG, + Studierendenantrag_model::TYP_ABMELDUNG_STGL + ] + ); + + $data = $this->getDataOrTerminateWithError($result); + + $data->canCancel = ( + $data->status == Studierendenantragstatus_model::STATUS_CREATED && + $this->antraglib->isEntitledToCancelAntrag($data->studierendenantrag_id) + ); + + $this->terminateWithSuccess($data); + } + + $result = $this->antraglib->getDetailsForNewAntrag($prestudent_id); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getDetailsForAntrag($studierendenantrag_id) + { + if (!$this->antraglib->isEntitledToShowAntrag($studierendenantrag_id)) + return show_404(); + + $result = $this->antraglib->getDetailsForAntrag($studierendenantrag_id); + + $data = $this->getDataOrTerminateWithError($result); + + if ($data->typ !== Studierendenantrag_model::TYP_ABMELDUNG_STGL && $data->typ !== Studierendenantrag_model::TYP_ABMELDUNG) + return show_404(); + + $data->canCancel = ( + $data->status == Studierendenantragstatus_model::STATUS_CREATED && + $this->antraglib->isEntitledToCancelAntrag($data->studierendenantrag_id) + ); + + $this->terminateWithSuccess($data); + } + + public function createAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('studiensemester', 'Studiensemester', 'required'); + $this->form_validation->set_rules('prestudent_id', 'Prestudent ID', 'required'); + $this->form_validation->set_rules('grund', 'Grund', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $grund = $this->input->post('grund'); + $studiensemester = $this->input->post('studiensemester'); + $prestudent_id = $this->input->post('prestudent_id'); + + $result = $this->antraglib->getPrestudentAbmeldeBerechtigt($prestudent_id); + $result = $this->getDataOrTerminateWithError($result); + if (!$result) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_no_student'), self::ERROR_TYPE_GENERAL); + elseif ($result == -3) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_stg_blacklist'), self::ERROR_TYPE_GENERAL); + elseif ($result < 0) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_antrag_exists'), self::ERROR_TYPE_GENERAL); + + $result = $this->antraglib->createAbmeldung($prestudent_id, $studiensemester, getAuthUID(), $grund); + $data = $this->getDataOrTerminateWithError($result); + + $result = $this->antraglib->getDetailsForAntrag($data); + if (!hasData($result)) + return $this->terminateWithSuccess(true); + + $data = getData($result); + $data->canCancel = (boolean)$this->antraglib->isEntitledToCancelAntrag($data->studierendenantrag_id); + + $this->terminateWithSuccess($data); + } + + public function cancelAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('antrag_id', 'Antrag ID', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $antrag_id = $this->input->post('antrag_id'); + + if (!$this->antraglib->isEntitledToCancelAntrag($antrag_id)) + $this->terminateWithError('Forbidden', self::ERROR_TYPE_AUTH, REST_Controller::HTTP_FORBIDDEN); + + $result = $this->antraglib->cancelAntrag($antrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + $result = $this->antraglib->getDetailsForAntrag($antrag_id); + if (!hasData($result)) + $this->terminateWithSuccess($antrag_id); + + $data = getData($result); + + $this->terminateWithSuccess($data); + } +} diff --git a/application/controllers/api/frontend/v1/studstatus/Leitung.php b/application/controllers/api/frontend/v1/studstatus/Leitung.php new file mode 100644 index 000000000..2699a3dbb --- /dev/null +++ b/application/controllers/api/frontend/v1/studstatus/Leitung.php @@ -0,0 +1,428 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +use \stdClass as stdClass; +use \Studierendenantrag_model as Studierendenantrag_model; + +/** + * This controller operates between (interface) the JS (GUI) and the AntragLib (back-end) + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Leitung extends FHCAPI_Controller +{ + + /** + * Calls the parent's constructor and loads the AntragLib + */ + public function __construct() + { + parent::__construct([ + 'getActiveStgs' => ['student/antragfreigabe:r', 'student/studierendenantrag:r'], + 'getAntraege' => ['student/antragfreigabe:r', 'student/studierendenantrag:r'], + 'getHistory' => ['student/antragfreigabe:r', 'student/studierendenantrag:r'], + 'getPrestudents' => 'student/studierendenantrag:w', + 'approveAntrag' => 'student/antragfreigabe:w', + 'rejectAntrag' => 'student/antragfreigabe:w', + 'reopenAntrag' => 'student/studierendenantrag:w', + 'pauseAntrag' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], + 'unpauseAntrag' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], + 'objectAntrag' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], + 'approveObjection' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], + 'denyObjection' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'] + ]); + + // Libraries + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getActiveStgs() + { + $studiengaenge = $this->permissionlib->getSTG_isEntitledFor('student/antragfreigabe') ?: []; + $studiengaenge = array_merge($studiengaenge, $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag') ?: []); + + $result = $this->StudierendenantragModel->loadStgsWithAntraege($studiengaenge); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getAntraege($studiengang = null, $extra = null) + { + if ($studiengang && $studiengang == 'todo') { + $studiengang = $extra; + $extra = true; + } else { + $extra = false; + } + + $studiengaenge = $this->permissionlib->getSTG_isEntitledFor('student/antragfreigabe'); + if(!is_array($studiengaenge)) + $studiengaenge = []; + + + $stgsNeuanlage = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + if(!is_array($stgsNeuanlage)) + $stgsNeuanlage = []; + + $studiengaenge = array_unique(array_merge($studiengaenge, $stgsNeuanlage)); + + if ($studiengang) { + if (!in_array($studiengang, $studiengaenge)) + $this->terminateWithError( + 'Forbidden', + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + $studiengaenge = [$studiengang]; + } + + $antraege = []; + if ($studiengaenge) { + $result = $extra + ? $this->StudierendenantragModel->loadActiveForStudiengaenge($studiengaenge) + : $this->StudierendenantragModel->loadForStudiengaenge($studiengaenge); + + $antraege = $this->getDataOrTerminateWithError($result); + } + + $this->terminateWithSuccess($antraege ?: []); + } + + public function getHistory($studierendenantrag_id) + { + if (!$this->antraglib->isEntitledToSeeHistoryForAntrag($studierendenantrag_id)) + $this->terminateWithError( + 'Forbidden', + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + + $result = $this->antraglib->getAntragHistory($studierendenantrag_id); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data ?: []); + } + + public function getPrestudents() + { + $query = $this->input->post('query'); + + $studiengaenge = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + + $result = $this->antraglib->getAktivePrestudentenInStgs($studiengaenge, $query); + $result = $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($result ?: []); + } + + public function approveAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToApproveAntrag', [$this->antraglib, 'isEntitledToApproveAntrag']], + ], + [ + 'isEntitledToApproveAntrag' => $this->p->t('studierendenantrag', 'error_no_right') + ] + ); + $this->form_validation->set_rules( + 'typ', + 'Typ', + 'required|in_list[' . implode(',', [ + Studierendenantrag_model::TYP_ABMELDUNG, + Studierendenantrag_model::TYP_ABMELDUNG_STGL, + Studierendenantrag_model::TYP_UNTERBRECHUNG, + Studierendenantrag_model::TYP_WIEDERHOLUNG + ]) . ']' + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + switch ($this->input->post('typ')) { + case Studierendenantrag_model::TYP_ABMELDUNG: + case Studierendenantrag_model::TYP_ABMELDUNG_STGL: + $result = $this->antraglib->approveAbmeldung([$studierendenantrag_id], getAuthUID()); + break; + case Studierendenantrag_model::TYP_UNTERBRECHUNG: + $result = $this->antraglib->approveUnterbrechung([$studierendenantrag_id], getAuthUID()); + break; + case Studierendenantrag_model::TYP_WIEDERHOLUNG: + $result = $this->antraglib->approveWiederholung($studierendenantrag_id, getAuthUID()); + break; + } + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function rejectAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToRejectAntrag', [$this->antraglib, 'isEntitledToRejectAntrag']], + ], + [ + 'isEntitledToRejectAntrag' => $this->p->t('studierendenantrag', 'error_no_right') + ] + ); + $this->form_validation->set_rules('grund', 'Grund', 'required'); + $this->form_validation->set_rules( + 'typ', + 'Typ', + 'required|in_list[' . implode(',', [ + Studierendenantrag_model::TYP_UNTERBRECHUNG + ]) . ']' + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + $grund = $this->input->post('grund'); + + $result = $this->antraglib->rejectUnterbrechung([$studierendenantrag_id], getAuthUID(), $grund); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function reopenAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToReopenAntrag', [$this->antraglib, 'isEntitledToReopenAntrag']], + ], + [ + 'isEntitledToReopenAntrag' => $this->p->t('studierendenantrag', 'error_no_right') + ] + ); + $this->form_validation->set_rules( + 'typ', + 'Typ', + 'required|in_list[' . implode(',', [ + Studierendenantrag_model::TYP_WIEDERHOLUNG + ]) . ']' + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->reopenWiederholung($studierendenantrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function pauseAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToPauseAntrag', [$this->antraglib, 'isEntitledToPauseAntrag']], + ['antragCanBeManualPaused', [$this->antraglib, 'antragCanBeManualPaused']] + ], + [ + 'isEntitledToPauseAntrag' => $this->p->t('studierendenantrag', 'error_no_right'), + 'antragCanBeManualPaused' => $this->p->t( + 'studierendenantrag', + 'error_not_pauseable', + ['id' => $this->input->post('studierendenantrag_id')] + ) + ] + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->pauseAntrag($studierendenantrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function unpauseAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToUnpauseAntrag', [$this->antraglib, 'isEntitledToUnpauseAntrag']], + ['antragCanBeManualUnpaused', [$this->antraglib, 'antragCanBeManualUnpaused']] + ], + [ + 'isEntitledToUnpauseAntrag' => $this->p->t('studierendenantrag', 'error_no_right'), + 'antragCanBeManualUnpaused' => $this->p->t( + 'studierendenantrag', + 'error_not_paused', + ['id' => $this->input->post('studierendenantrag_id')] + ) + ] + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->unpauseAntrag($studierendenantrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function objectAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToObjectAntrag', [$this->antraglib, 'isEntitledToObjectAntrag']], + ['canBeObjected', function ($a) { + return $this->antraglib->hasType($a, Studierendenantrag_model::TYP_ABMELDUNG_STGL); + }] + ], + [ + 'isEntitledToObjectAntrag' => $this->p->t('studierendenantrag', 'error_no_right'), + 'canBeObjected' => $this->p->t( + 'studierendenantrag', + 'error_no_objection' + ) + ] + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->objectAbmeldung($studierendenantrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function approveObjection() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToObjectAntrag', [$this->antraglib, 'isEntitledToObjectAntrag']], + ['isObjected', function ($a) { + return $this->antraglib->hasStatus($a, Studierendenantragstatus_model::STATUS_OBJECTED); + }] + ], + [ + 'isEntitledToObjectAntrag' => $this->p->t('studierendenantrag', 'error_no_right'), + 'isObjected' => $this->p->t( + 'studierendenantrag', + 'error_not_objected' + ) + ] + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->cancelAntrag($studierendenantrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } + + public function denyObjection() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToObjectAntrag', [$this->antraglib, 'isEntitledToObjectAntrag']], + ['isObjected', function ($a) { + return $this->antraglib->hasStatus($a, Studierendenantragstatus_model::STATUS_OBJECTED); + }] + ], + [ + 'isEntitledToObjectAntrag' => $this->p->t('studierendenantrag', 'error_no_right'), + 'isObjected' => $this->p->t( + 'studierendenantrag', + 'error_not_objected' + ) + ] + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + $grund = $this->input->post('grund'); + + $result = $this->antraglib->denyObjectionAbmeldung($studierendenantrag_id, getAuthUID(), $grund); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } +} diff --git a/application/controllers/components/Antrag/Unterbrechung.php b/application/controllers/api/frontend/v1/studstatus/Unterbrechung.php similarity index 51% rename from application/controllers/components/Antrag/Unterbrechung.php rename to application/controllers/api/frontend/v1/studstatus/Unterbrechung.php index 33bd0035d..abf58cf4f 100755 --- a/application/controllers/components/Antrag/Unterbrechung.php +++ b/application/controllers/api/frontend/v1/studstatus/Unterbrechung.php @@ -1,4 +1,20 @@ . + */ if (! defined('BASEPATH')) exit('No direct script access allowed'); @@ -6,23 +22,28 @@ use \Studierendenantrag_model as Studierendenantrag_model; use \DateTime as DateTime; /** - * + * This controller operates between (interface) the JS (GUI) and the AntragLib (back-end) + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON */ -class Unterbrechung extends FHC_Controller +class Unterbrechung extends FHCAPI_Controller { /** - * Calls the parent's constructor and loads the FilterCmptLib + * Calls the parent's constructor and loads the AntragLib */ public function __construct() { - parent::__construct(); + parent::__construct([ + 'getDetailsForNewAntrag' => self::PERM_LOGGED, + 'getDetailsForAntrag' => self::PERM_LOGGED, + 'createAntrag' => self::PERM_LOGGED, + 'cancelAntrag' => self::PERM_LOGGED + ]); // Configs $this->load->config('studierendenantrag'); // Libraries - $this->load->library('AuthLib'); $this->load->library('AntragLib'); // Load language phrases @@ -38,74 +59,62 @@ class Unterbrechung extends FHC_Controller public function getDetailsForNewAntrag($prestudent_id) { - if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, false)) { - $this->output->set_status_header(403); - return $this->outputJsonError('Forbidden'); - } + if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, false)) + $this->terminateWithError('Forbidden', self::ERROR_TYPE_AUTH, REST_Controller::HTTP_FORBIDDEN); + $result = $this->antraglib->getPrestudentUnterbrechungsBerechtigt($prestudent_id); - if (isError($result)) { - $this->output->set_status_header(500); - return $this->outputJsonError(getError($result)); - } - $result = $result->retval; + $result = $this->getDataOrTerminateWithError($result); + if (!$result) { - $this->output->set_status_header(403); - return $this->outputJsonError($this->p->t('studierendenantrag', 'error_no_student')); - } - elseif ($result == -1) - { + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_no_student'), + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + } elseif ($result == -1) { $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id, Studierendenantrag_model::TYP_UNTERBRECHUNG); - if (isError($result)) { - return $this->outputJsonError(getError($result)); - } + + $data = $this->getDataOrTerminateWithError($result); - return $this->outputJsonSuccess(getData($result)); - } - elseif ($result == -2) - { + return $this->terminateWithSuccess($data); + } elseif ($result == -2) { $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id); - if (isError($result)) { - return $this->outputJsonError(getError($result)); - } - $result = getData($result); - $this->output->set_status_header(400); - return $this->outputJsonError($this->p->t('studierendenantrag', 'error_antrag_pending', [ + $data = $this->getDataOrTerminateWithError($result); + + return $this->terminateWithError($this->p->t('studierendenantrag', 'error_antrag_pending', [ 'typ' => $this->p->t('studierendenantrag', 'antrag_typ_' . $result->typ) ])); + } elseif ($result == -3) { + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_stg_blacklist'), + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); } - elseif ($result == -3) - { - $this->output->set_status_header(403); - return $this->outputJsonError($this->p->t('studierendenantrag', 'error_stg_blacklist')); - } + $result = $this->antraglib->getDetailsForNewAntrag($prestudent_id); - if (isError($result)) { - return $this->outputJsonError(getError($result)); - } - $data = getData($result); + $data = $this->getDataOrTerminateWithError($result); - $data->studiensemester = $this->antraglib->getSemesterForUnterbrechung($data->studiensemester_kurzbz); + $data->studiensemester = $this->antraglib->getSemesterForUnterbrechung($prestudent_id, null); - $this->outputJsonSuccess($data); + $this->terminateWithSuccess($data); } public function getDetailsForAntrag($studierendenantrag_id) { - if (!$this->antraglib->isEntitledToShowAntrag($studierendenantrag_id)) return show_404(); + if (!$this->antraglib->isEntitledToShowAntrag($studierendenantrag_id)) + return show_404(); $result = $this->antraglib->getDetailsForAntrag($studierendenantrag_id); - if (isError($result)) { - return $this->outputJsonError(getError($result)); - } - $data = getData($result); + $data = $this->getDataOrTerminateWithError($result); if ($data->typ !== Studierendenantrag_model::TYP_UNTERBRECHUNG) return show_404(); - $this->outputJsonSuccess($data); + $this->terminateWithSuccess($data); } public function createAntrag() @@ -125,9 +134,8 @@ class Unterbrechung extends FHC_Controller ] ); - if ($this->form_validation->run() == false) - { - return $this->outputJsonError($this->form_validation->error_array()); + if (!$this->form_validation->run()) { + $this->terminateWithValidationErrors($this->form_validation->error_array()); } $grund = $this->input->post('grund'); @@ -136,26 +144,18 @@ class Unterbrechung extends FHC_Controller $datum_wiedereinstieg = $this->input->post('datum_wiedereinstieg'); $dms_id = null; - $result = $this->antraglib->getPrestudentUnterbrechungsBerechtigt($prestudent_id); - if (isError($result)) { - return $this->outputJsonError(['db' => getError($result)]); - } - $result = $result->retval; - if (!$result) - { - return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_no_student')]); - } - elseif ($result == -3) - { - return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_stg_blacklist')]); - } - elseif ($result < 0) - { - return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_antrag_exists')]); - } + $result = $this->antraglib->getPrestudentUnterbrechungsBerechtigt($prestudent_id, $studiensemester, $datum_wiedereinstieg); - if(isset($_FILES['attachment']) && (!isset($_FILES['attachment']['error']) || $_FILES['attachment']['error'] != UPLOAD_ERR_NO_FILE)) - { + $result = $this->getDataOrTerminateWithError($result); + + if (!$result) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_no_student'), self::ERROR_TYPE_GENERAL); + elseif ($result == -3) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_stg_blacklist'), self::ERROR_TYPE_GENERAL); + elseif ($result < 0) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_antrag_exists'), self::ERROR_TYPE_GENERAL); + + if (isset($_FILES['attachment']) && (!isset($_FILES['attachment']['error']) || $_FILES['attachment']['error'] != UPLOAD_ERR_NO_FILE)) { $this->load->library('DmsLib'); $dms = $this->config->item('unterbrechung_dms'); @@ -167,53 +167,46 @@ class Unterbrechung extends FHC_Controller $allowed_filetypes = $this->config->item('unterbrechung_dms_filetypes') ?: ['*']; $result = $this->dmslib->upload($dms, 'attachment', $allowed_filetypes); - if(isError($result)) - { - return $this->outputJsonError(['db' => getError($result)]); - } - $dms_id = getData($result)['dms_id']; + + $data = $this->getDataOrTerminateWithError($result); + + $dms_id = $data['dms_id']; } $result = $this->antraglib->createUnterbrechung($prestudent_id, $studiensemester, getAuthUID(), $grund, $datum_wiedereinstieg, $dms_id); - if(isError($result)) - { - return $this->outputJsonError(['db' => getError($result)]); - } - $antragId = getData($result); + $antragId = $this->getDataOrTerminateWithError($result); + $result = $this->antraglib->getDetailsForAntrag($antragId); - if(!hasData($result)) - return $this->outputJsonSuccess($antragId); - $this->outputJsonSuccess(getData($result)); + if (!hasData($result)) + $this->terminateWithSuccess($antragId); + + $this->terminateWithSuccess(getData($result)); } public function cancelAntrag() { $this->load->library('form_validation'); - $_POST = json_decode($this->input->raw_input_stream, true); - $this->form_validation->set_rules('antrag_id', 'Antrag ID', 'required'); - if ($this->form_validation->run() == false) - { - return $this->outputJsonError($this->form_validation->error_array()); + if (!$this->form_validation->run()) { + $this->terminateWithValidationErrors($this->form_validation->error_array()); } $antrag_id = $this->input->post('antrag_id'); $result = $this->antraglib->cancelAntrag($antrag_id, getAuthUID()); - if (isError($result)) - { - return $this->outputJsonError(['db' => getError($result)]); - } + + $this->getDataOrTerminateWithError($result); $result = $this->antraglib->getDetailsForAntrag($antrag_id); if (!hasData($result)) - return $this->outputJsonSuccess($antrag_id); - $this->outputJsonSuccess(getData($result)); + return $this->terminateWithSuccess($antrag_id); + + $this->terminateWithSuccess(getData($result)); } public function isValidDate($date) diff --git a/application/controllers/api/frontend/v1/studstatus/Wiederholung.php b/application/controllers/api/frontend/v1/studstatus/Wiederholung.php new file mode 100644 index 000000000..1a8f70d52 --- /dev/null +++ b/application/controllers/api/frontend/v1/studstatus/Wiederholung.php @@ -0,0 +1,258 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +use \REST_Controller as REST_Controller; +use \Studierendenantragstatus_model as Studierendenantragstatus_model; + +/** + * This controller operates between (interface) the JS (GUI) and the AntragLib (back-end) + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Wiederholung extends FHCAPI_Controller +{ + + /** + * Calls the parent's constructor and loads the FilterCmptLib + */ + public function __construct() + { + parent::__construct([ + 'getDetailsForNewAntrag' => self::PERM_LOGGED, + 'createAntrag' => self::PERM_LOGGED, + 'cancelAntrag' => self::PERM_LOGGED, + 'getLvs' => self::PERM_LOGGED, + 'saveLvs' => ['student/studierendenantrag:w'] + ]); + + // Libraries + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'global', + 'studierendenantrag' + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Retrieves data of the current studiengang for the current user + */ + + public function getDetailsForNewAntrag($prestudent_id) + { + if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, false)) + $this->terminateWithError('Forbidden', self::ERROR_TYPE_AUTH, REST_Controller::HTTP_FORBIDDEN); + + $result = $this->antraglib->getPrestudentWiederholungsBerechtigt($prestudent_id); + $result = $this->getDataOrTerminateWithError($result); + + if (!$result) { + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_no_student_no_failed_exam'), + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + } elseif ($result == -1) { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id, Studierendenantrag_model::TYP_WIEDERHOLUNG); + $data = $this->getDataOrTerminateWithError($result); + + $result = $this->antraglib->getFailedExamForPrestudent($prestudent_id, $data->datum, $data->studiensemester_kurzbz); + // NOTE(chris): error handling for this function should already happenden in antraglib->getPrestudentWiederholungsBerechtigt() + $pruefungsdata = current(getData($result)); + + $data->studiensemester_kurzbz = $pruefungsdata->studiensemester_kurzbz; + $data->lvbezeichnung = $pruefungsdata->lvbezeichnung; + $data->pruefungsdatum = $pruefungsdata->datum; + + $this->terminateWithSuccess($data); + } elseif ($result == -2) { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id); + $result = $this->getDataOrTerminateWithError($result); + + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_antrag_pending', [ + 'typ' => $this->p->t('studierendenantrag', 'antrag_typ_' . $result->typ) + ]), + self::ERROR_TYPE_GENERAL, + REST_Controller::HTTP_BAD_REQUEST + ); + } elseif ($result == -3) { + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_stg_blacklist'), + self::ERROR_TYPE_GENERAL, + REST_Controller::HTTP_BAD_REQUEST + ); + } + + $result = $this->antraglib->getDetailsForNewAntrag($prestudent_id); + $data = $this->getDataOrTerminateWithError($result); + + $result = $this->antraglib->getFailedExamForPrestudent($prestudent_id); + // NOTE(chris): error handling for this function should already happenden in antraglib->getPrestudentWiederholungsBerechtigt() + $pruefungsdata = current(getData($result)); + + $data->studiensemester_kurzbz = $pruefungsdata->studiensemester_kurzbz; + $data->lvbezeichnung = $pruefungsdata->lvbezeichnung; + $data->pruefungsdatum = $pruefungsdata->datum; + + $this->terminateWithSuccess($data); + } + + public function createAntrag() + { + $this->createAntragWithStatus(true); + } + + public function cancelAntrag() + { + $this->createAntragWithStatus(false); + } + + protected function createAntragWithStatus($repeat) + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('prestudent_id', 'Prestudent ID', 'required'); + $this->form_validation->set_rules('studiensemester', 'Studiensemester', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $prestudent_id = $this->input->post('prestudent_id'); + $studiensemester = $this->input->post('studiensemester'); + + $result = $this->antraglib->getPrestudentWiederholungsBerechtigt($prestudent_id); + $result = $this->getDataOrTerminateWithError($result); + + if (!$result) { + $this->terminateWithError($this->p->t('studierendenantrag', 'error_no_student'), self::ERROR_TYPE_GENERAL); + } elseif ($result == -1) { + $result = $this->PrestudentstatusModel->getLastStatus($prestudent_id); + $result = $this->getDataOrTerminateWithError($result); + if (!$result) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_no_prestudentstatus', [ + 'prestudent_id' => $prestudent_id + ]), self::ERROR_TYPE_GENERAL); + if (!in_array(current($result)->status_kurzbz, $this->config->item('antrag_prestudentstatus_whitelist'))) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_no_student'), self::ERROR_TYPE_GENERAL); + } elseif ($result == -2) { + $this->terminateWithError($this->p->t('studierendenantrag', 'error_antrag_exists'), self::ERROR_TYPE_GENERAL); + } elseif ($result == -3) { + $this->terminateWithError($this->p->t('studierendenantrag', 'error_stg_blacklist'), self::ERROR_TYPE_GENERAL); + } + + $result = $this->antraglib->createWiederholung($prestudent_id, $studiensemester, getAuthUID(), $repeat); + $antragId = $this->getDataOrTerminateWithError($result); + + $result = $this->antraglib->getDetailsForAntrag($antragId); + + if (!hasData($result)) + $this->terminateWithSuccess(true); + + $data = getData($result); + + $result = $this->antraglib->getFailedExamForPrestudent($prestudent_id); + // NOTE(chris): error handling for this function should already happenden in antraglib->getPrestudentWiederholungsBerechtigt() + $pruefungsdata = current(getData($result)); + + $data->studiensemester_kurzbz = $pruefungsdata->studiensemester_kurzbz; + $data->lvbezeichnung = $pruefungsdata->lvbezeichnung; + $data->pruefungsdatum = $pruefungsdata->datum; + + $this->terminateWithSuccess($data); + } + + + public function getLvs($antrag_id) + { + $result = $this->antraglib->getLvsForAntrag($antrag_id); + if (isError($result)) { + $error = getError($result); + if ($error == 'Forbidden') + $this->terminateWithError( + $error, + self::ERROR_TYPE_AUTH, + REST_Controller::HTTP_FORBIDDEN + ); + $this->terminateWithError( + $error, + self::ERROR_TYPE_GENERAL + ); + } + $lvs = getData($result); + + $this->terminateWithSuccess($lvs); + } + + public function saveLvs() + { + $forbiddenLvs = $this->input->post('forbiddenLvs'); + $mandatoryLvs = $this->input->post('mandatoryLvs'); + $antragsLvs = array_merge($forbiddenLvs, $mandatoryLvs); + + if (!$antragsLvs) + $this->terminateWithError($this->p->t('studierendenantrag', 'error_no_lv'), self::ERROR_TYPE_GENERAL); + + $insert = array_map(function ($lv) { + return [ + 'studierendenantrag_id' => $lv['studierendenantrag_id'], + 'lehrveranstaltung_id' => $lv['lehrveranstaltung_id'], + 'note' => $lv['zugelassen'] + ? ($lv['zugelassen'] == 1 ? 0 : $this->config->item('wiederholung_note_angerechnet')) + : $this->config->item('wiederholung_note_nicht_zugelassen'), + 'anmerkung' => $lv['anmerkung'], + 'insertvon' => getAuthUID(), + 'studiensemester_kurzbz' => $lv['studiensemester_kurzbz'] + ]; + }, $antragsLvs); + + $antrag_ids = array_unique(array_map(function ($lv) { + return $lv['studierendenantrag_id']; + }, $insert)); + + foreach ($antrag_ids as $antrag_id) { + $result = $this->StudierendenantragModel->loadIdAndStatusWhere([ + 'studierendenantrag_id' => $antrag_id + ]); + $antrag = $this->getDataOrTerminateWithError($result); + if (!$antrag) + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_no_antrag_found', ['id' => $antrag_id]), + self::ERROR_TYPE_GENERAL + ); + $antrag = current($antrag); + + if ($antrag->status != Studierendenantragstatus_model::STATUS_CREATED + && $antrag->status != Studierendenantragstatus_model::STATUS_LVSASSIGNED) + $this->terminateWithError( + $this->p->t('studierendenantrag', 'error_antrag_locked'), + self::ERROR_TYPE_GENERAL + ); + } + + $result = $this->antraglib->saveLvs($insert); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } +} diff --git a/application/controllers/components/Filter.php b/application/controllers/components/Filter.php index bde7d7ed7..617edd69f 100755 --- a/application/controllers/components/Filter.php +++ b/application/controllers/components/Filter.php @@ -9,6 +9,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON * NOTE: extends the FHC_Controller instead of the Auth_Controller because the FilterCmpt has its * own permissions check + * TODO(chris): deprecated */ class Filter extends FHC_Controller { diff --git a/application/controllers/components/Phrasen.php b/application/controllers/components/Phrasen.php index 87516ce00..3ac35a652 100755 --- a/application/controllers/components/Phrasen.php +++ b/application/controllers/components/Phrasen.php @@ -3,7 +3,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); /** - * + * TODO(chris): deprecated */ class Phrasen extends FHC_Controller { diff --git a/application/controllers/components/SearchBar.php b/application/controllers/components/SearchBar.php index d19113177..eac1a4cbc 100755 --- a/application/controllers/components/SearchBar.php +++ b/application/controllers/components/SearchBar.php @@ -3,7 +3,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); /** - * + * TODO(chris): deprecated */ class SearchBar extends FHC_Controller { @@ -21,7 +21,7 @@ class SearchBar extends FHC_Controller // NOTE: // - A user must be authenticated via another controller to access this one // - It is loaded to be able to call the isLogged function later - $this->load->library('AuthLib', array(false)); + $this->load->library('AuthLib'); // Load the library SearchBarLib $this->load->library('SearchBarLib'); diff --git a/application/controllers/jobs/AntragJob.php b/application/controllers/jobs/AntragJob.php index 0663be94b..717561589 100755 --- a/application/controllers/jobs/AntragJob.php +++ b/application/controllers/jobs/AntragJob.php @@ -19,6 +19,8 @@ class AntragJob extends JOB_Controller // Loads SanchoHelper $this->load->helper('hlp_sancho_helper'); + $this->load->library('AntragLib'); + // Load Model $this->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); $this->load->model('education/Studierendenantragstatus_model', 'StudierendenantragstatusModel'); @@ -172,7 +174,16 @@ class AntragJob extends JOB_Controller $cc = $leitung['Details']->email; // NOTE(chris): Sancho mail - if (sendSanchoMail("Sancho_Mail_Antrag_Stgl", $data, $to, 'Anträge - Aktion(en) erforderlich', DEFAULT_SANCHO_HEADER_IMG, DEFAULT_SANCHO_FOOTER_IMG, '', $cc)) + if (sendSanchoMail( + "Sancho_Mail_Antrag_Stgl", + $data, + $to, + 'Anträge - Aktion(en) erforderlich', + DEFAULT_SANCHO_HEADER_IMG, + DEFAULT_SANCHO_FOOTER_IMG, + '', + $cc + )) $count++; } @@ -316,12 +327,52 @@ class AntragJob extends JOB_Controller } else { $deregisterStatus = getData($result); + $result = $this->antraglib->pauseAntrag( + $prestudent->studierendenantrag_id, + Studierendenantragstatus_model::INSERTVON_DEREGISTERED + ); + if (isError($result)) + $this->logError(getError($result)); + $result = $this->prestudentlib->setAbbrecher($prestudent->prestudent_id, '', $insertvon); if (isError($result)) { $this->StudierendenantragstatusModel->delete($deregisterStatus); $this->logError(getError($result)); } else { $count++; + + $datum_kp = new DateTime($prestudent->datum); + $dataMail = array( + 'name'=> trim($prestudent->vorname . ' '. $prestudent->nachname), + 'vorname' => $prestudent->vorname, + 'nachname' => $prestudent->nachname, + 'pers_kz'=> $prestudent->matrikelnr, + 'stg' => $prestudent->bezeichnung, + 'lvbezeichnung' => $prestudent->lvbezeichnung, + 'datum_kp' => $datum_kp->format('d.m.Y'), + 'studiensemester'=> $prestudent->studiensemester_kurzbz, + 'Orgform'=> $prestudent->orgform, + 'prestudent_id' => $prestudent->prestudent_id, + 'fristablauf' => $dateDeadline->format('d.m.Y') + ); + + $email = $this->StudentModel->getEmailFH($this->StudentModel->getUID($prestudent->prestudent_id)); + // Mail to Student + if (!sendSanchoMail('Sancho_Mail_Antrag_W_DL_Stud', $dataMail, $email, 'Wiederholung: Frist abgelaufen')) { + $this->logWarning("Failed to send Notification to " . $email); + } + + $result = $this->StudiengangModel->load($prestudent->studiengang_kz); + if (!hasData($result)) { + $this->logWarning('No Studiengang found'); + continue; + } + $studiengang = current(getData($result)); + $email = $studiengang->email; + // Mail to Assistenz + if (!sendSanchoMail('Sancho_Mail_Antrag_W_DL_Assist', $dataMail, $email, 'Wiederholung: Frist abgelaufen')) { + $this->logWarning("Failed to send Notification to " . $email); + } } } } @@ -339,8 +390,6 @@ class AntragJob extends JOB_Controller { $this->logInfo('Start Job handleAbmeldungenStglDeadline'); - $this->load->library('AntragLib'); - $insertvon = $this->config->item('antrag_job_systemuser'); if (!$insertvon) { $this->logError('Config "antrag_job_systemuser" nicht gesetzt'); @@ -364,7 +413,10 @@ class AntragJob extends JOB_Controller $this->StudierendenantragModel->addSelect('s.insertamum'); $this->StudierendenantragModel->addSelect('s.insertvon'); - $this->StudierendenantragModel->db->where_in('public.get_rolle_prestudent(prestudent_id, studiensemester_kurzbz)', $this->config->item('antrag_prestudentstatus_whitelist')); + $this->StudierendenantragModel->db->where_in( + 'public.get_rolle_prestudent(prestudent_id, studiensemester_kurzbz)', + $this->config->item('antrag_prestudentstatus_whitelist') + ); $result = $this->StudierendenantragModel->getWithLastStatusWhere([ 'typ' => Studierendenantrag_model::TYP_ABMELDUNG_STGL, @@ -393,6 +445,10 @@ class AntragJob extends JOB_Controller else { $deregisterStatus = getData($result); + $result = $this->antraglib->pauseAntrag($antrag->studierendenantrag_id, Studierendenantragstatus_model::INSERTVON_DEREGISTERED); + if (isError($result)) + $this->logError(getError($result)); + $result = $this->prestudentlib->setAbbrecher( $antrag->prestudent_id, $antrag->studiensemester_kurzbz, @@ -438,7 +494,6 @@ class AntragJob extends JOB_Controller $this->logWarning("Failed to send Notification to " . $email); } } - } } $this->logInfo($count . "/" . count($antraege) . " Students set to Abbrecher"); @@ -569,12 +624,6 @@ class AntragJob extends JOB_Controller } } - $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); - $result = $this->PrestudentstatusModel->loadLastWithStgDetails($prestudent->prestudent_id, $prestudent->studiensemester_kurzbz); - if (hasData($result)) { - $ausbildungssemester = current(getData($result))->semester; - } - $dataMail = array( 'name'=> trim($prestudent->vorname . ' '. $prestudent->nachname), 'vorname' => $prestudent->vorname, @@ -591,7 +640,7 @@ class AntragJob extends JOB_Controller 'fristablauf' => $fristende->format('d.m.Y'), 'pre_wiederholer_sem' => $next_sem, 'wiederholer_sem' => $sem_after_next_sem, - 'sem' => $ausbildungssemester + 'sem' => $prestudent->ausbildungssemester ); // NOTE(chris): Sancho mail diff --git a/application/controllers/jobs/ReihungstestJob.php b/application/controllers/jobs/ReihungstestJob.php index c31ed54c9..b55287439 100755 --- a/application/controllers/jobs/ReihungstestJob.php +++ b/application/controllers/jobs/ReihungstestJob.php @@ -1023,7 +1023,7 @@ class ReihungstestJob extends JOB_Controller { $studiengang = $this->StudiengangModel->load($stg); $mailcontent = ''; - + $content = false; foreach ($orgform AS $art=>$value) { // Orgform nur dazu schreiben, wenn es mehr als Eine gibt @@ -1044,6 +1044,7 @@ class ReihungstestJob extends JOB_Controller $mailcontent .= ''.$bewerber.''; } $mailcontent .= '

'; + $content = true; } if (isset($value['AufnahmeHoeherePrio']) && !isEmptyArray($value['AufnahmeHoeherePrio'])) { @@ -1058,6 +1059,7 @@ class ReihungstestJob extends JOB_Controller $mailcontent .= ''.$bewerber.''; } $mailcontent .= ''; + $content = true; } if (isset($value['AbgewiesenHoeherePrio']) && !isEmptyArray($value['AbgewiesenHoeherePrio'])) { @@ -1071,6 +1073,7 @@ class ReihungstestJob extends JOB_Controller $mailcontent .= ''.$bewerber.''; } $mailcontent .= ''; + $content = true; } if ($bcc != '' && isset($value['AbgewiesenWeilBewerber']) && !isEmptyArray($value['AbgewiesenWeilBewerber'])) { @@ -1085,13 +1088,14 @@ class ReihungstestJob extends JOB_Controller $mailcontent .= ''.$bewerber.''; } $mailcontent .= ''; + $content = true; } } $mailcontent_data_arr['table'] = $mailcontent; // Send email in Sancho design - if (!isEmptyString($mailcontent)) + if (!isEmptyString($mailcontent) && $content === true) { sendSanchoMail( 'Sancho_ReihungstestteilnehmerJob', diff --git a/application/controllers/system/MigrateContract.php b/application/controllers/system/MigrateContract.php index 99b894473..f011be356 100644 --- a/application/controllers/system/MigrateContract.php +++ b/application/controllers/system/MigrateContract.php @@ -677,4 +677,35 @@ class MigrateContract extends CLI_Controller else return 0; } + + /** + * Habilitation wird aus der Tabelle bis.tbl_bisverwendung in die Tabelle public.tbl_mitarbeiter uebernommen + * Sofern die Person einmal in den Verwendungen eine habiliation eingetragen hat wird diese in den MA-Datensatz übernommen + * Da es in der regel öfter vorkommt dass das hakerl vergessen wurde beim Vertragswechsel als dass die person die habiliation verliert. + */ + public function migrateHabilitation() + { + $this->load->model('ressource/Mitarbeiter_model','MitarbeiterModel'); + $db = new DB_Model(); + + $qry = " + SELECT + distinct mitarbeiter_uid + FROM + bis.tbl_bisverwendung + WHERE + habilitation=true"; + + $resultHabilitation = $db->execReadOnlyQuery($qry); + + if (isSuccess($resultHabilitation) && hasData($resultHabilitation)) + { + $habilitationen = getData($resultHabilitation); + + foreach ($habilitationen as $row_habilitationen) + { + $this->MitarbeiterModel->update($row_habilitationen->mitarbeiter_uid, array('habilitation'=>true)); + } + } + } } diff --git a/application/controllers/system/MigrateSalary.php b/application/controllers/system/MigrateSalary.php index e8771f913..4bd1d3e7d 100644 --- a/application/controllers/system/MigrateSalary.php +++ b/application/controllers/system/MigrateSalary.php @@ -3,7 +3,7 @@ * Job zur einmaligen Import der Gehälter * * Aufruf (Encode / im Filenmae mit %2F): - * php index.ci.php system/MigrateSalary/import filename + * php index.ci.php system/MigrateSalary/import filename * */ /* @@ -34,7 +34,7 @@ class MigrateSalary extends CLI_Controller $this->load->model('vertragsbestandteil/VertragsbestandteilStunden_model','VertragsbestandteilStundenModel'); $this->load->model('vertragsbestandteil/VertragsbestandteilFreitext_model','VertragsbestandteilFreitextModel'); $this->load->model('vertragsbestandteil/VertragsbestandteilFunktion_model','VertragsbestandteilFunktionModel'); - + } // ----------------------------------------------------------------------------------------------------------------- @@ -45,7 +45,7 @@ class MigrateSalary extends CLI_Controller */ public function import($file) { - + // CSV Laden $file = urldecode($file); if($handle = fopen($file, "r")) @@ -108,8 +108,8 @@ class MigrateSalary extends CLI_Controller } else { - if ($data[$i] != '' - && isset($gehaltsarr[$gehaltsindex]) && isset($gehaltsarr[$gehaltsindex]['betrag']) + if ($data[$i] != '' + && isset($gehaltsarr[$gehaltsindex]) && isset($gehaltsarr[$gehaltsindex]['betrag']) && $gehaltsarr[$gehaltsindex]['betrag'] == $data[$i]) { // Gehalt bleibt gleich @@ -138,30 +138,31 @@ class MigrateSalary extends CLI_Controller } } } - + $monat++; } // Zeile zu Ende - Ende Datum setzen wenn nicht für alle Monate ein Eintrag vorhanden ist if($monat < count($monate) && isset($gehaltsarr[$gehaltsindex])) - $gehaltsarr[$gehaltsindex]['ende'] == $monate[$monat-1]; - + $gehaltsarr[$gehaltsindex]['ende'] = $monate[$monat-1]; + } $this->_saveGehalt($lastuser, $gehaltsarr); } } /** - * Ermittelt das passende Dienstverhaeltnis uns speichert den + * Ermittelt das passende Dienstverhaeltnis uns speichert den * Gehaltsbestandteil */ private function _saveGehalt($uid, $gehaltsarr) - { + { $failed = false; $this->db->trans_begin(); foreach($gehaltsarr as $row_gehalt) { + //var_dump($row_gehalt); $auszahlungen = 14; $dvid = ''; $vbsid = ''; @@ -171,16 +172,18 @@ class MigrateSalary extends CLI_Controller //DV und VBS Ermitteln $dv = $this->DienstverhaeltnisModel->getDVByPersonUID($uid, $this->OE_DEFAULT, $row_gehalt['beginn']); - if (!hasData($dv)) + // Wenn keiner gefunden wird oder mit Monatsersteln nur ein externer gefunden wird, weitersuchen ob im Monat noch ein + // "richtiger" Vertrag startet + if (!hasData($dv) || getData($dv)[0]->vertragsart_kurzbz='externerLehrender') { $date = new DateTime($row_gehalt['beginn']); $date->modify('last day of this month'); $last_day_this_month = $date->format('Y-m-d'); - // Wenn mit Monatsersten kein DV gefunden wird, wird stattdessen mit Monatsletzten gesucht um DVs zu finden + // Wenn mit Monatsersten kein DV gefunden wird, wird stattdessen mit Monatsletzten gesucht um DVs zu finden // für Personen die erst später im Monat in ihr DV einsteigen - $dv = $this->DienstverhaeltnisModel->getDVByPersonUID($uid, $this->OE_DEFAULT, $last_day_this_month); - + $dv = $this->DienstverhaeltnisModel->getDVByPersonUIDOverlapping($uid, $this->OE_DEFAULT, $row_gehalt['beginn'], $last_day_this_month); + if (!hasData($dv)) { echo "\nKein passendes DV gefunden für User ".$uid." und Datum ".$row_gehalt['beginn']." -> ROLLBACK\n"; @@ -189,34 +192,53 @@ class MigrateSalary extends CLI_Controller } else { - // Gehaltsstart wird auf den Start des DV korrigiert wenn nicht der Monatserste - $row_gehalt['beginn'] = getData($dv)[0]->von; + $resultdata = getData($dv); + foreach($resultdata as $dvdata) + { + // Externer DV wird in Monatsmitte zu echten DV - daher weitersuchen bei externenDVs da + // diese sowieso kein Gehalt zugeordnet haben + if($dvdata->vertragsart_kurzbz != 'externerLehrender') + { + $dvid = $dvdata->dienstverhaeltnis_id; + // Gehaltsstart wird auf den Start des DV korrigiert wenn nicht der Monatserste + // nur wenn das Beginndatum vor dem DV-Start liegt da sonst das Datum korrigiert wird + // wenn der Vertragsbestandteil wechselt + if($row_gehalt['beginn'] < $dvdata->von) + $row_gehalt['beginn'] = $dvdata->von; + break; + } + } } } - - $resultdata = getData($dv); - if (count($resultdata) !== 1) + else { - echo "Kein oder Mehrere DVs gefunden -> ROLLBACK"; + $resultdata = getData($dv); + + if (count($resultdata) == 1) + $dvid = $resultdata[0]->dienstverhaeltnis_id; + } + + if ($dvid == '') + { + echo "Kein oder mehrere DVs gefunden -> ROLLBACK"; $failed = true; break; } - $dvid = $resultdata[0]->dienstverhaeltnis_id; - $allin = $this->_isAllIn($dvid, $row_gehalt['beginn']); $db = new DB_Model(); $resultVBS = $this->_getVBS($dvid, $row_gehalt['beginn']); - + if (hasData($resultVBS)) { $vbsid = getData($resultVBS)[0]->vertragsbestandteil_id; + $vbsbis = getData($resultVBS)[0]->bis; } else { - echo "Vertragsbestandteil wurde nicht gefunden -> ROLLBACK"; + echo "Vertragsbestandteil fuer $uid DV $dvid wurde nicht gefunden mit Beginn ".$row_gehalt['beginn']."-> ROLLBACK"; $failed = true; break; } @@ -246,7 +268,7 @@ class MigrateSalary extends CLI_Controller ); if (isset($row_gehalt['ende']) && $row_gehalt['ende']!='') $data['bis'] = $row_gehalt['ende']; - + $resultVBS = $this->VertragsbestandteilModel->Insert($data); if(!isSuccess($resultVBS)) { @@ -286,7 +308,7 @@ class MigrateSalary extends CLI_Controller ); if (isset($row_gehalt['ende']) && $row_gehalt['ende']!='') $data['bis'] = $row_gehalt['ende']; - + $resultVBS = $this->VertragsbestandteilModel->Insert($data); if(!isSuccess($resultVBS)) { @@ -356,16 +378,24 @@ class MigrateSalary extends CLI_Controller $date->modify('last day of this month'); $last_day_this_month = $date->format('Y-m-d'); - // TODO: wenn das Dienstverhaeltnis in diesem Monat endet und nicht der Monatsletzte ist, + // Wenn das Dienstverhaeltnis in diesem Monat endet und nicht der Monatsletzte ist, // dann muss hier das Ende Datum des DV stehen bzw das Ende // oder das Ende des VBS falls die Person in der Monatsmitte Stunden wechselt $data['bis'] = $last_day_this_month; + + // Wenn der Vertragsbestandteil endet bevor das Gehalt endet, dann wir das Gehaltsende auf VBS Ende gesetzt + //echo "Ende des VBS: $vbsbis Ende des Gehalt: ".$data['bis']; + if ($vbsbis != '' && $vbsbis < $data['bis']) + { + $data['bis'] = $vbsbis; + //echo "Gehalt auf vbs ende gesetzt"; + } } $ret = $this->GehaltsbestandteilModel->insert($data, $this->GehaltsbestandteilModel->getEncryptedColumns() ); - } + } if(!$failed) { @@ -375,7 +405,7 @@ class MigrateSalary extends CLI_Controller { echo "ROLLBACK"; $this->db->trans_rollback(); - } + } } /** @@ -386,17 +416,17 @@ class MigrateSalary extends CLI_Controller $db = new DB_Model(); $qry = " - SELECT - * - FROM - hr.tbl_vertragsbestandteil + SELECT + * + FROM + hr.tbl_vertragsbestandteil JOIN hr.tbl_vertragsbestandteil_freitext USING(vertragsbestandteil_id) - WHERE - dienstverhaeltnis_id=".$db->escape($dvid)." - AND vertragsbestandteiltyp_kurzbz='freitext' + WHERE + dienstverhaeltnis_id=".$db->escape($dvid)." + AND vertragsbestandteiltyp_kurzbz='freitext' AND ".$db->escape($datum)." BETWEEN von AND COALESCE(bis, '2999-12-31') AND freitexttyp_kurzbz='allin'"; - + $resultAllIn = $db->execReadOnlyQuery($qry); if (hasData($resultAllIn)) @@ -410,15 +440,15 @@ class MigrateSalary extends CLI_Controller $db = new DB_Model(); $qry = " - SELECT - * - FROM - hr.tbl_vertragsbestandteil - WHERE - dienstverhaeltnis_id=".$db->escape($dvid)." - AND vertragsbestandteiltyp_kurzbz='stunden' + SELECT + * + FROM + hr.tbl_vertragsbestandteil + WHERE + dienstverhaeltnis_id=".$db->escape($dvid)." + AND vertragsbestandteiltyp_kurzbz='stunden' AND ".$db->escape($datum)." BETWEEN von AND COALESCE(bis, '2999-12-31')"; - + $resultVBS = $db->execReadOnlyQuery($qry); return $resultVBS; @@ -430,22 +460,22 @@ class MigrateSalary extends CLI_Controller private function _getUser($svnr) { $db = new DB_Model(); - + $qry = " - SELECT - mitarbeiter_uid - FROM - public.tbl_person + SELECT + mitarbeiter_uid + FROM + public.tbl_person JOIN public.tbl_benutzer using(person_id) JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid) WHERE tbl_person.svnr = ". $db->escape($svnr)." AND EXISTS( - SELECT - 1 - FROM - hr.tbl_dienstverhaeltnis - WHERE + SELECT + 1 + FROM + hr.tbl_dienstverhaeltnis + WHERE mitarbeiter_uid=tbl_mitarbeiter.mitarbeiter_uid AND oe_kurzbz=". $db->escape($this->OE_DEFAULT)." ) diff --git a/application/controllers/system/Navigation.php b/application/controllers/system/Navigation.php index c3764b612..71ab1c81b 100755 --- a/application/controllers/system/Navigation.php +++ b/application/controllers/system/Navigation.php @@ -22,6 +22,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); * This controller operates between (interface) the JS (GUI) and the NavigationLib (back-end) * Provides data to the ajax get calls about the filter * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + * TODO(chris): deprecated */ class Navigation extends FHC_Controller { diff --git a/application/core/Auth_Controller.php b/application/core/Auth_Controller.php index c407a106f..d170a7eca 100755 --- a/application/core/Auth_Controller.php +++ b/application/core/Auth_Controller.php @@ -7,6 +7,10 @@ if (!defined('BASEPATH')) exit('No direct script access allowed'); */ abstract class Auth_Controller extends FHC_Controller { + // Special Permissions + const PERM_ANONYMOUS = 'anonymous'; // Everyone + const PERM_LOGGED = 'logged_in'; // Every registered user + /** * Extends this controller if authentication is required */ @@ -14,17 +18,41 @@ abstract class Auth_Controller extends FHC_Controller { parent::__construct(); - // Loads authentication library and starts authentication - $this->load->library('AuthLib'); + if (!is_array($requiredPermissions) || isEmptyArray($requiredPermissions)) + show_error('The given permissions is not a valid array or it is an empty one'); + + if (!isset($requiredPermissions[$this->router->method])) + show_error('The given permission array does not contain the given method or is not correctly set'); + + $anonAllowed = false; + if ($requiredPermissions[$this->router->method] == self::PERM_ANONYMOUS) + $anonAllowed = true; + elseif (is_array($requiredPermissions[$this->router->method]) + && in_array(self::PERM_ANONYMOUS, $requiredPermissions[$this->router->method])) + $anonAllowed = true; - // Checks if the caller is allowed to access to this content - $this->_isAllowed($requiredPermissions); + if ($anonAllowed) { + // Loads authentication library without authentication + $this->load->library('AuthLib', [false]); + + // Loads helper since it would only be called on authentication + $this->load->helper('hlp_authentication'); + } else { + // Loads authentication library and starts authentication + $this->load->library('AuthLib'); + + // Checks if the caller is allowed to access to this content + $this->_isAllowed($requiredPermissions); + } } /** * Checks if the caller is allowed to access to this content with the given permissions * If it is not allowed will set the HTTP header with code 401 * Wrapper for permissionlib->isEntitled + * + * @param array $requiredPermissions + * @return void */ private function _isAllowed($requiredPermissions) { @@ -34,28 +62,43 @@ abstract class Auth_Controller extends FHC_Controller // Checks if this user is entitled to access to this content if (!$this->permissionlib->isEntitled($requiredPermissions, $this->router->method)) { - $this->output->set_status_header(REST_Controller::HTTP_UNAUTHORIZED); // set the HTTP header as unauthorized - - $this->load->library('EPrintfLib'); // loads the EPrintfLib to format the output - - // Prints the main error message - $this->eprintflib->printError('You are not allowed to access to this content'); - // Prints the called controller name - $this->eprintflib->printInfo('Controller name: '.$this->router->class); - // Prints the called controller method name - $this->eprintflib->printInfo('Method name: '.$this->router->method); - // Prints the required permissions needed to access to this method - $this->eprintflib->printInfo('Required permissions: '.$this->_rpsToString($requiredPermissions, $this->router->method)); - + $this->_outputAuthError($requiredPermissions); exit; // immediately terminate the execution } } + /** + * Outputs an error message and sets the HTTP Header. + * This function is protected so that it can be overwritten. + * + * @param array $requiredPermissions + * @return void + */ + protected function _outputAuthError($requiredPermissions) + { + $this->output->set_status_header(REST_Controller::HTTP_UNAUTHORIZED); // set the HTTP header as unauthorized + + $this->load->library('EPrintfLib'); // loads the EPrintfLib to format the output + + // Prints the main error message + $this->eprintflib->printError('You are not allowed to access to this content'); + // Prints the called controller name + $this->eprintflib->printInfo('Controller name: '.$this->router->class); + // Prints the called controller method name + $this->eprintflib->printInfo('Method name: '.$this->router->method); + // Prints the required permissions needed to access to this method + $this->eprintflib->printInfo('Required permissions: '.$this->_rpsToString($requiredPermissions, $this->router->method)); + } + /** * Converts an array of permissions to a string that contains them as a comma separated list * Ex: ", , " + * + * @param array $requiredPermissions + * @param string $method + * @return void */ - private function _rpsToString($requiredPermissions, $method) + final protected function _rpsToString($requiredPermissions, $method) { $strRequiredPermissions = ''; // string that contains all the required permissions needed to access to this method diff --git a/application/core/FHCAPI_Controller.php b/application/core/FHCAPI_Controller.php new file mode 100644 index 000000000..647032795 --- /dev/null +++ b/application/core/FHCAPI_Controller.php @@ -0,0 +1,231 @@ +returnObj['meta']) || !isset($this->returnObj['meta']['status'])) { + switch ($http_response_code) { + case 200: + $this->setStatus(self::STATUS_SUCCESS); + break; + case 400: + $this->setStatus(self::STATUS_FAIL); + break; + default: + $this->setStatus(self::STATUS_ERROR); + break; + } + } + + return json_encode($this->returnObj); + }); + + // NOTE(chris): overwrite error_views_path before constructor + load_class('Config')->set_item('error_views_path', VIEWPATH.'errors'.DIRECTORY_SEPARATOR.'json'.DIRECTORY_SEPARATOR); + + parent::__construct($requiredPermissions); + + // For JSON Requests (as opposed to multipart/form-data) get the $_POST variable from the input stream instead + if ($this->input->get_request_header('Content-Type', true) == 'application/json') + $_POST = json_decode($this->security->xss_clean($this->input->raw_input_stream), true); + elseif (isset($_POST['_jsondata'])) { + $_POST = array_merge($_POST, json_decode($_POST['_jsondata'], true)); + unset($_POST['_jsondata']); + } + } + + + // --------------------------------------------------------------- + // Handle Output object + // --------------------------------------------------------------- + + /** + * @param array $data + * @param string $type (optional) + * @return void + */ + public function addError($data, $type = null) + { + if (!isset($this->returnObj['errors'])) + $this->returnObj['errors'] = []; + + $error = []; + + if (is_array($data)) { + if ($type == self::ERROR_TYPE_VALIDATION) + $error['messages'] = $data; + else + $error = $data; + } else { + $error['message'] = $data; + } + + if ($type) + $error['type'] = $type; + + $this->returnObj['errors'][] = $error; + } + + /** + * @param mixed $data + * @return void + */ + public function setData($data) + { + $this->returnObj['data'] = $data; + } + + /** + * @param string $key + * @param mixed $value + * @return void + */ + public function addMeta($key, $value) + { + if (!isset($this->returnObj['meta'])) + $this->returnObj['meta'] = []; + $this->returnObj['meta'][$key] = $value; + } + + /** + * @param string $status + * @return void + */ + public function setStatus($status) + { + $this->addMeta('status', $status); + } + + + // --------------------------------------------------------------- + // Handle Output object - Shortcut functions + // --------------------------------------------------------------- + + /** + * @param mixed $data (optional) + * @return void + */ + protected function terminateWithSuccess($data = null) + { + $this->setData($data); + $this->setStatus(self::STATUS_SUCCESS); + exit; + } + + /** + * @param array $errors + * @return void + */ + protected function terminateWithValidationErrors($errors) + { + $this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST); + $this->addError($errors, self::ERROR_TYPE_VALIDATION); + $this->setStatus(self::STATUS_FAIL); + exit(EXIT_ERROR); + } + + /** + * @param array $error + * @param string $type (optional) + * @param integer $status (optional) + * @return void + */ + protected function terminateWithError($error, $type = null, $status = REST_Controller::HTTP_INTERNAL_SERVER_ERROR) + { + $this->output->set_status_header($status); + $this->addError($error, $type); + $this->setStatus(self::STATUS_ERROR); + exit; + } + + /** + * @param stdclass $result + * @param string $errortype + * @return void + */ + protected function getDataOrTerminateWithError($result, $errortype = self::ERROR_TYPE_GENERAL) + { + if (isError($result)) { + $this->terminateWithError(getError($result), $errortype); + } + return $result->retval; + } + + + // --------------------------------------------------------------- + // Security + // --------------------------------------------------------------- + + /** + * Outputs an error message and sets the HTTP Header. + * This overwrites the default behaviour to output a json object. + * + * @param array $requiredPermissions + * @return void + */ + protected function _outputAuthError($requiredPermissions) + { + $this->output->set_status_header(isLogged() ? REST_Controller::HTTP_FORBIDDEN : REST_Controller::HTTP_UNAUTHORIZED); + + $this->addError([ + 'message' => 'You are not allowed to access to this content', + 'controller' => $this->router->class, + 'method' => $this->router->method, + 'required_permissions' => $this->_rpsToString($requiredPermissions, $this->router->method) + ], self::ERROR_TYPE_AUTH); + } +} diff --git a/application/libraries/AntragLib.php b/application/libraries/AntragLib.php index 298c3652f..ce4485279 100755 --- a/application/libraries/AntragLib.php +++ b/application/libraries/AntragLib.php @@ -62,9 +62,11 @@ class AntragLib 'insertvon' => $insertvon ]); - // NOTE(chris): remove "preabbrecher" statusgrund for Stgl-Abmeldungen if set + // NOTE(chris): remove "preabbrecher" statusgrund and paused stati for sibling Anträge for Stgl-Abmeldungen if set $res = $this->_ci->StudierendenantragModel->load($antrag_id); if (hasData($res) && current(getData($res))->typ == Studierendenantrag_model::TYP_ABMELDUNG_STGL) { + $this->unpauseAntrag($antrag_id, Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL); + $this->_ci->PrestudentstatusModel->addSelect('tbl_status_grund.statusgrund_kurzbz'); $res = $this->_ci->PrestudentstatusModel->getLastStatusWithStgEmail(current(getData($res))->prestudent_id, '', 'Student'); if (hasData($res) && current(getData($res))->statusgrund_kurzbz == 'preabbrecher') { @@ -83,6 +85,67 @@ class AntragLib return $result; } + /** + * @param integer $antrag_id + * @param string $insertvon + * + * @return stdClass + */ + public function pauseAntrag($antrag_id, $insertvon) + { + switch ($insertvon) { + case Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL: + $result = $this->_ci->StudierendenantragstatusModel->stopAntraegeForAbmeldungStgl($antrag_id); + break; + case Studierendenantragstatus_model::INSERTVON_DEREGISTERED: + $result = $this->_ci->StudierendenantragstatusModel->stopAntraegeForAbbruchBy($antrag_id); + break; + default: + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_PAUSE, + 'insertvon' => $insertvon + ]); + break; + } + + return $result; + } + + /** + * @param integer $antrag_id + * @param string $insertvon + * + * @return stdClass + */ + public function unpauseAntrag($antrag_id, $insertvon) + { + if ($insertvon == Studierendenantragstatus_model::INSERTVON_DEREGISTERED) + return error($this->p->t('studierendenantrag', 'error_no_right')); + if ($insertvon == Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL) { + return $this->_ci->StudierendenantragstatusModel->resumeAntraegeForAbmeldungStgl($antrag_id); + } + // NOTE(chris): get last status that is not pause + $this->_ci->StudierendenantragstatusModel->addOrder('insertamum'); + $this->_ci->StudierendenantragstatusModel->addLimit(1); + $result = $this->_ci->StudierendenantragstatusModel->loadWhere([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz !=' => Studierendenantragstatus_model::STATUS_PAUSE + ]); + if (isError($result)) + return $result; + if (!hasData($result)) + return error($this->_ci->p->t('studierendenantrag', 'error_no_antragstatus', ['id' => $antrag_id])); + $status = current(getData($result)); + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => $status->studierendenantrag_statustyp_kurzbz, + 'insertvon' => $insertvon + ]); + return $result; + } + /** * NOTE(chris): permissions & verification must be handled outside * @@ -169,7 +232,7 @@ class AntragLib if (isError($result)) $errors[] = getError($result); else { - $this->_ci->StudiengangModel->addJoin('public.tbl_prestudent ps','studiengang_kz'); + $this->_ci->StudiengangModel->addJoin('public.tbl_prestudent ps', 'studiengang_kz'); $result = $this->_ci->StudiengangModel->loadWhere(['prestudent_id' => $antrag->prestudent_id]); $stg = ''; $orgform = ''; @@ -190,6 +253,10 @@ class AntragLib $vorlage ='Sancho_Mail_Antrag_A_Approve'; $subject = $this->_ci->p->t('studierendenantrag', 'mail_subject_A_Approve'); + $result = $this->pauseAntrag($studierendenantrag_id, Studierendenantragstatus_model::INSERTVON_DEREGISTERED); + if (isError($result)) + $errors[] = getError($result); + $result = $this->_ci->prestudentlib->setAbbrecher( $antrag->prestudent_id, $antrag->studiensemester_kurzbz, @@ -208,7 +275,13 @@ class AntragLib $data = [ 'student' => $this->_ci->p->t('person', 'studentIn'), 'sem' => $antrag->studiensemester_kurzbz, - 'linkPdf' => base_url('content/pdfExport.php?xml=Antrag' . $antrag->typ . '.xml.php&xsl=Antrag' . $antrag->typ . '&id=' . $antrag->studierendenantrag_id . '&output=pdf') + 'linkPdf' => base_url('content/pdfExport.php?xml=Antrag' . + $antrag->typ . + '.xml.php&xsl=Antrag' . + $antrag->typ . + '&id=' . + $antrag->studierendenantrag_id . + '&output=pdf') ]; if (hasData($result)) { $person = current(getData($result)); @@ -229,6 +302,10 @@ class AntragLib sendSanchoMail($vorlage, $data, $prestudent_status->email, $subject); } } else { // ($antrag->typ == Studierendenantrag_model::TYP_ABMELDUNG_STGL) + $result = $this->pauseAntrag($studierendenantrag_id, Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL); + if (isError($result)) + $errors[] = getError($result); + $result = $this->_ci->PrestudentstatusModel->getLastStatusWithStgEmail($antrag->prestudent_id, '', 'Student'); if (isError($result)) { @@ -340,6 +417,10 @@ class AntragLib if (isError($result)) return $result; else { + $result = $this->pauseAntrag($studierendenantrag_id, Studierendenantragstatus_model::INSERTVON_DEREGISTERED); + // NOTE(chris): here we should have error handling but at the + // moment there is no way to notify the user for "soft" errors + $result = $this->_ci->prestudentlib->setAbbrecher( $antrag->prestudent_id, $antrag->studiensemester_kurzbz, @@ -471,7 +552,6 @@ class AntragLib '
Details:
' . $error_msg; } else { - $data = getData($data); $result = $this->_ci->StudierendenantragstatusModel->insert([ @@ -582,7 +662,11 @@ class AntragLib 'nachname' => $data['person']->nachname, 'UID' => $data['UID'], 'sem' => $resultAntrag->studiensemester_kurzbz, - 'linkPdf' => base_url('content/pdfExport.php?xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&id=' . $studierendenantrag_id . '&output=pdf'), + 'linkPdf' => base_url( + 'content/pdfExport.php?xml=AntragUnterbrechung.xml.php&xsl=AntragUnterbrechung&id=' . + $studierendenantrag_id . + '&output=pdf' + ), 'insertvon' => $approvedBy ], $data['prestudent_status']->email, @@ -699,7 +783,9 @@ class AntragLib 'Orgform' => $data['prestudent_status']->orgform_kurzbz, 'prestudent_id' => $data['prestudent_status']->prestudent_id, 'abmeldungLink' => site_url('lehre/Studierendenantrag/abmeldung/' . $data['prestudent_status']->prestudent_id), - 'abmeldungLinkCIS' => CIS_ROOT . 'index.ci.php/lehre/Studierendenantrag/abmeldung/' . $data['prestudent_status']->prestudent_id + 'abmeldungLinkCIS' => CIS_ROOT . + 'index.ci.php/lehre/Studierendenantrag/abmeldung/' . + $data['prestudent_status']->prestudent_id ], $data['email'], $this->_ci->p->t('studierendenantrag', 'mail_subject_U_Reject') @@ -734,7 +820,7 @@ class AntragLib return error($this->_ci->p->t('studierendenantrag', 'error_no_antrag_found', ['id' => $studierendenantrag_id])); $result['antrag'] = $antrag = current($res); - $this->_ci->StudiengangModel->addJoin('public.tbl_prestudent ps','studiengang_kz'); + $this->_ci->StudiengangModel->addJoin('public.tbl_prestudent ps', 'studiengang_kz'); $res = $this->_ci->StudiengangModel->loadWhere(['prestudent_id' => $antrag->prestudent_id]); if (hasData($res)) { $result['studiengang'] = current(getData($res)); @@ -862,7 +948,9 @@ class AntragLib $result = $this->_ci->StudierendenantragstatusModel->insert([ 'studierendenantrag_id' => $antrag_id, - 'studierendenantrag_statustyp_kurzbz' => $repeat ? Studierendenantragstatus_model::STATUS_CREATED : Studierendenantragstatus_model::STATUS_PASS, + 'studierendenantrag_statustyp_kurzbz' => $repeat + ? Studierendenantragstatus_model::STATUS_CREATED + : Studierendenantragstatus_model::STATUS_PASS, 'insertvon' => $insertvon ]); @@ -878,8 +966,7 @@ class AntragLib $email = $prestudent_status->email; // NOTE(chris): Sancho mail $lvzuweisungLink = site_url('lehre/Antrag/Wiederholung/assistenz/' . $antrag_id); - if( defined('VILESCI_ROOT') ) - { + if (defined('VILESCI_ROOT')) { $lvzuweisungLink = VILESCI_ROOT . 'index.ci.php/lehre/Antrag/Wiederholung/assistenz/' . $antrag_id; } sendSanchoMail( @@ -888,7 +975,7 @@ class AntragLib 'antrag_id' => $antrag_id, 'stg' => $prestudent_status->stg_bezeichnung, 'Orgform' => $prestudent_status->orgform, - 'lvzuweisungLink' => $lvzuweisungLink + 'lvzuweisungLink' => $lvzuweisungLink ], $email, $this->_ci->p->t('studierendenantrag', 'mail_subject_W_New') @@ -1062,7 +1149,11 @@ class AntragLib if (isError($result)) return $result; if (!hasData($result)) - return error($this->_ci->p->t('studierendenantrag', 'error_no_stdsem', ['studiensemester_kurzbz' => $antrag->studiensemester_kurzbz])); + return error($this->_ci->p->t( + 'studierendenantrag', + 'error_no_stdsem', + ['studiensemester_kurzbz' => $antrag->studiensemester_kurzbz] + )); $asem = current(getData($result)); foreach ($stdsems as $sem) { @@ -1117,7 +1208,6 @@ class AntragLib $lv->antrag_anmerkung = $lvszugewiesen[$lv->lehrveranstaltung_id]->anmerkung; $lv->antrag_zugelassen = true; } - } } else { $lvsA = null; @@ -1224,10 +1314,10 @@ class AntragLib * @param integer $prestudent_id * * @return \stdClass on success retval 0 means not a student; - * retval 1 means Berechtigt; - * retval -1 means has already an Antrag pending; - * retval -2 means other Antrag pending; - * retval -3 means in blacklist stg + * retval 1 means Berechtigt; + * retval -1 means has already an Antrag pending; + * retval -2 means other Antrag pending; + * retval -3 means in blacklist stg */ public function getPrestudentAbmeldeBerechtigt($prestudent_id) { @@ -1251,12 +1341,24 @@ class AntragLib if (!in_array($result->status_kurzbz, $this->_ci->config->item('antrag_prestudentstatus_whitelist_abmeldung'))) { $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere([ - 'prestudent_id' => $prestudent_id, - 'campus.get_status_studierendenantrag(studierendenantrag_id)' => Studierendenantragstatus_model::STATUS_APPROVED - ], [ - Studierendenantrag_model::TYP_ABMELDUNG, - Studierendenantrag_model::TYP_ABMELDUNG_STGL - ]); + 'prestudent_id' => $prestudent_id, + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED + ], [ + Studierendenantrag_model::TYP_ABMELDUNG, + Studierendenantrag_model::TYP_ABMELDUNG_STGL + ]); + if (isError($result)) + return $result; + if (hasData($result)) + return success(-1); + + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere([ + 'prestudent_id' => $prestudent_id, + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_PAUSE + ], [ + Studierendenantrag_model::TYP_ABMELDUNG, + Studierendenantrag_model::TYP_ABMELDUNG_STGL + ]); if (isError($result)) return $result; if (hasData($result)) @@ -1297,12 +1399,12 @@ class AntragLib * @param string $studiensemester_kurzbz (optional) * * @return \stdClass on success retval 0 means not a student; - * retval 1 means Berechtigt; + * retval 1 means Berechtigt; * retval -1 means has already an Antrag pending; * retval -2 means other Antrag pending; * retval -3 means in blacklist stg */ - public function getPrestudentUnterbrechungsBerechtigt($prestudent_id, $studiensemester_kurzbz = null) + public function getPrestudentUnterbrechungsBerechtigt($prestudent_id, $studiensemester_kurzbz = null, $datum_wiedereinstieg = null) { $result = $this->_ci->PrestudentModel->load($prestudent_id); if (isError($result)) @@ -1320,18 +1422,10 @@ class AntragLib if (!hasData($result)) return success(0); $result = current(getData($result)); + $prestudent_stdsem = $result->studiensemester_kurzbz; $datumStatus = $result->datum; - if (!in_array($result->status_kurzbz, $this->_ci->config->item('antrag_prestudentstatus_whitelist'))) { - $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere([ - 'prestudent_id' => $prestudent_id, - 'typ' => Studierendenantrag_model::TYP_UNTERBRECHUNG, - 'campus.get_status_studierendenantrag(studierendenantrag_id)' => Studierendenantragstatus_model::STATUS_APPROVED - ]); - if (isError($result)) - return $result; - if (hasData($result)) - return success(-1); - + if (!in_array($result->status_kurzbz, $this->_ci->config->item('antrag_prestudentstatus_whitelist')) + && $result->status_kurzbz != 'Unterbrecher') { return success(0); } $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id]); @@ -1339,7 +1433,8 @@ class AntragLib return $result; if (!hasData($result)) return success(1); - $result= getData($result); + + $result = getData($result); foreach ($result as $antrag) { if ($antrag->typ == Studierendenantrag_model::TYP_ABMELDUNG || $antrag->typ == Studierendenantrag_model::TYP_ABMELDUNG_STGL) @@ -1349,11 +1444,11 @@ class AntragLib elseif($antrag->status == Studierendenantragstatus_model::STATUS_APPROVED && $antrag->datum > $datumStatus) return success(-2); } - if ($studiensemester_kurzbz && $antrag->typ == Studierendenantrag_model::TYP_UNTERBRECHUNG) + if ($antrag->typ == Studierendenantrag_model::TYP_UNTERBRECHUNG) { - // NOTE(chris): check if this is an old or canceled one - if ($antrag->studiensemester_kurzbz == $studiensemester_kurzbz && $antrag->status != Studierendenantragstatus_model::STATUS_CANCELLED) - return success(-1); + // NOTE(chris): Ignore canceled ones + if ($antrag->status == Studierendenantragstatus_model::STATUS_CANCELLED) + continue; } if ($antrag->typ == Studierendenantrag_model::TYP_WIEDERHOLUNG) { @@ -1362,6 +1457,17 @@ class AntragLib } } + if (!$studiensemester_kurzbz) { + $sems = $this->getSemesterForUnterbrechung($prestudent_id, $prestudent_stdsem); + if (!count(array_filter($sems, function ($item) { + return !$item['disabled']; + }))) + return success(-1); + } else { + if ($this->_ci->StudierendenantragModel->hasRunningUnterbrechungBetween($prestudent_id, $studiensemester_kurzbz, $datum_wiedereinstieg)) + return success(-1); + } + return success(1); } @@ -1406,7 +1512,27 @@ class AntragLib $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere([ 'prestudent_id' => $prestudent_id, 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, - 'campus.get_status_studierendenantrag(studierendenantrag_id)' => Studierendenantragstatus_model::STATUS_APPROVED + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED + ]); + if (isError($result)) + return $result; + if (hasData($result)) + return success(-1); + + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere([ + 'prestudent_id' => $prestudent_id, + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_DEREGISTERED + ]); + if (isError($result)) + return $result; + if (hasData($result)) + return success(-1); + + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere([ + 'prestudent_id' => $prestudent_id, + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_PAUSE ]); if (isError($result)) return $result; @@ -1457,15 +1583,16 @@ class AntragLib return success($result); } + /** + * Gets details for the latest Antrag of one or more types + * + * @param integer $prestudent_id + * @param array|string $typ + * + * @return \stdClass + */ public function getDetailsForLastAntrag($prestudent_id, $typ = null) { - $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($prestudent_id); - if (isError($result)) - return $result; - if (!hasData($result)) - return error($this->_ci->p->t('studierendenantrag', 'error_no_prestudentstatus', ['prestudent_id' => $prestudent_id])); - $resultDetails = current(getData($result)); - $where = [ 'prestudent_id' => $prestudent_id ]; @@ -1494,21 +1621,20 @@ class AntragLib 'prestudent_id' => $prestudent_id ])); - $resultDetails->status = $resultAntrag->status; - $resultDetails->statustyp = $resultAntrag->statustyp; - $resultDetails->grund = $resultAntrag->grund; - $resultDetails->studierendenantrag_id = $resultAntrag->studierendenantrag_id; - $resultDetails->typ = $resultAntrag->typ; - $resultDetails->datum = $resultAntrag->datum; - $resultDetails->studiensemester_kurzbz = $resultAntrag->studiensemester_kurzbz; - - return success($resultDetails); + return $this->addDetailsToAntrag($resultAntrag); } + /** + * Gets details for a specific Antrag + * + * @param integer $studierendenantrag_id + * + * @return \stdClass + */ public function getDetailsForAntrag($studierendenantrag_id) { $where = [ - 'studierendenantrag_id' => $studierendenantrag_id + 's.studierendenantrag_id' => $studierendenantrag_id ]; $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere($where); @@ -1519,76 +1645,99 @@ class AntragLib return error($this->_ci->p->t('studierendenantrag', "error_no_antrag_found", ['id' => $studierendenantrag_id])); $resultAntrag = current(getData($result)); - $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($resultAntrag->prestudent_id, $resultAntrag->studiensemester_kurzbz); + return $this->addDetailsToAntrag($resultAntrag); + } + + /** + * Helper function for getDetailsForAntrag and getDetailsForLastAntrag + * + * @param \stdClass $antrag + * + * @return \stdClass + */ + protected function addDetailsToAntrag($antrag) + { + $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails( + $antrag->prestudent_id, + $antrag->studiensemester_kurzbz, + $antrag->insertamum + ); if (isError($result)) return $result; if (!hasData($result)) { - $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($resultAntrag->prestudent_id); + $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails( + $antrag->prestudent_id, + null, + $antrag->insertamum + ); if (isError($result)) return $result; if (!hasData($result)) - return error($this->_ci->p->t('studierendenantrag', 'error_no_prestudentstatus', $resultAntrag)); + return error($this->_ci->p->t('studierendenantrag', 'error_no_prestudent_in_sem', $antrag)); + $tmp = current(getData($result)); + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + $res = $this->_ci->StudiensemesterModel->load($antrag->studiensemester_kurzbz); + if (hasData($res)) + $tmp->studienjahr_kurzbz = current(getData($res))->studienjahr_kurzbz; + else + $tmp->studienjahr_kurzbz = ''; + // NOTE(chris): the semester might not be correct on this fallback so we disable it + $tmp->semester = ''; } - $resultDetails = current(getData($result)); - $resultDetails->status = $resultAntrag->status; - $resultDetails->statustyp = $resultAntrag->statustyp; - $resultDetails->grund = $resultAntrag->grund; - $resultDetails->studierendenantrag_id = $resultAntrag->studierendenantrag_id; - $resultDetails->typ = $resultAntrag->typ; - $resultDetails->dms_id = $resultAntrag->dms_id; - $resultDetails->datum_wiedereinstieg = $resultAntrag->datum_wiedereinstieg; + $result = current(getData($result)); - return success($resultDetails); + $result->status = $antrag->status; + $result->statustyp = $antrag->statustyp; + $result->status_insertvon = $antrag->status_insertvon; + $result->grund = $antrag->grund; + $result->studierendenantrag_id = $antrag->studierendenantrag_id; + $result->typ = $antrag->typ; + $result->datum = $antrag->datum; + $result->dms_id = $antrag->dms_id; + $result->datum_wiedereinstieg = $antrag->datum_wiedereinstieg; + + return success($result); } - public function getSemesterForUnterbrechung($studiensemester_kurzbz) + /** + * Rearrange the free semester slots for a new Unterbrechung + * + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz + * + * @return array + */ + public function getSemesterForUnterbrechung($prestudent_id, $studiensemester_kurzbz) { - $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); - - $semester = []; - - $result = $this->_ci->StudiensemesterModel->getNextFrom($studiensemester_kurzbz); - if (!hasData($result)) - return $semester; - $nextSem = current(getData($result)); - - $semester[0] = [ - 'studiensemester_kurzbz' => $studiensemester_kurzbz, - 'wiedereinstieg' => [$nextSem] - ]; - - $result = $this->_ci->StudiensemesterModel->getNextFrom($nextSem->studiensemester_kurzbz); - if (!hasData($result)) - return $semester; - - $currSemester = current(getData($result)); - $followingSemester = [$currSemester]; - - $max = $this->_ci->config->item('unterbrecher_semester_max_length'); - if(!$max || $max < 1) - $max = 2; - - for ($i = 1; $i < $max; $i++) { - $result = $this->_ci->StudiensemesterModel->getNextFrom($currSemester->studiensemester_kurzbz); - if (!hasData($result)) - break; - $currSemester = current(getData($result)); - $followingSemester[] = $currSemester; - } - - $semester[1] = [ - 'studiensemester_kurzbz' => $nextSem->studiensemester_kurzbz, - 'wiedereinstieg' => $followingSemester - ]; - - //remove last Semester of the array - array_pop($followingSemester); - - foreach ($followingSemester as $sem) - $semester[0]['wiedereinstieg'][] = $sem; - - return $semester; + $result = $this->_ci->StudierendenantragModel->getFreeSlotsForUnterbrechung($prestudent_id, $studiensemester_kurzbz); + if (isError($result)) + return []; + $result = getData($result); + if (!$result) + return []; + return array_reduce($result, function ($carry, $item) { + if (!isset($carry[$item->von])) + $carry[$item->von] = [ + 'studienjahr_kurzbz' => $item->studienjahr_kurzbz, + 'studiensemester_kurzbz' => $item->von, + 'wiedereinstieg' => [], + 'disabled' => true + ]; + + $carry[$item->von]['wiedereinstieg'][] = [ + 'studiensemester_kurzbz' => $item->bis, + 'start' => $item->ende, + 'disabled' => (boolean)$item->studierendenantrag_id + ]; + + if ($carry[$item->von]['disabled'] && !$item->studierendenantrag_id) { + $carry[$item->von]['disabled'] = false; + } + + return $carry; + }, []); + return $result; } public function getAktivePrestudentenInStgs($studiengaenge, $query) @@ -1664,7 +1813,6 @@ class AntragLib return error($this->_ci->p->t('studierendenantrag', 'error_no_stg_antrag', ['id' => $antrag_id])); $stg = current($result); - $studiengang_kz = $stg->studiengang_kz; $semester = $stg->ausbildungssemester; $result = $this->_ci->StudierendenantragModel->load($antrag_id); @@ -1726,9 +1874,7 @@ class AntragLib $result = $this->getLvsForAntrag($antrag_id); if (hasData($result)) { $lvs = getData($result); - $repeat_last = false; if (isset($lvs['repeat_last'])) { - $repeat_last = true; unset($lvs['repeat_last']); $vorlage .= '_Lst'; } @@ -1895,6 +2041,26 @@ class AntragLib return $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag'); } + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToPauseAntrag($antrag_id) + { + return ($this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe') || $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag')); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToUnpauseAntrag($antrag_id) + { + return ($this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe') || $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag')); + } + /** * @param integer $antrag_id * @@ -1935,6 +2101,36 @@ class AntragLib return $this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe'); } + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function antragCanBeManualPaused($antrag_id) + { + $this->_ci->StudierendenantragModel->db->where_not_in('campus.get_status_studierendenantrag(studierendenantrag_id)', [ + Studierendenantragstatus_model::STATUS_DEREGISTERED, + Studierendenantragstatus_model::STATUS_APPROVED, + Studierendenantragstatus_model::STATUS_PAUSE + ]); + $result = $this->_ci->StudierendenantragModel->loadWhere([ + 'studierendenantrag_id' => $antrag_id, + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG + ]); + + return hasData($result); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function antragCanBeManualUnpaused($antrag_id) + { + return $this->_ci->StudierendenantragModel->isManuallyPaused($antrag_id); + } + /** * @param integer $antrag_id * @param string|array $status diff --git a/application/libraries/PermissionLib.php b/application/libraries/PermissionLib.php index 09f89abee..bf8174cf4 100755 --- a/application/libraries/PermissionLib.php +++ b/application/libraries/PermissionLib.php @@ -21,6 +21,8 @@ require_once(FHCPATH.'include/functions.inc.php'); require_once(FHCPATH.'include/wawi_kostenstelle.class.php'); require_once(FHCPATH.'include/benutzerberechtigung.class.php'); +use \benutzerberechtigung as benutzerberechtigung; + class PermissionLib { // Available rights in the DB @@ -65,8 +67,10 @@ class PermissionLib if (!is_cli()) { // API Caller rights initialization + $authObj = $this->_ci->authlib->getAuthObj(); self::$bb = new benutzerberechtigung(); - self::$bb->getBerechtigungen(($this->_ci->authlib->getAuthObj())->{AuthLib::AO_USERNAME}); + if ($authObj) + self::$bb->getBerechtigungen($authObj->{AuthLib::AO_USERNAME}); } } @@ -166,6 +170,16 @@ class PermissionLib if ($checkPermissions === true) break; } } + elseif ($permissions[$pCounter] == Auth_Controller::PERM_ANONYMOUS) + { + $checkPermissions = true; + break; + } + elseif ($permissions[$pCounter] == Auth_Controller::PERM_LOGGED) + { + $checkPermissions = isLogged(); + break; + } else { show_error('The given permission does not use the correct format'); diff --git a/application/libraries/vertragsbestandteil/Dienstverhaeltnis.php b/application/libraries/vertragsbestandteil/Dienstverhaeltnis.php index 5745c2196..309d3dfdc 100644 --- a/application/libraries/vertragsbestandteil/Dienstverhaeltnis.php +++ b/application/libraries/vertragsbestandteil/Dienstverhaeltnis.php @@ -247,7 +247,7 @@ EOTXT; $this->validationerrors[] = 'Das Beginndatum muss vor dem Endedatum liegen.'; } - if( $this->checkoverlap && !($this->vertragsart_kurzbz === 'werkvertrag') + if( $this->checkoverlap && !(in_array($this->vertragsart_kurzbz, array('werkvertrag', 'studentischehilfskr')) ) && $ci->VertragsbestandteilLib->isOverlappingExistingDV($this) ) { $this->validationerrors[] = 'Es existiert bereits ein überlappendes Dienstverhältnis'; diff --git a/application/libraries/vertragsbestandteil/VertragsbestandteilLib.php b/application/libraries/vertragsbestandteil/VertragsbestandteilLib.php index 8fb3900d5..297896a02 100644 --- a/application/libraries/vertragsbestandteil/VertragsbestandteilLib.php +++ b/application/libraries/vertragsbestandteil/VertragsbestandteilLib.php @@ -26,30 +26,35 @@ class VertragsbestandteilLib { const INCLUDE_FUTURE = true; const DO_NOT_INCLUDE_FUTURE = false; - + protected $CI; /** @var Dienstverhaeltnis_model */ protected $DienstverhaeltnisModel; /** @var Vertragsbestandteil_model */ protected $VertragsbestandteilModel; - /** + /** @var Benutzer_model */ + protected $BenutzerModel; + /** * @var GehaltsbestandteilLib */ protected $GehaltsbestandteilLib; - + protected $loggedInUser; - + public function __construct() { $this->loggedInUser = getAuthUID(); $this->CI = get_instance(); - $this->CI->load->model('vertragsbestandteil/Dienstverhaeltnis_model', + $this->CI->load->model('vertragsbestandteil/Dienstverhaeltnis_model', 'DienstverhaeltnisModel'); $this->DienstverhaeltnisModel = $this->CI->DienstverhaeltnisModel; - $this->CI->load->model('vertragsbestandteil/Vertragsbestandteil_model', + $this->CI->load->model('vertragsbestandteil/Vertragsbestandteil_model', 'VertragsbestandteilModel'); $this->VertragsbestandteilModel = $this->CI->VertragsbestandteilModel; - $this->CI->load->library('vertragsbestandteil/GehaltsbestandteilLib', + $this->CI->load->model('person/benutzer_model', + 'BenutzerModel'); + $this->BenutzerModel = $this->CI->BenutzerModel; + $this->CI->load->library('vertragsbestandteil/GehaltsbestandteilLib', null, 'GehaltsbestandteilLib'); $this->GehaltsbestandteilLib = $this->CI->GehaltsbestandteilLib; } @@ -63,49 +68,54 @@ class VertragsbestandteilLib } catch (Exception $ex) { log_message('debug', "Error handling json data from GUI. " . $ex->getMessage()); - } + } return $ret; } + public function fetchDienstverhaeltnisse($unternehmen, $stichtag=null, $mitarbeiteruid=null) { + $dvs = $this->DienstverhaeltnisModel->fetchDienstverhaeltnisse($unternehmen, $stichtag, $mitarbeiteruid); + return $dvs; + } + public function fetchDienstverhaeltnis($dienstverhaeltnis_id) { $result = $this->DienstverhaeltnisModel->load($dienstverhaeltnis_id); $dv = null; - if(null !== ($row = getData($result))) + if(null !== ($row = getData($result))) { $dv = new Dienstverhaeltnis(); $dv->hydrateByStdClass($row[0], true); } return $dv; } - + public function fetchVertragsbestandteile($dienstverhaeltnis_id, $stichtag=null, $includefuture=false) { $vbs = $this->VertragsbestandteilModel->getVertragsbestandteile($dienstverhaeltnis_id, $stichtag, $includefuture); $gbs = $this->GehaltsbestandteilLib->fetchGehaltsbestandteile($dienstverhaeltnis_id, $stichtag, $includefuture); - + $gbsByVBid = array(); - foreach( $gbs as $gb ) + foreach( $gbs as $gb ) { - if( intval($gb->getVertragsbestandteil_id()) > 0 ) + if( intval($gb->getVertragsbestandteil_id()) > 0 ) { - if( !isset($gbsByVBid[$gb->getVertragsbestandteil_id()]) + if( !isset($gbsByVBid[$gb->getVertragsbestandteil_id()]) || !is_array($gbsByVBid[$gb->getVertragsbestandteil_id()]) ) { $gbsByVBid[$gb->getVertragsbestandteil_id()] = array(); } $gbsByVBid[$gb->getVertragsbestandteil_id()][] = $gb; } } - + foreach ($vbs as $vb) { - if( isset($gbsByVBid[$vb->getVertragsbestandteil_id()]) ) + if( isset($gbsByVBid[$vb->getVertragsbestandteil_id()]) ) { $vb->setGehaltsbestandteile($gbsByVBid[$vb->getVertragsbestandteil_id()]); } } - + return $vbs; } @@ -113,22 +123,22 @@ class VertragsbestandteilLib { return $this->VertragsbestandteilModel->getVertragsbestandteil($vertragsbestandteil_id); } - + public function storeDienstverhaeltnis(Dienstverhaeltnis $dv) { if( intval($dv->getDienstverhaeltnis_id()) > 0 ) { $this->updateDienstverhaeltnis($dv); } - else + else { - $this->insertDienstverhaeltnis($dv); + $this->insertDienstverhaeltnis($dv); } } - - public function storeVertragsbestandteil(Vertragsbestandteil $vertragsbestandteil) + + public function storeVertragsbestandteil(Vertragsbestandteil $vertragsbestandteil) { - $this->CI->db->trans_begin(); + $this->CI->db->trans_begin(); try { $this->setUIDtoPGSQL(); @@ -144,7 +154,7 @@ class VertragsbestandteilLib { log_message('debug', "Transaction failed"); throw new Exception("Transaction failed"); - } + } $this->CI->db->trans_commit(); } catch (Exception $ex) @@ -152,7 +162,7 @@ class VertragsbestandteilLib log_message('debug', "Transaction rolled back. " . $ex->getMessage()); $this->CI->db->trans_rollback(); throw new Exception('Storing Vertragsbestandteil failed.'); - } + } } public function deleteDienstverhaeltnis(Dienstverhaeltnis $dv) @@ -220,13 +230,13 @@ class VertragsbestandteilLib throw new Exception('Delete Vertragsbestandteil failed.'); } } - + protected function insertDienstverhaeltnis(Dienstverhaeltnis $dv) { $dv->setInsertvon($this->loggedInUser) ->setInsertamum(strftime('%Y-%m-%d %H:%M:%S')); $ret = $this->DienstverhaeltnisModel->insert($dv->toStdClass()); - if( hasData($ret) ) + if( hasData($ret) ) { $dv->setDienstverhaeltnis_id(getData($ret)); } @@ -235,14 +245,14 @@ class VertragsbestandteilLib throw new Exception('error inserting dienstverhaeltnis'); } } - + protected function insertVertragsbestandteil(Vertragsbestandteil $vertragsbestandteil) { $vertragsbestandteil->setInsertvon($this->loggedInUser) ->setInsertamum(strftime('%Y-%m-%d %H:%M:%S')); $vertragsbestandteil->beforePersist(); $ret = $this->VertragsbestandteilModel->insert($vertragsbestandteil->baseToStdClass()); - if( hasData($ret) ) + if( hasData($ret) ) { $vertragsbestandteil->setVertragsbestandteil_id(getData($ret)); } @@ -254,19 +264,19 @@ class VertragsbestandteilLib $specialisedModel = VertragsbestandteilFactory::getVertragsbestandteilDBModel( $vertragsbestandteil->getVertragsbestandteiltyp_kurzbz()); $retspecial = $specialisedModel->insert($vertragsbestandteil->toStdClass()); - + if(isError($retspecial) ) { - throw new Exception('error updating vertragsbestandteil ' + throw new Exception('error updating vertragsbestandteil ' . $vertragsbestandteil->getVertragsbestandteiltyp_kurzbz()); } - - try + + try { $gehaltsbestandteile = $vertragsbestandteil->getGehaltsbestandteile(); $this->GehaltsbestandteilLib->storeGehaltsbestandteile($gehaltsbestandteile); - } - catch(Exception $ex) + } + catch(Exception $ex) { throw new Exception('VertragsbestandteilLib insertVertragsbestandteil ' . 'failed to store Gehaltsbestandteile. ' . $ex->getMessage()); @@ -278,7 +288,7 @@ class VertragsbestandteilLib if(!$dv->isDirty()) { return; } - + $dv->setUpdatevon($this->loggedInUser) ->setUpdateamum(strftime('%Y-%m-%d %H:%M:%S')); $ret = $this->DienstverhaeltnisModel->update($dv->getDienstverhaeltnis_id(), @@ -288,20 +298,20 @@ class VertragsbestandteilLib throw new Exception('error updating dienstverhaeltnis'); } } - + private function deleteVertragsbestandteilHelper(Vertragsbestandteil $vertragsbestandteil) { $specialisedModel = VertragsbestandteilFactory::getVertragsbestandteilDBModel( $vertragsbestandteil->getVertragsbestandteiltyp_kurzbz()); $retspecial = $specialisedModel->delete($vertragsbestandteil->getVertragsbestandteil_id()); - + if(isError($retspecial) ) { throw new Exception('error deleting vertragsbestandteil ' . $vertragsbestandteil->getVertragsbestandteiltyp_kurzbz()); } - + try { $gehaltsbestandteile = $vertragsbestandteil->getGehaltsbestandteile(); @@ -320,76 +330,118 @@ class VertragsbestandteilLib { throw new Exception('error deleting vertragsbestandteil'); } - + $vertragsbestandteil->afterDelete(); } protected function updateVertragsbestandteil(Vertragsbestandteil $vertragsbestandteil) { - if($vertragsbestandteil->isDirty()) { + if($vertragsbestandteil->isDirty()) { $vertragsbestandteil->setUpdatevon($this->loggedInUser) ->setUpdateamum(strftime('%Y-%m-%d %H:%M:%S')); $vertragsbestandteil->beforePersist(); $basedata = $vertragsbestandteil->baseToStdClass(); - if( count((array) $basedata) > 0 ) + if( count((array) $basedata) > 0 ) { $ret = $this->VertragsbestandteilModel->update( - $vertragsbestandteil->getVertragsbestandteil_id(), + $vertragsbestandteil->getVertragsbestandteil_id(), $basedata); if(isError($ret) ) { throw new Exception('error updating vertragsbestandteil'); - } + } } $specialisedData = $vertragsbestandteil->toStdClass(); - if( count((array) $specialisedData) > 0 ) + if( count((array) $specialisedData) > 0 ) { $specialisedModel = VertragsbestandteilFactory::getVertragsbestandteilDBModel( $vertragsbestandteil->getVertragsbestandteiltyp_kurzbz()); $retspecial = $specialisedModel->update( - $vertragsbestandteil->getVertragsbestandteil_id(), + $vertragsbestandteil->getVertragsbestandteil_id(), $specialisedData); if(isError($retspecial) ) { - throw new Exception('error updating vertragsbestandteil ' + throw new Exception('error updating vertragsbestandteil ' . $vertragsbestandteil->getVertragsbestandteiltyp_kurzbz()); } } } - - try + + try { $gehaltsbestandteile = $vertragsbestandteil->getGehaltsbestandteile(); $this->GehaltsbestandteilLib->storeGehaltsbestandteile($gehaltsbestandteile); - } - catch(Exception $ex) + } + catch(Exception $ex) { throw new Exception('VertragsbestandteilLib updateVertragsbestandteil ' . 'failed to store Gehaltsbestandteile. ' . $ex->getMessage()); } } - - public function isOverlappingExistingDV(Dienstverhaeltnis $dv) + + public function isOverlappingExistingDV(Dienstverhaeltnis $dv) { return $this->DienstverhaeltnisModel->isOverlappingExistingDV( - $dv->getMitarbeiter_uid(), - $dv->getOe_kurzbz(), - $dv->getVon(), + $dv->getMitarbeiter_uid(), + $dv->getOe_kurzbz(), + $dv->getVon(), $dv->getBis(), $dv->getDienstverhaeltnis_id() ); } - + + protected function hasOtherActiveDV(Dienstverhaeltnis $dv, $duedate) + { + $hasotheractivedv = false; + $result = $this->DienstverhaeltnisModel->getDVByPersonUID($dv->getMitarbeiter_uid(), null, $duedate); + $dvs = getData($result); + foreach ($dvs as $tmpdv) + { + if(intval($tmpdv->dienstverhaeltnis_id) !== intval($dv->getDienstverhaeltnis_id())) + { + $hasotheractivedv = true; + break; + } + } + return $hasotheractivedv; + } + + /** + * like endDienstverhaeltnis, but also sets aktiv flag to false + */ + public function deactivateDienstverhaeltnis(Dienstverhaeltnis $dv, $enddate, $deactivate) + { + $result = $this->endDienstverhaeltnis($dv, $enddate); + if ( $result === true) + { + if (!$deactivate) return $result; + + if(!$this->hasOtherActiveDV($dv, $enddate)) + { + $result = $this->BenutzerModel->update( + array('uid' => $dv->getMitarbeiter_uid()), + array( + 'aktiv' => false, + 'updateaktivam' => date('Y-m-d'), + 'updateaktivvon' => $this->loggedInUser + ) + ); + } + } + + return $result; + } + public function endDienstverhaeltnis(Dienstverhaeltnis $dv, $enddate) { - if( $dv->getBis() !== null && $dv->getBis() < $enddate ) + if( $dv->getBis() !== null && $dv->getBis() < $enddate ) { return 'Dienstverhältnis ist bereits beendet.'; } - + $this->CI->db->trans_begin(); try { @@ -401,13 +453,13 @@ class VertragsbestandteilLib { $this->GehaltsbestandteilLib->endGehaltsbestandteil($gb, $enddate); } - + $vbs = $this->fetchVertragsbestandteile($dv->getDienstverhaeltnis_id()); foreach ($vbs as $vb) { $this->endVertragsbestandteil($vb, $enddate); - } - + } + $dv->setBis($enddate); $this->updateDienstverhaeltnis($dv); @@ -428,23 +480,23 @@ class VertragsbestandteilLib } return true; } - + public function endVertragsbestandteil(Vertragsbestandteil $vertragsbestandteil, $enddate) { - if( $vertragsbestandteil->getBis() !== null && $vertragsbestandteil->getBis() < $enddate ) + if( $vertragsbestandteil->getBis() !== null && $vertragsbestandteil->getBis() < $enddate ) { return; } - + $vertragsbestandteil->setBis($enddate); $this->updateVertragsbestandteil($vertragsbestandteil); } - + protected function setUIDtoPGSQL() { $ret = $this->VertragsbestandteilModel - ->execReadOnlyQuery('SET LOCAL pv21.uid TO \'' + ->execReadOnlyQuery('SET LOCAL pv21.uid TO \'' . $this->loggedInUser . '\''); - if(isError($ret)) + if(isError($ret)) { throw new Exception('error setting uid to pgsql'); } diff --git a/application/models/codex/Bismeldestichtag_model.php b/application/models/codex/Bismeldestichtag_model.php index 1a45f0fbd..6ab755c8b 100644 --- a/application/models/codex/Bismeldestichtag_model.php +++ b/application/models/codex/Bismeldestichtag_model.php @@ -11,4 +11,25 @@ class Bismeldestichtag_model extends DB_Model $this->dbTable = 'bis.tbl_bismeldestichtag'; $this->pk = 'meldestichtag_id'; } + + /** + * Gets last Bismeldestichtag for a Studiensemester. + * @param $studiensemester_kurzbz + * @return object success or error + */ + public function getByStudiensemester($studiensemester_kurzbz) + { + $query = ' + SELECT + meldestichtag + FROM + bis.tbl_bismeldestichtag + JOIN public.tbl_studiensemester USING (studiensemester_kurzbz) + WHERE + studiensemester_kurzbz = ? + ORDER BY meldestichtag DESC + LIMIT 1'; + + return $this->execQuery($query, array($studiensemester_kurzbz)); + } } diff --git a/application/models/crm/Prestudentstatus_model.php b/application/models/crm/Prestudentstatus_model.php index e5f043358..30e4f1a42 100755 --- a/application/models/crm/Prestudentstatus_model.php +++ b/application/models/crm/Prestudentstatus_model.php @@ -338,7 +338,7 @@ class Prestudentstatus_model extends DB_Model return $this->loadWhere($where); } - public function loadLastWithStgDetails($prestudent_id, $studiensemester_kurzbz = null) + public function loadLastWithStgDetails($prestudent_id, $studiensemester_kurzbz = null, $max_date = null) { $this->load->config('studierendenantrag'); @@ -375,7 +375,8 @@ class Prestudentstatus_model extends DB_Model $this->addLimit(1); - $this->db->where_in($this->dbTable . '.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist')); + if ($max_date) + $this->db->where($this->dbTable . '.insertamum <', $max_date); $whereArr = [ $this->dbTable . '.prestudent_id' => $prestudent_id, diff --git a/application/models/education/Pruefung_model.php b/application/models/education/Pruefung_model.php index 217915ff1..3ecb3e3d2 100755 --- a/application/models/education/Pruefung_model.php +++ b/application/models/education/Pruefung_model.php @@ -116,7 +116,13 @@ class Pruefung_model extends DB_Model $this->addJoin('public.tbl_person pers', 'person_id'); $this->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid'); $this->addJoin('public.tbl_studiengang g', 'ps.studiengang_kz=g.studiengang_kz'); - $this->addJoin('public.tbl_prestudentstatus pss', 'pss.prestudent_id=ps.prestudent_id AND pss.studiensemester_kurzbz=le.studiensemester_kurzbz AND pss.status_kurzbz=get_rolle_prestudent(ps.prestudent_id, le.studiensemester_kurzbz)', 'LEFT'); + $this->addJoin( + 'public.tbl_prestudentstatus pss', + 'pss.prestudent_id=ps.prestudent_id + AND pss.studiensemester_kurzbz=le.studiensemester_kurzbz + AND pss.status_kurzbz=get_rolle_prestudent(ps.prestudent_id, le.studiensemester_kurzbz)', + 'LEFT' + ); $this->addJoin('lehre.tbl_studienplan plan', 'studienplan_id', 'LEFT'); $this->addJoin('bis.tbl_orgform o', 'COALESCE(plan.orgform_kurzbz, pss.orgform_kurzbz, g.orgform_kurzbz)=o.orgform_kurzbz'); $this->db->join('campus.tbl_studierendenantrag a', 'ps.prestudent_id=a.prestudent_id and a.typ = ?', 'LEFT', false); @@ -196,6 +202,7 @@ class Pruefung_model extends DB_Model $this->addSelect('a.studierendenantrag_id'); $this->addSelect('a.typ'); $this->addSelect('campus.get_status_studierendenantrag(a.studierendenantrag_id) status'); + $this->addSelect('pss.ausbildungssemester'); $this->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id'); $this->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id'); @@ -204,12 +211,20 @@ class Pruefung_model extends DB_Model $this->addJoin('public.tbl_person pers', 'person_id'); $this->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid'); $this->addJoin('public.tbl_studiengang g', 'ps.studiengang_kz=g.studiengang_kz'); - $this->addJoin('public.tbl_prestudentstatus pss', 'pss.prestudent_id=ps.prestudent_id AND pss.studiensemester_kurzbz=le.studiensemester_kurzbz AND pss.status_kurzbz=get_rolle_prestudent(ps.prestudent_id, le.studiensemester_kurzbz)', 'LEFT'); + $this->addJoin( + 'public.tbl_prestudentstatus pss', + 'pss.prestudent_id=ps.prestudent_id + AND pss.studiensemester_kurzbz=le.studiensemester_kurzbz + AND pss.status_kurzbz=get_rolle_prestudent(ps.prestudent_id, le.studiensemester_kurzbz)', + 'LEFT' + ); $this->addJoin('lehre.tbl_studienplan plan', 'studienplan_id', 'LEFT'); $this->addJoin('bis.tbl_orgform o', 'COALESCE(plan.orgform_kurzbz, pss.orgform_kurzbz, g.orgform_kurzbz)=o.orgform_kurzbz'); - $this->addJoin('campus.tbl_studierendenantrag a', 'ps.prestudent_id=a.prestudent_id and a.typ=' . $this->escape(Studierendenantrag_model::TYP_WIEDERHOLUNG), 'LEFT'); - - $this->db->where_in("get_rolle_prestudent(ps.prestudent_id, null)", $this->config->item('antrag_prestudentstatus_whitelist')); + $this->addJoin( + 'campus.tbl_studierendenantrag a', + 'ps.prestudent_id=a.prestudent_id and a.typ=' . $this->escape(Studierendenantrag_model::TYP_WIEDERHOLUNG), + 'LEFT' + ); $this->db->where("g.aktiv", true); @@ -267,6 +282,8 @@ class Pruefung_model extends DB_Model $this->db->where("b.aktiv", true); + $this->db->where_in("get_rolle_prestudent(ps.prestudent_id, null)", $this->config->item('antrag_prestudentstatus_whitelist')); + if (is_array($status)) { if (in_array(null, $status)) { $status = array_filter($status); diff --git a/application/models/education/Studierendenantrag_model.php b/application/models/education/Studierendenantrag_model.php index ea481ebef..e138d1a1c 100755 --- a/application/models/education/Studierendenantrag_model.php +++ b/application/models/education/Studierendenantrag_model.php @@ -46,6 +46,8 @@ class Studierendenantrag_model extends DB_Model $this->addSelect('datum_wiedereinstieg'); $this->addSelect($this->dbTable . '.typ'); $this->addSelect('st.studierendenantrag_statustyp_kurzbz as status'); + $this->addSelect('s.insertvon as status_insertvon'); + $this->addSelect('s.insertamum as status_insertamum'); $this->addSelect('dms_id'); $this->addSelect('st.bezeichnung[(' . $sql . ')] as statustyp'); @@ -54,7 +56,13 @@ class Studierendenantrag_model extends DB_Model $this->addJoin('public.tbl_person', 'person_id'); $this->addJoin('public.tbl_studiengang stg', 'p.studiengang_kz=stg.studiengang_kz'); $this->addJoin('public.tbl_studiensemester ss', 'studiensemester_kurzbz'); - $this->addJoin('public.tbl_prestudentstatus ps', 'ps.prestudent_id=p.prestudent_id AND ps.studiensemester_kurzbz=ss.studiensemester_kurzbz AND ps.status_kurzbz=get_rolle_prestudent(p.prestudent_id, ss.studiensemester_kurzbz)', 'LEFT'); + $this->addJoin( + 'public.tbl_prestudentstatus ps', + 'ps.prestudent_id=p.prestudent_id + AND ps.studiensemester_kurzbz=ss.studiensemester_kurzbz + AND ps.status_kurzbz=get_rolle_prestudent(p.prestudent_id, ss.studiensemester_kurzbz)', + 'LEFT' + ); $this->addJoin('lehre.tbl_studienplan plan', 'studienplan_id', 'LEFT'); $this->addJoin('bis.tbl_orgform of', 'of.orgform_kurzbz=COALESCE(plan.orgform_kurzbz, ps.orgform_kurzbz, stg.orgform_kurzbz)'); $this->addJoin( @@ -76,7 +84,9 @@ class Studierendenantrag_model extends DB_Model public function loadActiveForStudiengaenge($studiengaenge) { - // NOTE(chris): get language before changing things in the global db object because getUserLanguage() might use it and it should not have been tampered with + // NOTE(chris): get language before changing things in the global + // db object because getUserLanguage() might use it and it should + // not have been tampered with $sql = "SELECT index FROM public.tbl_sprache WHERE sprache='" . getUserLanguage() . "' LIMIT 1"; $this->db->group_start(); @@ -85,7 +95,8 @@ class Studierendenantrag_model extends DB_Model Studierendenantragstatus_model::STATUS_APPROVED, Studierendenantragstatus_model::STATUS_REJECTED, Studierendenantragstatus_model::STATUS_OBJECTION_DENIED, - Studierendenantragstatus_model::STATUS_DEREGISTERED + Studierendenantragstatus_model::STATUS_DEREGISTERED, + Studierendenantragstatus_model::STATUS_PAUSE ]); $this->db->or_group_start(); $this->db->where('s.studierendenantrag_statustyp_kurzbz', Studierendenantragstatus_model::STATUS_APPROVED); @@ -133,12 +144,18 @@ class Studierendenantrag_model extends DB_Model $lang = 'SELECT index FROM public.tbl_sprache WHERE sprache=' . $this->escape(getUserLanguage()); $this->addSelect('*'); - $this->addSelect('campus.get_status_studierendenantrag(studierendenantrag_id) status'); + $this->addSelect($this->dbTable . '.grund AS grund'); + $this->addSelect('s.studierendenantrag_statustyp_kurzbz status'); + $this->addSelect('s.insertvon status_insertvon'); $this->addSelect('t.bezeichnung[(' . $lang . ')] statustyp'); + $this->addJoin( + 'campus.tbl_studierendenantrag_status s', + 'campus.get_status_id_studierendenantrag(' . $this->dbTable . '.studierendenantrag_id)=s.studierendenantrag_status_id' + ); $this->addJoin( 'campus.tbl_studierendenantrag_statustyp t', - 'campus.get_status_studierendenantrag(studierendenantrag_id)=t.studierendenantrag_statustyp_kurzbz' + 's.studierendenantrag_statustyp_kurzbz=t.studierendenantrag_statustyp_kurzbz' ); if ($types && is_array($types)) { @@ -168,7 +185,11 @@ class Studierendenantrag_model extends DB_Model $this->addJoin( 'public.tbl_prestudentstatus s', - $this->dbTable . '.prestudent_id=s.prestudent_id AND ' . $this->dbTable . '.studiensemester_kurzbz=s.studiensemester_kurzbz' + $this->dbTable . '.prestudent_id=s.prestudent_id + AND ' . + $this->dbTable . '.studiensemester_kurzbz=s.studiensemester_kurzbz + AND ' . + $this->dbTable . '.insertamum > s.insertamum' ); $this->addJoin('public.tbl_prestudent p', $this->dbTable . '.prestudent_id=p.prestudent_id'); $this->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT'); @@ -180,8 +201,6 @@ class Studierendenantrag_model extends DB_Model $this->addLimit(1); - $this->db->where_in('s.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist')); - return $this->loadWhere([ $this->pk => $antrag_id ]); @@ -233,20 +252,45 @@ class Studierendenantrag_model extends DB_Model $this->addSelect($this->dbTable . '.datum_wiedereinstieg'); $this->addSelect($this->dbTable . '.grund'); $this->addSelect($this->dbTable . '.dms_id'); - $this->addSelect("(SELECT count(1) FROM campus.tbl_studierendenantrag_status WHERE studierendenantrag_id = " . $this->dbTable . ".studierendenantrag_id AND studierendenantrag_statustyp_kurzbz = 'Genehmigt') AS isapproved", false); + $this->addSelect('s.insertvon AS status_insertvon'); + $this->addSelect( + "(SELECT count(1) FROM campus.tbl_studierendenantrag_status WHERE studierendenantrag_id = " . + $this->dbTable . + ".studierendenantrag_id AND studierendenantrag_statustyp_kurzbz = 'Genehmigt') AS isapproved", + false + ); $this->addJoin('public.tbl_prestudent p', 'prestudent_id', 'RIGHT'); $this->addJoin('public.tbl_studiengang stg', 'p.studiengang_kz=stg.studiengang_kz'); - $this->addJoin('public.tbl_prestudentstatus ps', 'ps.prestudent_id=p.prestudent_id AND ps.studiensemester_kurzbz=' . $this->dbTable . '.studiensemester_kurzbz AND ps.status_kurzbz=get_rolle_prestudent(p.prestudent_id, ' . $this->dbTable . '.studiensemester_kurzbz)', 'LEFT'); + $this->addJoin( + 'public.tbl_prestudentstatus ps', + 'ps.prestudent_id=p.prestudent_id AND ps.studiensemester_kurzbz=' . + $this->dbTable . + '.studiensemester_kurzbz AND ps.status_kurzbz=get_rolle_prestudent(p.prestudent_id, ' . + $this->dbTable . + '.studiensemester_kurzbz)', + 'LEFT' + ); $this->addJoin('lehre.tbl_studienplan plan', 'studienplan_id', 'LEFT'); $this->addJoin('bis.tbl_orgform of', 'of.orgform_kurzbz=COALESCE(plan.orgform_kurzbz, ps.orgform_kurzbz, stg.orgform_kurzbz)'); + $this->addJoin( + 'campus.tbl_studierendenantrag_status s', + 'campus.get_status_id_studierendenantrag(' . $this->dbTable . '.studierendenantrag_id)=s.studierendenantrag_status_id', + 'LEFT' + ); $this->addJoin( 'campus.tbl_studierendenantrag_statustyp st', - 'campus.get_status_studierendenantrag(studierendenantrag_id)=st.studierendenantrag_statustyp_kurzbz', + 's.studierendenantrag_statustyp_kurzbz=st.studierendenantrag_statustyp_kurzbz', 'LEFT' ); - $this->db->where("(SELECT status_kurzbz FROM public.tbl_prestudentstatus WHERE prestudent_id=p.prestudent_id AND status_kurzbz='Student' LIMIT 1) IS NOT NULL", null, false); + $this->db->where("( + SELECT status_kurzbz + FROM public.tbl_prestudentstatus + WHERE prestudent_id=p.prestudent_id + AND status_kurzbz='Student' + LIMIT 1 + ) IS NOT NULL", null, false); return $this->loadWhere([ @@ -287,4 +331,144 @@ class Studierendenantrag_model extends DB_Model return $this->loadWhere($where); } + + /** + * Checks if the Prestudent has an active Unterbrechung between + * the start of the given semester and the given enddate. + * If the enddate is omitted the end of the given semester is used. + * + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz + * @param string $enddate (optional) + * + * @return boolean + */ + public function hasRunningUnterbrechungBetween($prestudent_id, $studiensemester, $enddate = null) + { + $start = '(SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=' . $this->db->escape($studiensemester) . ')'; + $end = $enddate + ? $this->db->escape($enddate) + : '(SELECT ende FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=' . $this->db->escape($studiensemester) . ')'; + + $this->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $this->db->where([ + 'prestudent_id' => $prestudent_id, + 'typ' => Studierendenantrag_model::TYP_UNTERBRECHUNG, + 'campus.get_status_studierendenantrag(studierendenantrag_id) !=' => Studierendenantragstatus_model::STATUS_CANCELLED, + 'start < ' . $end => null, + 'datum_wiedereinstieg > ' . $start => null, + ]); + return (boolean)$this->db->count_all_results($this->dbTable); + } + + /** + * Gets free semester slots for a new Unterbrechung. + * + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz (optional) + * + * @return stdClass + */ + public function getFreeSlotsForUnterbrechung($prestudent_id, $studiensemester = null) + { + $max_starters = 2; + $max_length = max( + 2, + (integer)$this->config->item('unterbrecher_semester_max_length') + ); + + + $subquery = ''; + if ($studiensemester) + $subquery = 'SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=?'; + else + $subquery = 'SELECT start FROM public.tbl_studiensemester WHERE studiensemester_kurzbz=public.get_stdsem_prestudent (?, null)'; + + $sql = "WITH numbered_sems AS ( + SELECT + a.studienjahr_kurzbz AS studienjahr_kurzbz, + a.studiensemester_kurzbz AS von, + b.studiensemester_kurzbz AS bis, + a.start AS start, + b.start AS ende, + ROW_NUMBER() OVER ( + PARTITION BY a.studiensemester_kurzbz + ORDER BY b.start + ) AS row_number + FROM public.tbl_studiensemester a + LEFT JOIN public.tbl_studiensemester b ON (b.start > a.ende) + ), + last_sems AS ( + SELECT * + FROM numbered_sems + WHERE numbered_sems.row_number <= ? + ) + SELECT s.von, s.bis, s.start, s.ende, studierendenantrag_id, studienjahr_kurzbz + FROM last_sems s + LEFT JOIN ( + SELECT studierendenantrag_id, start, datum_wiedereinstieg AS ende + FROM campus.tbl_studierendenantrag + LEFT JOIN public.tbl_studiensemester USING(studiensemester_kurzbz) + WHERE typ=? + AND campus.get_status_studierendenantrag(studierendenantrag_id) != ? + AND prestudent_id=? + ) a ON (s.start < a.ende AND s.ende > a.start) + WHERE s.start >= (" . $subquery . ") + ORDER BY s.start, s.ende + LIMIT ?;"; + + return $this->execQuery($sql, [ + $max_length, + self::TYP_UNTERBRECHUNG, + Studierendenantragstatus_model::STATUS_CANCELLED, + $prestudent_id, + $studiensemester ?: $prestudent_id, + $max_length * $max_starters + ]); + } + + /** + * Returns if an Antrag is manually paused + * + * @param integer $antrag_id + * + * @return boolean + */ + public function isManuallyPaused($antrag_id) + { + $this->addJoin( + 'campus.tbl_studierendenantrag_status s', + 'campus.get_status_id_studierendenantrag(' . $this->dbTable . '.studierendenantrag_id)=s.studierendenantrag_status_id' + ); + + $this->db->where([ + 's.studierendenantrag_id' => $antrag_id, + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_PAUSE + ]); + + $this->db->group_start(); + $this->db->where_not_in('s.insertvon', [ + Studierendenantragstatus_model::INSERTVON_DEREGISTERED, + Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL + ]); + $this->db->or_group_start(); + $this->db->where('s.insertvon', Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL); + $this->db->where('1 !=', '( + SELECT COUNT(*)%2 + FROM campus.tbl_studierendenantrag_status i + WHERE i.studierendenantrag_id = s.studierendenantrag_id + AND i.insertamum > ( + SELECT ii.insertamum + FROM campus.tbl_studierendenantrag_status ii + WHERE ii.studierendenantrag_id = s.studierendenantrag_id + AND ii.insertvon <> ' . $this->escape(Studierendenantragstatus_model::INSERTVON_ABMELDUNGSTGL) . ' + ORDER BY ii.insertamum DESC + LIMIT 1 + ) + )', false); + $this->db->group_end(); + $this->db->group_end(); + + return hasData($this->load()); + } } diff --git a/application/models/education/Studierendenantragstatus_model.php b/application/models/education/Studierendenantragstatus_model.php index c134cc4ee..cf9cce1be 100755 --- a/application/models/education/Studierendenantragstatus_model.php +++ b/application/models/education/Studierendenantragstatus_model.php @@ -15,6 +15,10 @@ class Studierendenantragstatus_model extends DB_Model const STATUS_OBJECTED = 'Beeinsprucht'; const STATUS_OBJECTION_DENIED = 'EinspruchAbgelehnt'; const STATUS_DEREGISTERED = 'Abgemeldet'; + const STATUS_PAUSE = 'Pause'; + + const INSERTVON_ABMELDUNGSTGL = "AbmeldungStgl"; + const INSERTVON_DEREGISTERED = "Studienabbruch"; /** * Constructor @@ -49,4 +53,157 @@ class Studierendenantragstatus_model extends DB_Model return $this->loadWhere($where); } + + public function stopAntraegeForAbmeldungStgl($antrag_id) + { + $sql = 'INSERT INTO campus.tbl_studierendenantrag_status + (studierendenantrag_id, studierendenantrag_statustyp_kurzbz, insertvon, insertamum) + SELECT studierendenantrag_id, ?, ?, ( + SELECT insertamum + FROM campus.tbl_studierendenantrag_status + WHERE studierendenantrag_status_id = campus.get_status_id_studierendenantrag(?) + ) + FROM campus.tbl_studierendenantrag + WHERE prestudent_id = ( + SELECT prestudent_id + FROM campus.tbl_studierendenantrag + WHERE studierendenantrag_id = ? + ) + AND studierendenantrag_id <> ? + AND ( + ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) IN ? + ) OR ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) IN ? + ) OR ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) IN ? + ) + )'; + + return $this->execQuery($sql, [ + self::STATUS_PAUSE, + self::INSERTVON_ABMELDUNGSTGL, + $antrag_id, + $antrag_id, + $antrag_id, + Studierendenantrag_model::TYP_ABMELDUNG, + [ + Studierendenantragstatus_model::STATUS_CREATED + ], + Studierendenantrag_model::TYP_UNTERBRECHUNG, + [ + Studierendenantragstatus_model::STATUS_CREATED + ], + Studierendenantrag_model::TYP_WIEDERHOLUNG, + [ + Studierendenantragstatus_model::STATUS_REQUESTSENT_1, + Studierendenantragstatus_model::STATUS_REQUESTSENT_2, + Studierendenantragstatus_model::STATUS_CREATED, + Studierendenantragstatus_model::STATUS_LVSASSIGNED, + Studierendenantragstatus_model::STATUS_PAUSE + ], + ]); + } + + public function resumeAntraegeForAbmeldungStgl($antrag_id) + { + $sql = 'INSERT INTO campus.tbl_studierendenantrag_status + (studierendenantrag_id, studierendenantrag_statustyp_kurzbz, insertvon, insertamum) + SELECT studierendenantrag_id, ( + SELECT studierendenantrag_statustyp_kurzbz + FROM campus.tbl_studierendenantrag_status s + WHERE s.studierendenantrag_id=a.studierendenantrag_id + AND campus.get_status_id_studierendenantrag(a.studierendenantrag_id) <> studierendenantrag_status_id + ORDER BY insertamum DESC + LIMIT 1 + ), ?, ( + SELECT insertamum + FROM campus.tbl_studierendenantrag_status + WHERE studierendenantrag_status_id = campus.get_status_id_studierendenantrag(?) + ) + FROM campus.tbl_studierendenantrag a + WHERE prestudent_id = ( + SELECT prestudent_id + FROM campus.tbl_studierendenantrag + WHERE studierendenantrag_id = ? + ) + AND typ <> ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) = ? + '; + + return $this->execQuery($sql, [ + self::INSERTVON_ABMELDUNGSTGL, + $antrag_id, + $antrag_id, + Studierendenantrag_model::TYP_ABMELDUNG_STGL, + Studierendenantragstatus_model::STATUS_PAUSE + ]); + } + + public function stopAntraegeForAbbruchBy($antrag_id) + { + $sql = 'INSERT INTO campus.tbl_studierendenantrag_status + (studierendenantrag_id, studierendenantrag_statustyp_kurzbz, insertvon, insertamum) + SELECT studierendenantrag_id, ?, ?, ( + SELECT insertamum + FROM campus.tbl_studierendenantrag_status + WHERE studierendenantrag_status_id = campus.get_status_id_studierendenantrag(?) + ) + FROM campus.tbl_studierendenantrag + WHERE prestudent_id = ( + SELECT prestudent_id + FROM campus.tbl_studierendenantrag + WHERE studierendenantrag_id = ? + ) + AND studierendenantrag_id <> ? + AND ( + ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) NOT IN ? + ) OR ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) NOT IN ? + ) OR ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) NOT IN ? + ) OR ( + typ = ? + AND campus.get_status_studierendenantrag(studierendenantrag_id) NOT IN ? + ) + )'; + + return $this->execQuery($sql, [ + self::STATUS_PAUSE, + self::INSERTVON_DEREGISTERED, + $antrag_id, + $antrag_id, + $antrag_id, + Studierendenantrag_model::TYP_ABMELDUNG, + [ + Studierendenantragstatus_model::STATUS_APPROVED, + Studierendenantragstatus_model::STATUS_CANCELLED + ], + Studierendenantrag_model::TYP_UNTERBRECHUNG, + [ + Studierendenantragstatus_model::STATUS_APPROVED, + Studierendenantragstatus_model::STATUS_CANCELLED, + Studierendenantragstatus_model::STATUS_REMINDERSENT, + Studierendenantragstatus_model::STATUS_REJECTED + ], + Studierendenantrag_model::TYP_ABMELDUNG_STGL, + [ + Studierendenantragstatus_model::STATUS_CANCELLED, + Studierendenantragstatus_model::STATUS_DEREGISTERED, + Studierendenantragstatus_model::STATUS_OBJECTION_DENIED + ], + Studierendenantrag_model::TYP_WIEDERHOLUNG, + [ + Studierendenantragstatus_model::STATUS_DEREGISTERED, + Studierendenantragstatus_model::STATUS_APPROVED + ], + ]); + } } diff --git a/application/models/organisation/Studiengang_model.php b/application/models/organisation/Studiengang_model.php index 1e4917f78..e306ce950 100755 --- a/application/models/organisation/Studiengang_model.php +++ b/application/models/organisation/Studiengang_model.php @@ -610,7 +610,7 @@ class Studiengang_model extends DB_Model $this->addJoin('public.tbl_student stud', 'p.prestudent_id=stud.prestudent_id', 'LEFT'); $this->db->where_in($this->dbTable . '.studiengang_kz', $studiengang_kzs); - $this->db->where_in('ps.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist')); + $this->db->where_in('ps.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist_abmeldung')); $this->db->where($this->dbTable . ".aktiv", true); if ($not_antrag_typ !== null && is_array($not_antrag_typ)) { diff --git a/application/models/ressource/Zeitaufzeichnung_model.php b/application/models/ressource/Zeitaufzeichnung_model.php index b44861d13..8639a716a 100755 --- a/application/models/ressource/Zeitaufzeichnung_model.php +++ b/application/models/ressource/Zeitaufzeichnung_model.php @@ -21,4 +21,26 @@ class Zeitaufzeichnung_model extends DB_Model return $this->execQuery($qry); } + + public function getFullInterval($uid, $fromDate, $toDate) + { + $qry = <<execQuery($qry, array($uid, $fromDate, $toDate, $uid, $fromDate, $toDate, $fromDate, $toDate)); + } } diff --git a/application/models/vertragsbestandteil/Dienstverhaeltnis_model.php b/application/models/vertragsbestandteil/Dienstverhaeltnis_model.php index 5b276c55e..2fdfcffe2 100644 --- a/application/models/vertragsbestandteil/Dienstverhaeltnis_model.php +++ b/application/models/vertragsbestandteil/Dienstverhaeltnis_model.php @@ -18,7 +18,7 @@ class Dienstverhaeltnis_model extends DB_Model $result = null; $qry = " - SELECT + SELECT dv.dienstverhaeltnis_id, tbl_benutzer.uid, tbl_mitarbeiter.personalnummer, @@ -30,8 +30,8 @@ class Dienstverhaeltnis_model extends DB_Model org.oe_kurzbz, org.bezeichnung oe_bezeichnung, dv.von, - dv.bis, - dv.vertragsart_kurzbz, + dv.bis, + dv.vertragsart_kurzbz, dv.updateamum, dv.updatevon FROM tbl_mitarbeiter @@ -59,13 +59,13 @@ class Dienstverhaeltnis_model extends DB_Model "; return $this->execQuery($qry, $data); - + } public function getDVByID($dvid) { $this->addSelect('hr.tbl_dienstverhaeltnis.*, public.tbl_organisationseinheit.bezeichnung as unternehmen'); $this->addJoin('public.tbl_organisationseinheit', 'hr.tbl_dienstverhaeltnis.oe_kurzbz = public.tbl_organisationseinheit.oe_kurzbz'); - $result = $this->load($dvid); + $result = $this->load($dvid); if (hasData($result)) { return $result; @@ -81,7 +81,7 @@ class Dienstverhaeltnis_model extends DB_Model $datestring = $date->format("Y-m-d"); $qry = " - SELECT + SELECT dv.dienstverhaeltnis_id, tbl_benutzer.uid, tbl_mitarbeiter.personalnummer, @@ -115,26 +115,26 @@ class Dienstverhaeltnis_model extends DB_Model $params = array_merge($params, array($dvid, $dvid)); $dvidclause = <<= COALESCE(vb.von, '1970-01-01'::date) - AND - COALESCE(dv.bis::date, '2170-12-31'::date) <= COALESCE(vb.bis, '2170-12-31') + AND + vb.vertragsbestandteiltyp_kurzbz = 'karenz' + AND + dv.von::date >= COALESCE(vb.von, '1970-01-01'::date) + AND + COALESCE(dv.bis::date, '2170-12-31'::date) <= COALESCE(vb.bis, '2170-12-31') ) = 0 AND dv.dienstverhaeltnis_id != ? EODVIDC; - + } - + $query = <<= dv.von + AND + COALESCE(?::date, '2170-12-31'::date) >= dv.von AND ( - SELECT - COUNT(*) AS karenzen - FROM - hr.tbl_vertragsbestandteil vb - WHERE + SELECT + COUNT(*) AS karenzen + FROM + hr.tbl_vertragsbestandteil vb + WHERE vb.dienstverhaeltnis_id = dv.dienstverhaeltnis_id - AND - vb.vertragsbestandteiltyp_kurzbz = 'karenz' - AND - ?::date >= COALESCE(vb.von, '1970-01-01'::date) - AND - COALESCE(?::date, '2170-12-31'::date) <= COALESCE(vb.bis, '2170-12-31') - ) = 0 + AND + vb.vertragsbestandteiltyp_kurzbz = 'karenz' + AND + ?::date >= COALESCE(vb.von, '1970-01-01'::date) + AND + COALESCE(?::date, '2170-12-31'::date) <= COALESCE(vb.bis, '2170-12-31') + ) = 0 {$dvidclause} EOSQL; - + $ret = $this->execReadOnlyQuery($query, $params); - + if( ($dvcount = getData($ret)) && ($dvcount[0]->dvcount > 0) ) { return true; } - - return false; + + return false; } -} \ No newline at end of file + + public function getDVByPersonUIDOverlapping($uid, $oe_kurzbz=null, $beginn=null, $ende=null) + { + $result = null; + + $qry = " + SELECT + dv.dienstverhaeltnis_id, + tbl_benutzer.uid, + tbl_mitarbeiter.personalnummer, + tbl_mitarbeiter.kurzbz, + tbl_mitarbeiter.lektor, + tbl_mitarbeiter.fixangestellt, + tbl_person.person_id, + tbl_benutzer.alias, + org.oe_kurzbz, + org.bezeichnung oe_bezeichnung, + dv.von, + dv.bis, + dv.vertragsart_kurzbz, + dv.updateamum, + dv.updatevon + FROM tbl_mitarbeiter + JOIN tbl_benutzer ON tbl_mitarbeiter.mitarbeiter_uid::text = tbl_benutzer.uid::text + JOIN tbl_person USING (person_id) + JOIN hr.tbl_dienstverhaeltnis dv ON(tbl_benutzer.uid::text = dv.mitarbeiter_uid::text) + JOIN public.tbl_organisationseinheit org USING(oe_kurzbz) + WHERE tbl_benutzer.uid=?"; + $data = array($uid); + + if(!is_null($oe_kurzbz)) + { + $qry.=" AND oe_kurzbz=?"; + $data[] = $oe_kurzbz; + } + + if (!is_null($beginn) && !is_null($ende)) + { + $qry.=" AND (?,?) OVERLAPS (dv.von, COALESCE(dv.bis, '2999-12-31'))"; + $data[] = $beginn; + $data[] = $ende; + } + + $qry .=" + ORDER BY dv.von desc + "; + + return $this->execQuery($qry, $data); + + } + + public function fetchDienstverhaeltnisse($unternehmen, $stichtag=null, $mitarbeiteruid=null) { + $where = "oe_kurzbz = " . $this->escape($unternehmen); + if( !is_null($stichtag) ) + { + $where .= " AND " . $this->escape($stichtag) . " BETWEEN COALESCE(von, '1970-01-01') AND COALESCE(bis, '2070-12-31')"; + } + if( !is_null($mitarbeiteruid) ) + { + $where .= " AND mitarbeiter_uid = " . $this->escape($mitarbeiteruid); + } + $res = $this->loadWhere($where); + $dvs = array(); + if(hasData($res) ) + { + $dvs = getData($res); + } + return $dvs; + } +} diff --git a/application/views/errors/json/html/error_404.php b/application/views/errors/json/html/error_404.php new file mode 100644 index 000000000..0caade2b1 --- /dev/null +++ b/application/views/errors/json/html/error_404.php @@ -0,0 +1,65 @@ + + + + +404 Page Not Found + + + +
+

+ +
+ + diff --git a/application/views/errors/json/html/error_db.php b/application/views/errors/json/html/error_db.php new file mode 100644 index 000000000..dce6a7572 --- /dev/null +++ b/application/views/errors/json/html/error_db.php @@ -0,0 +1,49 @@ +

', $msg); + +$msgs = []; + +$error = [ + 'heading' => $heading +]; + +/** NOTE(chris): extract Error Number and SQL + * @see: DB_driver.php:692 + */ +if (substr(current($msg), 0, 14) == 'Error Number: ') { + $code = substr(array_shift($msg), 14); + if ($code) + $error['code'] = (int)$code; + $msgs[] = array_shift($msg); + $error['sql'] = array_shift($msg); +} + +/** NOTE(chris): extract Line Number and Filename + * @see: DB_driver.php:1782 + * @see: DB_driver.php:1783 + */ +if (count($msg) >= 2) { + if (substr(end($msg), 0, 13) == 'Line Number: ' && substr(prev($msg), 0, 10) == 'Filename: ') { + $error['line'] = (int)substr(array_pop($msg), 13); + $error['filename'] = substr(array_pop($msg), 10); + } +} + +foreach ($msg as $m) + $msgs[] = $m; + + +if (count($msgs) == 1) + $error['message'] = current($msgs); +else + $error['messages'] = $msgs; + +$g_result->addError($error, FHCAPI_Controller::ERROR_TYPE_DB); +$g_result->setStatus(FHCAPI_Controller::STATUS_ERROR); diff --git a/application/views/errors/json/html/error_exception.php b/application/views/errors/json/html/error_exception.php new file mode 100644 index 000000000..7984bd13e --- /dev/null +++ b/application/views/errors/json/html/error_exception.php @@ -0,0 +1,27 @@ + $message, + 'class' => get_class($exception), + 'filename' => $exception->getFile(), + 'line' => $exception->getLine() +]; + +if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === true) { + $error['backtrace'] = []; + foreach (debug_backtrace() as $err) { + if (isset($err['file']) && strpos($err['file'], realpath(BASEPATH)) !== 0) { + $error['backtrace'][] = [ + 'file' => $err['file'], + 'line' => $err['line'], + 'function' => $err['function'] + ]; + } + } +} + +$g_result->addError($error, FHCAPI_Controller::ERROR_TYPE_EXCEPTION); +$g_result->setStatus(FHCAPI_Controller::STATUS_ERROR); diff --git a/application/views/errors/json/html/error_general.php b/application/views/errors/json/html/error_general.php new file mode 100644 index 000000000..e69494463 --- /dev/null +++ b/application/views/errors/json/html/error_general.php @@ -0,0 +1,20 @@ +

', $msg); + +$error = [ + 'heading' => $heading +]; +if (count($msg) == 1) + $error['message'] = current($msg); +else + $error['messages'] = $msg; + +$g_result->addError($error, FHCAPI_Controller::ERROR_TYPE_GENERAL); +$g_result->setStatus(FHCAPI_Controller::STATUS_ERROR); diff --git a/application/views/errors/json/html/error_php.php b/application/views/errors/json/html/error_php.php new file mode 100644 index 000000000..91f2abf3c --- /dev/null +++ b/application/views/errors/json/html/error_php.php @@ -0,0 +1,31 @@ + $message, + 'severity' => $severity, + 'filename' => $filepath, + 'line' => $line +]; + +if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === true) { + $error['backtrace'] = []; + foreach (debug_backtrace() as $err) { + if (isset($err['file']) && strpos($err['file'], realpath(BASEPATH)) !== 0) { + $error['backtrace'][] = [ + 'file' => $err['file'], + 'line' => $err['line'], + 'function' => $err['function'] + ]; + } + } +} + +// TODO(chris): change type with severity +$g_result->addError($error, 'php'); + +if (((E_ERROR | E_PARSE | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity) { + $g_result->setStatus('error'); +} diff --git a/application/views/lehre/Antrag/Create.php b/application/views/lehre/Antrag/Create.php index ace9902de..dcfd29e42 100755 --- a/application/views/lehre/Antrag/Create.php +++ b/application/views/lehre/Antrag/Create.php @@ -11,6 +11,7 @@ $sitesettings = array( 'customJSModules' => array('public/js/apps/lehre/Antrag.js'), 'customCSSs' => array( 'public/css/Fhc.css', + 'public/css/components/primevue.css', 'vendor/vuejs/vuedatepicker_css/main.css' ), 'customJSs' => array( @@ -31,9 +32,9 @@ $this->load->view(

array('public/js/apps/lehre/Antrag/Leitung.js'), 'customCSSs' => array( - 'public/css/Fhc.css' + 'public/css/Fhc.css', + 'public/css/components/primevue.css', ), 'customJSs' => array( ) diff --git a/application/views/lehre/Antrag/Student/List.php b/application/views/lehre/Antrag/Student/List.php index 84b44afad..e2a4511c5 100755 --- a/application/views/lehre/Antrag/Student/List.php +++ b/application/views/lehre/Antrag/Student/List.php @@ -10,7 +10,8 @@ $sitesettings = array( ), 'customJSModules' => array('public/js/apps/lehre/Antrag/Student.js'), 'customCSSs' => array( - 'public/css/Fhc.css' + 'public/css/Fhc.css', + 'public/css/components/primevue.css', ), 'customJSs' => array( ) @@ -38,7 +39,10 @@ $this->load->view(

p->t('studierendenantrag', 'calltoaction_' . $type); ?>


- + p->t('studierendenantrag', 'antrag_typ_' . $type); ?>
@@ -63,7 +67,16 @@ $this->load->view( studierendenantrag_id; ?> p->t('studierendenantrag', 'antrag_typ_' . $antrag->typ); ?> - status_bezeichnung; ?> + + status == Studierendenantragstatus_model::STATUS_PAUSE + && $antrag->status_insertvon == Studierendenantragstatus_model::INSERTVON_DEREGISTERED + ) + ? $this->p->t('studierendenantrag', 'status_stop') + : $antrag->status_bezeichnung; + ?> + studiensemester_kurzbz; ?> datum))->format('d.m.Y'); ?> datum_wiedereinstieg ? (new DateTime($antrag->datum_wiedereinstieg))->format('d.m.Y') : ''; ?> @@ -74,15 +87,32 @@ $this->load->view( - diff --git a/application/views/system/logs/testSearch.php b/application/views/system/logs/testSearch.php index 882b953f5..57ed0d48a 100755 --- a/application/views/system/logs/testSearch.php +++ b/application/views/system/logs/testSearch.php @@ -1,13 +1,12 @@ 'Test Search', - 'jquery3' => true, 'bootstrap5' => true, 'fontawesome6' => true, - 'tablesorter2' => true, + 'tabulator5' => true, + 'primevue3' => true, + 'axios027' => true, 'vue3' => true, - 'ajaxlib' => true, - 'jqueryui1' => true, 'filtercomponent' => true, 'navigationcomponent' => true, 'phrases' => array( @@ -17,8 +16,8 @@ 'customCSSs' => array( 'public/css/components/verticalsplit.css', 'public/css/components/searchbar.css', + 'public/css/components/primevue.css', ), - 'customJSs' => array('vendor/axios/axios/axios.min.js'), 'customJSModules' => array('public/js/apps/TestSearch.js') ); @@ -40,17 +39,17 @@
- + - + - +
diff --git a/cis/private/info/service_uebersicht.php b/cis/private/info/service_uebersicht.php index 27759e6c6..348a82b0d 100755 --- a/cis/private/info/service_uebersicht.php +++ b/cis/private/info/service_uebersicht.php @@ -45,25 +45,22 @@ echo ' - - - - - -'; + + + + + + '; + +const MOODLE_ADDON_KURZBZ = 'moodle'; // Load Addons to get Moodle_Path $addon_obj = new addon(); -if ($addon_obj->loadAddons()) + +// include moodle addon config if active +if ($addon_obj->checkActiveAddon(MOODLE_ADDON_KURZBZ) && file_exists('../../../addons/'.MOODLE_ADDON_KURZBZ.'/config.inc.php')) { - if (count($addon_obj->result) > 0) - { - foreach ($addon_obj->result as $row) - { - if (file_exists('../../../addons/'.$row->kurzbz.'/config.inc.php')) - include_once('../../../addons/'.$row->kurzbz.'/config.inc.php'); - } - } + include_once('../../../addons/'.MOODLE_ADDON_KURZBZ.'/config.inc.php'); } echo ' @@ -117,6 +114,7 @@ echo ' '; +$servicekategorie_arr = $service->getKategorieArray(); if($oe_kurzbz!='') { @@ -134,6 +132,7 @@ echo ' + @@ -159,6 +158,8 @@ foreach($service->result as $row) echo ''; //echo ''; //echo ''; + $title = (isset($servicekategorie_arr[$row->servicekategorie_kurzbz])?$servicekategorie_arr[$row->servicekategorie_kurzbz]:''); + echo ''; echo ''; + $anzahl_spalten = $this->db_num_fields($this->data); + for($spalte=0;$spalte<$anzahl_spalten;$spalte++) + { + $this->html.= ''; + $this->csv.='"'.$this->db_field_name($this->data,$spalte).'",'; + } + $this->html.= ''; + $this->csv=substr($this->csv,0,-1)."\n"; + while($row = $this->db_fetch_object($this->data)) + { + $this->html.= ''; + $anzahl_spalten = $this->db_num_fields($this->data); + + for($spalte=0;$spalte<$anzahl_spalten;$spalte++) + { + $name = $this->db_field_name($this->data,$spalte); + $this->html.= ''; + // Umwandeln von Punkt in Komma bei Float-Werten + if (is_numeric($row->$name)) + { + if (strpos($row->$name,'.') != false) + $row->$name = number_format($row->$name,2,",",""); + } + $this->csv.= '"'.$row->$name.'",'; + } + + $this->json[] = $row; + $this->html.= ''; + $this->csv=substr($this->csv,0,-1)."\n"; + $this->countRows++; + } + $this->html.= ''; + } + return true; + } + else + { + $this->errormsg= 'Zu dieser Statistik gibt es keine SQL Abfrage'; + return false; + } + } + + function getHtmlTable($id, $class='') + { + return '

'.$this->countRows.' Zeilen

'.$p->t("global/bezeichnung").' '.$p->t("services/leistung").' '.$p->t("services/design").''.$p->t("services/kritikalitaet").' '.$p->t("services/details").'
',$design,'',$betrieb,'',$operativ,'',$title,''.($row->content_id!=''?'Details':''); if (defined("ADDON_MOODLE_PATH")) echo ' '.($row->ext_id!=''?'Beschreibung':''); diff --git a/cis/private/lehre/anwesenheitsliste.php b/cis/private/lehre/anwesenheitsliste.php index 8b1f28408..0ba5531c8 100755 --- a/cis/private/lehre/anwesenheitsliste.php +++ b/cis/private/lehre/anwesenheitsliste.php @@ -62,7 +62,7 @@ $stsem = $_GET['stsem']; else die($p->t('anwesenheitsliste/studiensemesterIstUngueltig')); - + $covidhelper = new CovidHelper(); ?> "; - $qry = "SELECT *, tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester FROM lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id) JOIN lehre.tbl_lehrveranstaltung USING(lehrveranstaltung_id) - WHERE lehrveranstaltung_id='$lvid' AND studiensemester_kurzbz=".$db->db_add_param($stsem); - $qry = "SELECT *, tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester ,tbl_lehreinheit.lehrform_kurzbz FROM lehre.tbl_lehreinheit JOIN lehre.tbl_lehreinheitgruppe USING(lehreinheit_id) @@ -213,7 +210,7 @@ $covidhelper = new CovidHelper(); $covid_content = "".$covid_content."

".$p->t('anwesenheitsliste/covidstatuslisten')."

"; else $covid_content = ($covidhelper->isUdfDefined()) ? $p->t('anwesenheitsliste/keineStudentenVorhanden') : ''; - + if($aw_content!='') $aw_content = "".$aw_content."

".$p->t('anwesenheitsliste/anwesenheitslisten')."

"; else @@ -241,9 +238,9 @@ $covidhelper = new CovidHelper(); { $covid_content = ''; } - + echo " - + diff --git a/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php b/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php index 6dce2b6ae..a799c9fad 100755 --- a/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php +++ b/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php @@ -102,6 +102,22 @@ $noten_obj->getAll(); $sprachen = new sprache(); $sprachen->getAll(true); + +$noten_array = array(); +$js_noten=''; +foreach ($noten_obj->result as $row) +{ + $js_noten .= " noten_array['" . $row->note . "']='" . addslashes($row->bezeichnung) . "';\n"; + $noten_array[$row->note]['bezeichnung'] = $row->bezeichnung; + $noten_array[$row->note]['positiv'] = $row->positiv; + $noten_array[$row->note]['aktiv'] = $row->aktiv; + $noten_array[$row->note]['lehre'] = $row->lehre; + $noten_array[$row->note]['lkt_ueberschreibbar'] = $row->lkt_ueberschreibbar; + $noten_array[$row->note]['anmerkung'] = $row->anmerkung; + foreach ($sprachen->result as $s) + $noten_array[$row->note]['bezeichnung_mehrsprachig'][$s->sprache] = $row->bezeichnung_mehrsprachig[$s->sprache]; +} + $errormsg = ''; // eingetragene lv-gesamtnoten freigeben @@ -326,19 +342,7 @@ echo ' var noten_array = Array(); '; -$noten_array = array(); -foreach ($noten_obj->result as $row) -{ - echo " noten_array['" . $row->note . "']='" . addslashes($row->bezeichnung) . "';\n"; - $noten_array[$row->note]['bezeichnung'] = $row->bezeichnung; - $noten_array[$row->note]['positiv'] = $row->positiv; - $noten_array[$row->note]['aktiv'] = $row->aktiv; - $noten_array[$row->note]['lehre'] = $row->lehre; - $noten_array[$row->note]['lkt_ueberschreibbar'] = $row->lkt_ueberschreibbar; - $noten_array[$row->note]['anmerkung'] = $row->anmerkung; - foreach ($sprachen->result as $s) - $noten_array[$row->note]['bezeichnung_mehrsprachig'][$s->sprache] = $row->bezeichnung_mehrsprachig[$s->sprache]; -} +echo $js_noten; ?> @@ -806,16 +810,16 @@ foreach ($noten_obj->result as $row) for(row in rows) { linenumber++; - if( rows[row] == '' ) + if( rows[row] == '' ) { //skip empty lines continue; } zeile = rows[row].split(" "); - + if( zeile.length < 2 ) { - alertMsg = alertMsg + "Zeile " + linenumber + ': ' + alertMsg = alertMsg + "Zeile " + linenumber + ': ' + 'Zu wenig Paramter - 2 erforderlich. ' + 'Die Zeile wurde uebersprungen.' + "\n\n"; continue; @@ -917,36 +921,36 @@ foreach ($noten_obj->result as $row) for(row in rows) { linenumber++; - if( rows[row] == '' ) + if( rows[row] == '' ) { //skip empty lines continue; } zeile = rows[row].split(" "); - + if( zeile.length < 3 ) { - alertMsg = alertMsg + "Zeile " + linenumber + ': ' + alertMsg = alertMsg + "Zeile " + linenumber + ': ' + 'Zu wenig Paramter - 3 erforderlich. ' + 'Die Zeile wurde uebersprungen.' + "\n\n"; continue; } - + if( zeile[1] == '' && zeile[2] == '' ) { - // ignore lines just copied from excel + // ignore lines just copied from excel continue; } - - if( zeile[2] == '' ) + + if( zeile[2] == '' ) { alertMsg = alertMsg + "Zeile " + linenumber + ': ' + "Die Note oder Punkte fehlen. " + "Die Zeile wurde uebersprungen. \n\n"; - continue; + continue; } - - if (CIS_GESAMTNOTE_PUNKTE == false) + + if (CIS_GESAMTNOTE_PUNKTE == false) { // check for valid grades if (validGrades.indexOf(zeile[2]) === -1) @@ -958,7 +962,7 @@ foreach ($noten_obj->result as $row) } } - if( !zeile[1].match(/[0-9]{2}\.[0-9]{2}\.[0-9]{4}/) ) + if( !zeile[1].match(/[0-9]{2}\.[0-9]{2}\.[0-9]{4}/) ) { alertMsg = alertMsg + "Zeile " + linenumber + ': ' + "Das Datum "+zeile[1]+" fehlt oder ist nicht zulaessig. " diff --git a/cis/private/lehre/benotungstool/nachpruefungeintragen.php b/cis/private/lehre/benotungstool/nachpruefungeintragen.php index 873c0f173..55954fcda 100755 --- a/cis/private/lehre/benotungstool/nachpruefungeintragen.php +++ b/cis/private/lehre/benotungstool/nachpruefungeintragen.php @@ -251,7 +251,7 @@ else // deshalb wird hier versucht eine passende Lehreinheit zu ermitteln. $lehreinheit_id = getLehreinheit($db, $lvid, $student_uid, $stsem); - $response = savePruefung($lvid, $student_uid, $stsem, $lehreinheit_id, $datum, $typ, $note); + $response = savePruefung($lvid, $student_uid, $stsem, $lehreinheit_id, $datum, $typ, $note, $punkte); echo $response; } else diff --git a/cis/private/lehre/notenliste.xls.php b/cis/private/lehre/notenliste.xls.php index 63cf45d29..6c8db5246 100755 --- a/cis/private/lehre/notenliste.xls.php +++ b/cis/private/lehre/notenliste.xls.php @@ -114,12 +114,15 @@ else $format_highlight->setFgColor(15); $format_highlight->setBorder(1); $format_highlight->setBorderColor('white'); + $format_highlight->setAlign('left'); + $format_highlight->setNumFormat(49); $format_highlightright=& $workbook->addFormat(); $format_highlightright->setFgColor(15); $format_highlightright->setBorder(1); $format_highlightright->setBorderColor('white'); $format_highlightright->setAlign('right'); + $format_highlightright->setNumFormat(49); $format_highlightright_date=& $workbook->addFormat(); $format_highlightright_date->setFgColor(15); @@ -318,17 +321,23 @@ else $worksheet->write($lines,1,$elem->uid); $worksheet->write($lines,2,$elem->nachname.$inc); - $worksheet->write($lines,3,$elem->vorname); - //wenn Wahlname vorhanden überschreibt dieser den Vornamen - $worksheet->write($lines,3,$elem->wahlname); - $worksheet->write($lines,4,'="'.$elem->semester.$elem->verband.$elem->gruppe.'"'); - $worksheet->write($lines,5,'="'.trim($elem->matrikelnr).'"',$format_highlight); + if( NULL !== $elem->wahlname ) + { + //wenn Wahlname vorhanden überschreibt dieser den Vornamen + $worksheet->write($lines,3,$elem->wahlname); + } + else + { + $worksheet->write($lines,3,$elem->vorname); + } + $worksheet->write($lines,4,$elem->semester.$elem->verband.$elem->gruppe); + $worksheet->write($lines,5,trim($elem->matrikelnr),$format_highlight); $worksheet->write($lines,6, $note, $format_highlightright); // Nachprüfung if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN2') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN2) { - $worksheet->write($lines,8, '="'.trim($elem->matrikelnr).'"', $format_highlight); + $worksheet->write($lines,8, trim($elem->matrikelnr), $format_highlight); $pr = new Pruefung(); $pr->getPruefungen($elem->uid, "Termin2", $lvid, $sem); $output2 = $pr->result; @@ -349,7 +358,7 @@ else // Nachprüfung if (defined('CIS_GESAMTNOTE_PRUEFUNG_TERMIN3') && CIS_GESAMTNOTE_PRUEFUNG_TERMIN3) { - $worksheet->write($lines,12, '="'.trim($elem->matrikelnr).'"', $format_highlight); + $worksheet->write($lines,12, trim($elem->matrikelnr), $format_highlight); $pr = new Pruefung(); $pr->getPruefungen($elem->uid, "Termin3", $lvid, $sem); $output3 = $pr->result; diff --git a/cis/private/pdfExport.php b/cis/private/pdfExport.php index 6db6f885a..ad2bb1fae 100755 --- a/cis/private/pdfExport.php +++ b/cis/private/pdfExport.php @@ -65,6 +65,11 @@ $xsl_stg_kz = 0; $sign = false; +/* Signing on CIS disabled +if(isset($_GET['sign'])) + $sign = true; +*/ + // Direkte uebergabe des Studienganges dessen Vorlage verwendet werden soll if (isset($_GET['xsl_stg_kz'])) $xsl_stg_kz = $_GET['xsl_stg_kz']; @@ -298,22 +303,18 @@ if ((((isset($_GET["uid"]) && $user == $_GET["uid"])) || $rechte->isBerechtigt(' $dokument->setFilename($filename); - if (!$dokument->create($output)) - die($dokument->errormsg); - if ($sign === true) { - if ($dokument->sign($user)) - { - $dokument->output(); - } - else + if (!$dokument->sign($user)) { echo $dokument->errormsg; } } - else - $dokument->output(); + + if (!$dokument->create($output)) + die($dokument->errormsg); + + $dokument->output(); $dokument->close(); } else diff --git a/cis/private/tools/zeitaufzeichnung_projektliste.php b/cis/private/tools/zeitaufzeichnung_projektliste.php index c96e816df..817f266a2 100755 --- a/cis/private/tools/zeitaufzeichnung_projektliste.php +++ b/cis/private/tools/zeitaufzeichnung_projektliste.php @@ -60,7 +60,7 @@ if (isset($_GET['uid'])) $rechte = new benutzerberechtigung(); $rechte->getBerechtigungen($uid); - if ($rechte->isBerechtigt('admin') || (in_array($_GET['uid'], $untergebenen_arr))) + if ($rechte->isBerechtigt('admin') || $rechte->isBerechtigt('mitarbeiter/zeitsperre') || (in_array($_GET['uid'], $untergebenen_arr))) { $uid = $_GET['uid']; } diff --git a/config/vilesci.config-default.inc.php b/config/vilesci.config-default.inc.php index ddcf8d563..a372bc9da 100755 --- a/config/vilesci.config-default.inc.php +++ b/config/vilesci.config-default.inc.php @@ -283,7 +283,7 @@ define('FAS_DOPPELTE_BUCHUNGSTYPEN_CHECK', serialize( 'StudiengebuehrRestzahlung' => array('StudiengebuehrErhoeht', 'Studiengebuehr', 'StudiengebuehrRestzahlung'), 'OEH' => array('OEH') )) -)); +); // Spezialnoten die am Zeunigs und Diplomasupplement ignoriert werden define('ZEUGNISNOTE_NICHT_ANZEIGEN',serialize(array('iar', 'nz'))); @@ -295,4 +295,13 @@ define ('DEFAULT_LEHRMODUS','regulaer'); //Echter Dienstvertrag define ('DEFAULT_ECHTER_DIENSTVERTRAG',[103,110]); +//Buchungstypen die fix auf eine bestimmte Kostenstelle gebucht werden sollen +//Buchungstyp => Studiengang_kz +define('FAS_BUCHUNGSTYP_FIXE_KOSTENSTELLE', serialize( + array( + 'Test_1' => 0, + 'Test_2' => 2 + ) +)); + ?> diff --git a/content/fas.xul.php b/content/fas.xul.php index abe7f613e..29515eae6 100755 --- a/content/fas.xul.php +++ b/content/fas.xul.php @@ -141,7 +141,9 @@ foreach($addon_obj->result as $addon) + + @@ -518,6 +520,13 @@ foreach($addon_obj->result as $addon) command = "menu-dokumente-antrag-abmeldung:command" accesskey = "&menu-dokumente-antrag-abmeldung.accesskey;" /> + result as $addon) command = "menu-dokumente-antrag-unterbrechung:command" accesskey = "&menu-dokumente-antrag-unterbrechung.accesskey;" /> + diff --git a/content/mitarbeiter/mitarbeiterDBDML.php b/content/mitarbeiter/mitarbeiterDBDML.php index 5515710e6..465c545f8 100755 --- a/content/mitarbeiter/mitarbeiterDBDML.php +++ b/content/mitarbeiter/mitarbeiterDBDML.php @@ -280,7 +280,7 @@ if(!$error) if($_POST['neu']!='true') { - if(!$entwt->load($_POST['mitarbeiter_uid'],$_POST['studiengang_kz_old'])) + if(!$entwt->load($_POST['entwicklungsteam_id'])) { $error = true; $return = false; @@ -291,12 +291,6 @@ if(!$error) else { - if($entwt->exists($_POST['mitarbeiter_uid'],$_POST['studiengang_kz'])) - { - $error = true; - $errormsg = 'Es existiert bereits ein Eintrag fuer diesen Studiengang'; - $return = false; - } $entwt->new = true; $entwt->insertamum = date('Y-m-d H:i:s'); $entwt->insertvon = $user; @@ -304,6 +298,7 @@ if(!$error) if(!$error) { + $entwt->entwicklungsteam_id= $_POST['entwicklungsteam_id']; $entwt->mitarbeiter_uid = $_POST['mitarbeiter_uid']; $entwt->studiengang_kz = $_POST['studiengang_kz']; $entwt->studiengang_kz_old = $_POST['studiengang_kz_old']; @@ -328,15 +323,17 @@ if(!$error) elseif(isset($_POST['type']) && $_POST['type']=='entwicklungsteamdelete') { //Loescht einen Entwicklungsteameintrag + $entwicklungsteam_id = $_POST['entwicklungsteam_id']; + $entwt = new entwicklungsteam(); - if($entwt->delete($_POST['mitarbeiter_uid'],$_POST['studiengang_kz'])) + if($entwt->delete($entwicklungsteam_id)) { $return = true; } else { $return = false; - $errormsg = $entwt->errormsg; + $errormsg = $entwt->errormsg; } } elseif(isset($_POST['type']) && $_POST['type']=='buchungsave') @@ -803,6 +800,7 @@ if(!$error) $errormsg = $konto->errormsg; } else + { $error = false; $return = true; diff --git a/content/mitarbeiter/mitarbeiterfunktionoverlay.xul.php b/content/mitarbeiter/mitarbeiterfunktionoverlay.xul.php index 6a50145eb..dd685c5da 100755 --- a/content/mitarbeiter/mitarbeiterfunktionoverlay.xul.php +++ b/content/mitarbeiter/mitarbeiterfunktionoverlay.xul.php @@ -199,7 +199,7 @@ echo ''; class="sortDirectionIndicator" sort="rdf:http://www.technikum-wien.at/bisfunktion/rdf#sws" onclick="MitarbeiterTreeFunktionSort()"/> - '; - $anzahl_spalten = $this->db_num_fields($this->data); - for($spalte=0;$spalte<$anzahl_spalten;$spalte++) - { - $this->html.= ''; - $this->csv.='"'.$this->db_field_name($this->data,$spalte).'",'; - } - $this->html.= ''; - $this->csv=substr($this->csv,0,-1)."\n"; - while($row = $this->db_fetch_object($this->data)) - { - $this->html.= ''; - $anzahl_spalten = $this->db_num_fields($this->data); - - for($spalte=0;$spalte<$anzahl_spalten;$spalte++) - { - $name = $this->db_field_name($this->data,$spalte); - $this->html.= ''; - // Umwandeln von Punkt in Komma bei Float-Werten - if (is_numeric($row->$name)) - { - if (strpos($row->$name,'.') != false) - $row->$name = number_format($row->$name,2,",",""); - } - $this->csv.= '"'.$row->$name.'",'; - } - - $this->json[] = $row; - $this->html.= ''; - $this->csv=substr($this->csv,0,-1)."\n"; - $this->countRows++; - } - $this->html.= ''; - } - return true; - } - else - { - $this->errormsg= 'Zu dieser Statistik gibt es keine SQL Abfrage'; - return false; - } - } - - function getHtmlTable($id, $class='') - { - return '

'.$this->countRows.' Zeilen

$aw_content $covid_content
'.$this->convert_html_chars($this->db_field_name($this->data,$spalte)).'
'.$this->convert_html_chars($row->$name).'
'.$this->html.'
'; - } - - function getCSV() - { - return $this->csv; - } - - function writeCSV($filename, $delimiter=',', $enclosure='"') - { - $fh=fopen($filename,'w'); - - $fieldnames=array(); - for ($i=0; $i < $this->db_num_fields($this->data); $i++) - $fieldnames[]=$this->db_field_name($this->data,$i); - fputcsv($fh, $fieldnames, $delimiter, $enclosure); - $this->db_result_seek($this->data,0); - while ($row = $this->db_fetch_row($this->data)) - fputcsv($fh, $row, $delimiter, $enclosure); - fclose($fh); - return true; - } - - function getJSON() - { - return json_encode($this->json); - } - - function getArray() - { - return $this->json; - } - - /** - * - * Parst Variablen aus einem String und liefert diese als Array zurueck - * @param $value String mit Variablen - * z.B.: "Select * from tbl_person where person_id<'$person_id'" - * oder "../content/statistik/bewerberstatistik.php?stsem=$StSem&stg_kz=$stg_kz" - * - * @return Array mit den Variablennamen - */ - function parseVars($value) - { - $result = array(); - - $check = '/\$[0-9A-z]+/'; - preg_match_all($check, $value, $result); - $result = $result[0]; - $vars = array(); - for($i=0;$i, + * Andreas Oesterreicher + * Karl Burkhart . + */ +require_once(dirname(__FILE__).'/basis_db.class.php'); + +class statistik extends basis_db +{ + public $new; + public $statistik_obj=array(); + public $result=array(); + + public $statistik_kurzbz; + public $content_id; + public $bezeichnung; + public $url; + public $sql; + public $gruppe; + public $publish; + public $insertamum; + public $insertvon; + public $updateamum; + public $udpatevon; + public $berechtigung_kurzbz; + public $preferences; + + public $studiengang_kz; // integer + public $prestudent_id; // integer + public $geschlecht; // char(1) + public $studiensemester_kurzbz;// varchar(16) + public $ausbildungssemester;// smallint + + public $anzahl; //Hilfsvariable fuer Group BY Abfragen + + // Daten der Statistik + public $data; // DB ressource + public $html; + public $countRows; + public $csv; + public $json; + + /** + * Konstruktor + */ + public function __construct($statistik_kurzbz=null) + { + parent::__construct(); + + if(!is_null($statistik_kurzbz)) + $this->load($statistik_kurzbz); + else + $this->new=true; + } + + /** + * Laedt eine Statistik + * @param $statistik_kurzbz + */ + public function load($statistik_kurzbz) + { + $qry = "SELECT + * + FROM + public.tbl_statistik + WHERE + statistik_kurzbz = " . $this->db_add_param($statistik_kurzbz); + + if($result = $this->db_query($qry)) + { + if($row = $this->db_fetch_object($result)) + { + $this->statistik_kurzbz = $row->statistik_kurzbz; + $this->content_id = $row->content_id; + $this->bezeichnung = $row->bezeichnung; + $this->url = $row->url; + $this->sql = $row->sql; + $this->gruppe = $row->gruppe; + $this->publish = $this->db_parse_bool($row->publish); + $this->insertamum = $row->insertamum; + $this->insertvon = $row->insertvon; + $this->updateamum = $row->updateamum; + $this->udpatevon = $row->updatevon; + $this->berechtigung_kurzbz = $row->berechtigung_kurzbz; + $this->preferences = $row->preferences; + $this->new = false; + + return true; + } + else + { + $this->errormsg = 'Dieser Eintrag wurde nicht gefunden: ' . $statistik_kurzbz; + return false; + } + } + else + { + $this->errormsg = 'Fehler beim Laden der Daten'; + return false; + } + } + + /** + * Laedt alle Statistiken + * @return true wenn ok, sonst false + */ + public function getAll($order = FALSE) + { + $qry = 'SELECT * FROM public.tbl_statistik'; + + if($order) + $qry .= ' ORDER BY ' . $order; + + if($result = $this->db_query($qry)) + { + while($row = $this->db_fetch_object($result)) + { + $obj = new statistik(); + + $obj->statistik_kurzbz = $row->statistik_kurzbz; + $obj->content_id = $row->content_id; + $obj->bezeichnung = $row->bezeichnung; + $obj->url = $row->url; + $obj->sql = $row->sql; + $obj->gruppe = $row->gruppe; + $obj->publish = $this->db_parse_bool($row->publish); + $obj->insertamum = $row->insertamum; + $obj->insertvon = $row->insertvon; + $obj->updateamum = $row->updateamum; + $obj->updatevon = $row->updatevon; + $obj->berechtigung_kurzbz = $row->berechtigung_kurzbz; + $obj->preferences = $row->preferences; + + $this->result[] = $obj; + } + + return true; + } + else + { + $this->errormsg = 'Fehler beim Laden der Daten'; + return false; + } + } + /** + * Laedt alle Statistiken einer Gruppe, Parameter publish zum Filtern. + * @return true wenn ok, sonst false + */ + public function getGruppe($gruppe,$publish=null) + { + $qry = "SELECT * FROM public.tbl_statistik WHERE gruppe=".$this->db_add_param($gruppe); + if ($publish===true) + $qry.=' AND publish '; + elseif ($publish===false) + $qry.=' AND NOT publish '; + $qry.=' ORDER BY bezeichnung;'; + + if($result = $this->db_query($qry)) + { + while($row = $this->db_fetch_object($result)) + { + $obj = new statistik(); + + $obj->statistik_kurzbz = $row->statistik_kurzbz; + $obj->content_id = $row->content_id; + $obj->bezeichnung = $row->bezeichnung; + $obj->url = $row->url; + $obj->sql = $row->sql; + $obj->gruppe = $row->gruppe; + $obj->publish = $this->db_parse_bool($row->publish); + $obj->insertamum = $row->insertamum; + $obj->insertvon = $row->insertvon; + $obj->updateamum = $row->updateamum; + $obj->udpatevon = $row->updatevon; + $obj->berechtigung_kurzbz = $row->berechtigung_kurzbz; + $obj->preferences = $row->preferences; + + $this->result[] = $obj; + } + + return true; + } + else + { + $this->errormsg = 'Fehler beim Laden der Daten'; + return false; + } + } + /** + * Laedt alle Statistik Gruppen, Parameter publish zum Filtern. + * @return true wenn ok, sonst false + */ + public function getAnzahlGruppe($publish = null) + { + $qry = 'SELECT gruppe, count(*) AS anzahl FROM public.tbl_statistik '; + + if($publish === true) + { + $qry .= 'WHERE publish '; + } + elseif($publish === false) + { + $qry .= 'WHERE NOT publish '; + } + + $qry .= ' GROUP BY gruppe ORDER BY gruppe;'; + + if($result = $this->db_query($qry)) + { + while($row = $this->db_fetch_object($result)) + { + $obj = new statistik(); + + $obj->gruppe = $row->gruppe; + $obj->anzahl = $row->anzahl; + + $this->result[] = $obj; + } + + return true; + } + else + { + $this->errormsg = 'Fehler beim Laden der Daten'; + return false; + } + } + /** + * Speichert einen Statistik Datensatz + * @param $new boolean + * @return boolean true wenn ok false im Fehlerfalls + */ + public function save($new=null) + { + if(is_null($new)) + $new = $this->new; + + /* Da derzeit die statistik_kurzbz der primary key in der DB ist, + * darf er vorerst nur [a-zA-Z0-9_] (\w) enthalten. (bis auf autoincrement + * integer umgestellt ist) + */ + $this->statistik_kurzbz = preg_replace('/\W/', '', $this->statistik_kurzbz); + + if($new) + { + $qry = 'INSERT INTO public.tbl_statistik(statistik_kurzbz, content_id, bezeichnung, url, sql, + gruppe, publish, insertamum, insertvon, updateamum, updatevon, preferences, berechtigung_kurzbz) VALUES('. + $this->db_add_param($this->statistik_kurzbz).','. + $this->db_add_param($this->content_id,FHC_INTEGER).','. + $this->db_add_param($this->bezeichnung).','. + $this->db_add_param($this->url).','. + $this->db_add_param($this->sql).','. + $this->db_add_param($this->gruppe).','. + $this->db_add_param($this->publish, FHC_BOOLEAN).','. + $this->db_add_param($this->insertamum).','. + $this->db_add_param($this->insertvon).','. + $this->db_add_param($this->updateamum).','. + $this->db_add_param($this->updatevon).','. + $this->db_add_param($this->preferences).','. + $this->db_add_param($this->berechtigung_kurzbz).');'; + } + else + { + if($this->statistik_kurzbz_orig=='') + $this->statistik_kurzbz_orig=$this->statistik_kurzbz; + $qry = 'UPDATE public.tbl_statistik SET + content_id='.$this->db_add_param($this->content_id,FHC_INTEGER).','. + ' bezeichnung='.$this->db_add_param($this->bezeichnung).','. + ' statistik_kurzbz='.$this->db_add_param($this->statistik_kurzbz).','. + ' url='.$this->db_add_param($this->url).','. + ' sql='.$this->db_add_param($this->sql).','. + ' gruppe='.$this->db_add_param($this->gruppe).','. + ' publish='.$this->db_add_param($this->publish, FHC_BOOLEAN).','. + ' insertamum='.$this->db_add_param($this->insertamum).','. + ' insertvon='.$this->db_add_param($this->insertvon).','. + ' updateamum='.$this->db_add_param($this->updateamum).','. + ' updatevon='.$this->db_add_param($this->updatevon).','. + ' preferences='.$this->db_add_param($this->preferences).','. + ' berechtigung_kurzbz='.$this->db_add_param($this->berechtigung_kurzbz). + ' WHERE statistik_kurzbz='.$this->db_add_param($this->statistik_kurzbz_orig,FHC_STRING,false); + } + //echo $qry; + if($this->db_query($qry)) + { + return true; + } + else + { + $this->errormsg='Fehler beim Speichern der Daten'; + return false; + } + } + + /** + * Liefert ein Array mit den Menueeintraegen der Statistiken + * Mit dem Returnwert dieser Funktion wird die entsprechende Stelle im + * Menue ueberschrieben + * @return Array fuer Menue + */ + public function getMenueArray() + { + $arr = array(); + + $qry = "SELECT + * + FROM + public.tbl_statistik + ORDER BY gruppe, bezeichnung, statistik_kurzbz"; + + if($result = $this->db_query($qry)) + { + $lastgruppe=''; + while($row = $this->db_fetch_object($result)) + { + if($row->gruppe!='' && $row->gruppe!=$lastgruppe) + { + $arr[$row->gruppe]=array('name'=>$row->gruppe); + $lastgruppe=$row->gruppe; + } + if($row->gruppe!='') + { + $arr[$row->gruppe][$row->statistik_kurzbz]=array('name'=>$row->bezeichnung, 'link'=>APP_ROOT.'vilesci/statistik/statistik_frameset.php?statistik_kurzbz='.$row->statistik_kurzbz, 'target'=>'main'); + if($row->berechtigung_kurzbz!='') + $arr[$row->gruppe][$row->statistik_kurzbz]['permissions']=array($row->berechtigung_kurzbz); + } + else + { + $arr[$row->statistik_kurzbz]=array('name'=>$row->bezeichnung, 'link'=>APP_ROOT.'vilesci/statistik/statistik_frameset.php?statistik_kurzbz='.$row->statistik_kurzbz, 'target'=>'main'); + if($row->berechtigung_kurzbz!='') + $arr[$row->statistik_kurzbz]['permissions']=array($row->berechtigung_kurzbz); + } + } + } + return $arr; + } + + /** + * Loescht einen Eintrag + * + * @param $statistik_kurzbz + * @return true wenn ok, sonst false + */ + public function delete($statistik_kurzbz) + { + $qry = "DELETE FROM public.tbl_statistik WHERE statistik_kurzbz=".$this->db_add_param($statistik_kurzbz).";"; + + if($this->db_query($qry)) + { + return true; + } + else + { + $this->errormsg='Fehler beim Löschen des Eintrages'; + return false; + } + } + + + + /** + * Laedt bestimmte PreStudenten + * @param studiengang_kz KZ des Studienganges der zu Laden ist + * @param studiensemester_kurzbz Studiensemester + * @param ausbildungssemester KZ Ausbildungssemester + * @param datum_stichtag Stichtag im ISO-Format, Ergebniss filtert auf <= (kleiner,gleich) + * @return true wenn ok, false im Fehlerfall + */ + public function get_prestudenten($studiengang_kz, $studiensemester_kurzbz, $ausbildungssemester=null, $datum_stichtag=null) + { + if(!is_numeric($studiengang_kz)) + { + $this->errormsg = 'Studiengang_kz muss eine gueltige Zahl sein'; + return false; + } + + if($ausbildungssemester!='' && !is_numeric($ausbildungssemester)) + { + $this->errormsg = 'Ausbildungssemester muss eine gueltige Zahl sein'; + return false; + } + + // Neue Studenten ermitteln + $qry=" + SELECT + DISTINCT prestudent_id, geschlecht, studiengang_kz, ausbildungssemester, studiensemester_kurzbz + FROM + public.tbl_prestudent + JOIN public.tbl_prestudentstatus status USING (prestudent_id) + JOIN public.tbl_person USING (person_id) + WHERE + status_kurzbz='Student' + AND NOT EXISTS(SELECT 1 FROM public.tbl_prestudentstatus WHERE status_kurzbz='Student' AND datumdb_query($qry)) + { + while($row = $this->db_fetch_object($result)) + { + $stat_obj = new statistik(); + $stat_obj->studiengang_kz=$row->studiengang_kz; + $stat_obj->ausbildungssemester=$row->ausbildungssemester; + $stat_obj->prestudent_id=$row->prestudent_id; + $stat_obj->geschlecht=$row->geschlecht; + $stat_obj->studiensemester_kurzbz=$row->studiensemester_kurzbz; + $this->statistik_obj[]=$stat_obj; + } + } + else + { + $this->errormsg = 'Datensatz konnte nicht geladen werden'; + return false; + } + + return true; + } + + /** + * + * Liefert die DropOut Rate + * @param unknown_type $studiengang_kz + * @param unknown_type $studiensemester_kurzbz + * @param unknown_type $ausbildungssemester + * @param unknown_type $datum_stichtag + */ + public function get_DropOut($studiengang_kz, $studiensemester_kurzbz, $ausbildungssemester=null, $datum_stichtag=null) + { + $this->statistik_obj=array(); + + if(!is_numeric($studiengang_kz)) + { + $this->errormsg = 'Studiengang_kz muss eine gueltige Zahl sein'; + return false; + } + + if($ausbildungssemester!='' && !is_numeric($ausbildungssemester)) + { + $this->errormsg = 'Ausbildungssemester muss eine gueltige Zahl sein'; + return false; + } + + // Neue Studenten ermitteln + $qry="SELECT DISTINCT prestudent_id, geschlecht, studiengang_kz, ausbildungssemester, studiensemester_kurzbz + FROM tbl_prestudent JOIN tbl_prestudentstatus USING (prestudent_id) JOIN tbl_person USING (person_id) + WHERE (status_kurzbz='Abbrecher') + AND studiengang_kz=".$this->db_add_param($studiengang_kz); + if($ausbildungssemester!='') + $qry.=" AND ausbildungssemester=".$this->db_add_param($ausbildungssemester); + + $qry.=" AND (studiensemester_kurzbz=".$this->db_add_param($studiensemester_kurzbz); + if (!is_null($datum_stichtag)) + $qry.=" AND datum <=".$this->db_add_param($datum_stichtag); + $qry.=') '; + $qry.=" ORDER BY prestudent_id;"; + + if($result = $this->db_query($qry)) + { + while($row = $this->db_fetch_object($result)) + { + $stat_obj = new statistik(); + $stat_obj->studiengang_kz=$row->studiengang_kz; + $stat_obj->ausbildungssemester=$row->ausbildungssemester; + $stat_obj->prestudent_id=$row->prestudent_id; + $stat_obj->geschlecht=$row->geschlecht; + $stat_obj->studiensemester_kurzbz=$row->studiensemester_kurzbz; + $this->statistik_obj[]=$stat_obj; + } + } + else + { + $this->errormsg = 'Datensatz konnte nicht geladen werden'; + return false; + } + + return true; + } + + /** + * Laedt die Daten einer Statistik (derzeit nur SQL) + * @param $statistik_kurzbz + */ + public function loadData() + { + $this->html=''; + $this->csv=''; + $this->json=array(); + $this->countRows=0; + set_time_limit(600); + + // In case a decryption function is used then perform password substitution + $this->sql = $this->replaceSQLDecryptionPassword($this->sql); + + if($this->sql!='') + { + $sql = $this->sql; + + // Wenn im SQL ein $user vorkommt wird das durch den eingeloggten User ersetzt + if(strpos($sql, '$user')!==false) + { + $uid = get_uid(); + $sql = str_replace('$user',$this->db_add_param($uid),$sql); + } + + foreach($_REQUEST as $name=>$value) + { + // Inputs, die in eckigen Klammern stehen, werden als Array interpretiert + if (is_string($value) && substr($value, 0, 1) == '[' && substr($value, -1) == ']') + { + //Eckige Klammern entfernen und String aufsplitten + $value = substr($value, 1); + $value = substr($value, 0, -1); + $value = explode(',', $value); + } + if (is_array($value)) + { + $in = $this->db_implode4SQL($value); + $sql = str_replace('$'.$name,$in,$sql); + } + else + $sql = str_replace('$'.$name,$this->db_add_param($value),$sql); + } + if($this->data = $this->db_query($sql)) + { + $this->html.= '
'.$this->convert_html_chars($this->db_field_name($this->data,$spalte)).'
'.$this->convert_html_chars($row->$name).'
'.$this->html.'
'; + } + + function getCSV() + { + return $this->csv; + } + + function writeCSV($filename, $delimiter=',', $enclosure='"') + { + $fh=fopen($filename,'w'); + + $fieldnames=array(); + for ($i=0; $i < $this->db_num_fields($this->data); $i++) + $fieldnames[]=$this->db_field_name($this->data,$i); + fputcsv($fh, $fieldnames, $delimiter, $enclosure); + $this->db_result_seek($this->data,0); + while ($row = $this->db_fetch_row($this->data)) + fputcsv($fh, $row, $delimiter, $enclosure); + fclose($fh); + return true; + } + + function getJSON() + { + return json_encode($this->json); + } + + function getArray() + { + return $this->json; + } + + /** + * + * Parst Variablen aus einem String und liefert diese als Array zurueck + * @param $value String mit Variablen + * z.B.: "Select * from tbl_person where person_id<'$person_id'" + * oder "../content/statistik/bewerberstatistik.php?stsem=$StSem&stg_kz=$stg_kz" + * + * @return Array mit den Variablennamen + */ + function parseVars($value) + { + $result = array(); + + $check = '/\$[0-9A-z]+/'; + preg_match_all($check, $value, $result); + $result = $result[0]; + $vars = array(); + for($i=0;$i'Mitarbeiter','permissions'=>array('admin','mitarbeiter','support'), 'Übersicht'=>array('name'=>'Übersicht', 'link'=>'personen/lektor_uebersicht.php', 'target'=>'main'), 'Zeitsperren'=>array('name'=>'Zeitsperren/Urlaub', 'link'=>'personen/urlaubsverwaltung.php', 'target'=>'main','permissions'=>array('mitarbeiter/zeitsperre:begrenzt')), + 'Projektexport'=>array('name'=>'Projektexport', 'link'=>'personen/projektexport.php', 'target'=>'main','permissions'=>array('mitarbeiter/zeitsperre')), ), 'Betriebsmittel'=>array('name'=>'Betriebsmittel', 'link'=>'stammdaten/betriebsmittel_frameset.php', 'target'=>'main','permissions'=>array('basis/betriebsmittel')), 'AnwesenheitslistenBarcode'=>array('name'=>'Anwesenheitslisten mit Barcodes', 'link'=>'personen/anwesenheitslisten_barcode.php', 'target'=>'main','permissions'=>array('basis/person')), diff --git a/include/zgv.class.php b/include/zgv.class.php index 20e2ef353..45a4c0cc0 100755 --- a/include/zgv.class.php +++ b/include/zgv.class.php @@ -187,16 +187,16 @@ class zgv extends basis_db public function getAllDoktor($onlyAktiv = false) { $sprache = new sprache(); - $qry='SELECT *,'.$sprache->getSprachQuery('bezeichnung').' FROM bis.tbl_zgvdoktor;'; - + $qry='SELECT *,'.$sprache->getSprachQuery('bezeichnung').' FROM bis.tbl_zgvdoktor'; + if ($onlyAktiv === true) { $qry .= " WHERE aktiv"; } - + $qry .= " ORDER BY zgvdoktor_bez"; - - + + if($result = $this->db_query($qry)) { while($row= $this->db_fetch_object($result)) diff --git a/locale/de-AT/fas.dtd b/locale/de-AT/fas.dtd index 3fd593833..b5598d85b 100755 --- a/locale/de-AT/fas.dtd +++ b/locale/de-AT/fas.dtd @@ -219,10 +219,18 @@ + + + + + + + + diff --git a/locale/de-AT/services.php b/locale/de-AT/services.php index 1b03bc956..159efd2e9 100755 --- a/locale/de-AT/services.php +++ b/locale/de-AT/services.php @@ -5,6 +5,7 @@ $this->phrasen['services/details']='Details'; $this->phrasen['services/filtern']='Filtern'; $this->phrasen['services/leistung']='Leistung'; $this->phrasen['services/design']='Verantwortlich'; +$this->phrasen['services/kritikalitaet']='Kritikalität'; $this->phrasen['services/betrieb']='Betrieb'; $this->phrasen['services/operativ']='Operativ'; ?> diff --git a/locale/en-US/services.php b/locale/en-US/services.php index d3c0642fa..ac31031a9 100755 --- a/locale/en-US/services.php +++ b/locale/en-US/services.php @@ -5,6 +5,7 @@ $this->phrasen['services/details']='Details'; $this->phrasen['services/filtern']='Filter'; $this->phrasen['services/leistung']='Service'; $this->phrasen['services/design']='Design'; +$this->phrasen['services/kritikalitaet']='Criticality'; $this->phrasen['services/betrieb']='Running'; $this->phrasen['services/operativ']='Operating'; -?> \ No newline at end of file +?> diff --git a/public/css/Fhc.css b/public/css/Fhc.css index 23c2379ea..376206f6e 100755 --- a/public/css/Fhc.css +++ b/public/css/Fhc.css @@ -1,83 +1,100 @@ -.text-prewrap { +.fhc-header { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: baseline; + margin-bottom: 3rem; +} +.fhc-header > h1:first-child { + font-size: calc(1.325rem + .9vw); +} +.fhc-header > :first-child > small { + color: var(--bs-secondary); + font-size: .65em; + padding-inline-start: 1em; +} + +.fhc-alert.p-toast-center { + width: 35rem; + max-width: 100vw; +} +.fhc-alert.p-toast-top-right { + max-width: calc(100vw - 40px); +} +.fhc-alert.p-toast-top-right .p-toast-detail, +.fhc-alert.p-toast-center .p-toast-message-text .card { + white-space: pre-wrap; +} + +.text-preline { white-space: pre-line; } - -.accordion-button-primary { - background-color: #e7f1ff; - color: #0c63e4; -} -.accordion-button-primary:not(.collapsed) { - background-color: #cfe2ff; - color: #0a58ca; +.text-prewrap { + white-space: pre-wrap; } -.accordion-button-secondary { - background-color: #f0f1f2; - color: #616971; -} -.accordion-button-secondary:not(.collapsed) { - background-color: #e2e3e5; - color: #565e64; +.btn-p-0 { + padding: 0 .375rem; } -.accordion-button-success { - background-color: #e8f3ee; - color: #177a4c; -} -.accordion-button-success:not(.collapsed) { - background-color: #d1e7dd; - color: #146c43; +.z-1 { + z-index: 1; } -.accordion-button-info { - background-color: #e7fafe; - color: #0cb6d8; -} -.accordion-button-info:not(.collapsed) { - background-color: #cff4fc; - color: #0aa2c0; +.input-group > .input-group-item { + position: relative; + flex: 1 1 auto; + width: 1%; + min-width: 0; } -.accordion-button-warning { - background-color: #fff9e6; - color: #e6ae06; -} -.accordion-button-warning:not(.collapsed) { - background-color: #fff3cd; - color: #cc9a06; +.input-group > .input-group-item .form-control:focus, +.input-group > .input-group-item .form-select:focus { + z-index: 3; + position: relative; } -.accordion-button-danger { - background-color: #fcebec; - color: #c6303e; -} -.accordion-button-danger:not(.collapsed) { - background-color: #f8d7da; - color: #b02a37; +.input-group-lg > .input-group-item .form-control, +.input-group-lg > .input-group-item .form-select { + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: 0.3rem; } -.accordion-button-light { - background-color: #fefeff; - color: #dfe0e1; -} -.accordion-button-light:not(.collapsed) { - background-color: #fefefe; - color: #c6c7c8; + +.input-group-sm > .input-group-item .form-control, +.input-group-sm > .input-group-item .form-select { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: 0.2rem; } -.accordion-button-dark { - background-color: #e9e9ea; - color: #1e2125; -} -.accordion-button-dark:not(.collapsed) { - background-color: #d3d3d4; - color: #1a1e21; +.input-group-lg > .input-group-item .form-select, +.input-group-sm > .input-group-item .form-select { + padding-right: 3rem; } -.tabulator-edit-list .tabulator-edit-list-item { - background-color: white; +.input-group:not(.has-validation) > .input-group-item:not(:last-child) .form-control, +.input-group:not(.has-validation) > .input-group-item:not(:last-child) .form-select { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group.has-validation > .input-group-item:nth-last-child(n+3) .form-control, +.input-group.has-validation > .input-group-item:nth-last-child(n+3) .form-select { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group > .input-group-item:not(:first-child) .form-control, +.input-group > .input-group-item:not(:first-child) .form-select { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.form-control-color.is-invalid, +.was-validated .form-control-color:invalid, +.form-control-color.is-valid, +.was-validated .form-control-color:valid { + padding-right: .375rem; + background-image: none; } -.tabulator-edit-list .tabulator-edit-list-item:hover, -.tabulator-edit-list .tabulator-edit-list-item.active { - color: white; -} \ No newline at end of file diff --git a/public/css/Tabulator5.css b/public/css/Tabulator5.css index 48124335f..b755f0d76 100755 --- a/public/css/Tabulator5.css +++ b/public/css/Tabulator5.css @@ -1,4 +1,4 @@ -@import '../../vendor/olifolkerd/tabulator5/dist/css/tabulator_bootstrap5.min.css'; +@import '../../vendor/olifolkerd/tabulator5/dist/css/tabulator_simple.min.css'; /* Apply borders and background to Cell instead of the Row * otherwise frozen columns won't look good (columns behind @@ -38,3 +38,9 @@ .tabulator .tabulator-col-resize-handle:last-of-type { z-index: 999999; } + +.tabulator-cell .btn { + padding: 0 .5rem; + max-height: 22px; + min-width: 30px; +} diff --git a/public/css/components/FilterComponent.css b/public/css/components/FilterComponent.css index 1a6698890..50bfe561e 100755 --- a/public/css/components/FilterComponent.css +++ b/public/css/components/FilterComponent.css @@ -64,7 +64,12 @@ margin-top: 20px; } -.tabulator{ - font-size:1rem; +.tabulator { + font-size: 1rem; +} +.tabulator-cell .btn { + padding: 0 .375rem; + font-size: .875rem; + border-radius: .2rem; } diff --git a/public/css/components/NavigationComponent.css b/public/css/components/NavigationComponent.css index 2d64d9cdc..429a131b5 100755 --- a/public/css/components/NavigationComponent.css +++ b/public/css/components/NavigationComponent.css @@ -82,6 +82,7 @@ /* * To be moved outside */ +.navbar.navbar-left-side ~ *, #content { position: inherit; margin: 0 0 0 250px; diff --git a/public/css/components/Tabs.css b/public/css/components/Tabs.css new file mode 100644 index 000000000..7127869f0 --- /dev/null +++ b/public/css/components/Tabs.css @@ -0,0 +1,16 @@ +.nav-item.nav-link:focus { + box-shadow: 0 0 0 .24rem rgba(13,110,253,.25); + z-index: 1; + outline: 0; + position: relative; +} +.nav-item.nav-link:focus::after { + content: ""; + display: block; + position: absolute; + left: -.5rem; + right: -.5rem; + top: calc(100% + 1px); + background: white; + height: .25rem; +} diff --git a/public/css/components/primevue.css b/public/css/components/primevue.css new file mode 100644 index 000000000..6e27bbc10 --- /dev/null +++ b/public/css/components/primevue.css @@ -0,0 +1,5677 @@ +@import '../../../vendor/npm-asset/primevue/resources/primevue.min.css'; +@import '../../../vendor/npm-asset/primeicons/primeicons.css'; + +:root { + --surface-a:#ffffff; + --surface-b:#efefef; + --surface-c:#e9ecef; + --surface-d:#dee2e6; + --surface-e:#ffffff; + --surface-f:#ffffff; + --text-color:#212529; + --text-color-secondary:#6c757d; + --primary-color:#007bff; + --primary-color-text:#ffffff; + --font-family:-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + --surface-0: #ffffff; + --surface-50: #f9fafb; + --surface-100: #f8f9fa; + --surface-200: #e9ecef; + --surface-300: #dee2e6; + --surface-400: #ced4da; + --surface-500: #adb5bd; + --surface-600: #6c757d; + --surface-700: #495057; + --surface-800: #343a40; + --surface-900: #212529; + --gray-50: #f9fafb; + --gray-100: #f8f9fa; + --gray-200: #e9ecef; + --gray-300: #dee2e6; + --gray-400: #ced4da; + --gray-500: #adb5bd; + --gray-600: #6c757d; + --gray-700: #495057; + --gray-800: #343a40; + --gray-900: #212529; + --content-padding:1.25rem; + --inline-spacing:0.5rem; + --border-radius:4px; + --surface-ground:#efefef; + --surface-section:#ffffff; + --surface-card:#ffffff; + --surface-overlay:#ffffff; + --surface-border:#dee2e6; + --surface-hover:#e9ecef; + --focus-ring: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + --maskbg: rgba(0, 0, 0, 0.4); +} + +* { + box-sizing: border-box; +} + +.p-component { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 1rem; + font-weight: normal; +} + +.p-component-overlay { + background-color: rgba(0, 0, 0, 0.4); + transition-duration: 0.15s; +} + +.p-disabled, .p-component:disabled { + opacity: 0.65; +} + +.p-error { + color: #dc3545; +} + +.p-text-secondary { + color: #6c757d; +} + +.pi { + font-size: 1rem; +} + +.p-link { + font-size: 1rem; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + border-radius: 4px; +} +.p-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-component-overlay-enter { + animation: p-component-overlay-enter-animation 150ms forwards; +} + +.p-component-overlay-leave { + animation: p-component-overlay-leave-animation 150ms forwards; +} + +@keyframes p-component-overlay-enter-animation { + from { + background-color: transparent; + } + to { + background-color: var(--maskbg); + } +} +@keyframes p-component-overlay-leave-animation { + from { + background-color: var(--maskbg); + } + to { + background-color: transparent; + } +} + +:root { + --blue-50:#f3f8ff; + --blue-100:#c5dcff; + --blue-200:#97c1fe; + --blue-300:#69a5fe; + --blue-400:#3b8afd; + --blue-500:#0d6efd; + --blue-600:#0b5ed7; + --blue-700:#094db1; + --blue-800:#073d8b; + --blue-900:#052c65; + --green-50:#f4f9f6; + --green-100:#c8e2d6; + --green-200:#9ccbb5; + --green-300:#70b595; + --green-400:#459e74; + --green-500:#198754; + --green-600:#157347; + --green-700:#125f3b; + --green-800:#0e4a2e; + --green-900:#0a3622; + --yellow-50:#fffcf3; + --yellow-100:#fff0c3; + --yellow-200:#ffe494; + --yellow-300:#ffd965; + --yellow-400:#ffcd36; + --yellow-500:#ffc107; + --yellow-600:#d9a406; + --yellow-700:#b38705; + --yellow-800:#8c6a04; + --yellow-900:#664d03; + --cyan-50:#f3fcfe; + --cyan-100:#c5f2fb; + --cyan-200:#97e8f9; + --cyan-300:#69def6; + --cyan-400:#3bd4f3; + --cyan-500:#0dcaf0; + --cyan-600:#0baccc; + --cyan-700:#098da8; + --cyan-800:#076f84; + --cyan-900:#055160; + --pink-50:#fdf5f9; + --pink-100:#f5cee1; + --pink-200:#eda7ca; + --pink-300:#e681b3; + --pink-400:#de5a9b; + --pink-500:#d63384; + --pink-600:#b62b70; + --pink-700:#96245c; + --pink-800:#761c49; + --pink-900:#561435; + --indigo-50:#f7f3fe; + --indigo-100:#dac6fc; + --indigo-200:#bd98f9; + --indigo-300:#a06bf7; + --indigo-400:#833df4; + --indigo-500:#6610f2; + --indigo-600:#570ece; + --indigo-700:#470ba9; + --indigo-800:#380985; + --indigo-900:#290661; + --teal-50:#f4fcfa; + --teal-100:#c9f2e6; + --teal-200:#9fe8d2; + --teal-300:#75debf; + --teal-400:#4ad3ab; + --teal-500:#20c997; + --teal-600:#1bab80; + --teal-700:#168d6a; + --teal-800:#126f53; + --teal-900:#0d503c; + --orange-50:#fff9f3; + --orange-100:#ffe0c7; + --orange-200:#fec89a; + --orange-300:#feaf6d; + --orange-400:#fd9741; + --orange-500:#fd7e14; + --orange-600:#d76b11; + --orange-700:#b1580e; + --orange-800:#8b450b; + --orange-900:#653208; + --bluegray-50:#f8f9fb; + --bluegray-100:#e0e4ea; + --bluegray-200:#c7ced9; + --bluegray-300:#aeb9c8; + --bluegray-400:#95a3b8; + --bluegray-500:#7c8ea7; + --bluegray-600:#69798e; + --bluegray-700:#576375; + --bluegray-800:#444e5c; + --bluegray-900:#323943; + --purple-50:#f8f6fc; + --purple-100:#dcd2f0; + --purple-200:#c1aee4; + --purple-300:#a68ad9; + --purple-400:#8a66cd; + --purple-500:#6f42c1; + --purple-600:#5e38a4; + --purple-700:#4e2e87; + --purple-800:#3d246a; + --purple-900:#2c1a4d; + --red-50:#fdf5f6; + --red-100:#f7cfd2; + --red-200:#f0a8af; + --red-300:#e9828c; + --red-400:#e35b68; + --red-500:#dc3545; + --red-600:#bb2d3b; + --red-700:#9a2530; + --red-800:#791d26; + --red-900:#58151c; + --primary-50:#f2f8ff; + --primary-100:#c2dfff; + --primary-200:#91c6ff; + --primary-300:#61adff; + --primary-400:#3094ff; + --primary-500:#007bff; + --primary-600:#0069d9; + --primary-700:#0056b3; + --primary-800:#00448c; + --primary-900:#003166; +} + +.p-autocomplete .p-autocomplete-loader { + right: 0.75rem; +} +.p-autocomplete.p-autocomplete-dd .p-autocomplete-loader { + right: 3.107rem; +} +.p-autocomplete .p-autocomplete-multiple-container { + padding: 0.25rem 0.75rem; +} +.p-autocomplete .p-autocomplete-multiple-container:not(.p-disabled):hover { + border-color: #ced4da; +} +.p-autocomplete .p-autocomplete-multiple-container:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-input-token { + padding: 0.25rem 0; +} +.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-input-token input { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 1rem; + color: #212529; + padding: 0; + margin: 0; +} +.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token { + padding: 0.25rem 0.75rem; + margin-right: 0.5rem; + background: #dee2e6; + color: #212529; + border-radius: 16px; +} +.p-autocomplete .p-autocomplete-multiple-container .p-autocomplete-token .p-autocomplete-token-icon { + margin-left: 0.5rem; +} +.p-autocomplete.p-invalid.p-component > .p-inputtext { + border-color: #dc3545; +} + +.p-autocomplete-panel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + box-shadow: none; +} +.p-autocomplete-panel .p-autocomplete-items { + padding: 0.5rem 0; +} +.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item { + margin: 0; + padding: 0.5rem 1.5rem; + border: 0 none; + color: #212529; + background: transparent; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item:hover { + color: #212529; + background: #e9ecef; +} +.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-autocomplete-panel .p-autocomplete-items .p-autocomplete-item-group { + margin: 0; + padding: 0.75rem 1rem; + color: #212529; + background: #ffffff; + font-weight: 600; +} + +.p-calendar.p-invalid.p-component > .p-inputtext { + border-color: #dc3545; +} + +.p-datepicker { + padding: 0; + background: #ffffff; + color: #212529; + border: 1px solid #ced4da; + border-radius: 4px; +} +.p-datepicker:not(.p-datepicker-inline) { + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; +} +.p-datepicker:not(.p-datepicker-inline) .p-datepicker-header { + background: #efefef; +} +.p-datepicker .p-datepicker-header { + padding: 0.5rem; + color: #212529; + background: #ffffff; + font-weight: 600; + margin: 0; + border-bottom: 1px solid #dee2e6; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-datepicker .p-datepicker-header .p-datepicker-prev, +.p-datepicker .p-datepicker-header .p-datepicker-next { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-datepicker .p-datepicker-header .p-datepicker-prev:enabled:hover, +.p-datepicker .p-datepicker-header .p-datepicker-next:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-datepicker .p-datepicker-header .p-datepicker-prev:focus, +.p-datepicker .p-datepicker-header .p-datepicker-next:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-datepicker .p-datepicker-header .p-datepicker-title { + line-height: 2rem; +} +.p-datepicker .p-datepicker-header .p-datepicker-title .p-datepicker-year, +.p-datepicker .p-datepicker-header .p-datepicker-title .p-datepicker-month { + color: #212529; + transition: box-shadow 0.15s; + font-weight: 600; + padding: 0.5rem; +} +.p-datepicker .p-datepicker-header .p-datepicker-title .p-datepicker-year:enabled:hover, +.p-datepicker .p-datepicker-header .p-datepicker-title .p-datepicker-month:enabled:hover { + color: #007bff; +} +.p-datepicker .p-datepicker-header .p-datepicker-title .p-datepicker-month { + margin-right: 0.5rem; +} +.p-datepicker table { + font-size: 1rem; + margin: 0.5rem 0; +} +.p-datepicker table th { + padding: 0.5rem; +} +.p-datepicker table th > span { + width: 2.5rem; + height: 2.5rem; +} +.p-datepicker table td { + padding: 0.5rem; +} +.p-datepicker table td > span { + width: 2.5rem; + height: 2.5rem; + border-radius: 4px; + transition: box-shadow 0.15s; + border: 1px solid transparent; +} +.p-datepicker table td > span.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-datepicker table td > span:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-datepicker table td.p-datepicker-today > span { + background: #ced4da; + color: #212529; + border-color: transparent; +} +.p-datepicker table td.p-datepicker-today > span.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-datepicker .p-datepicker-buttonbar { + padding: 1rem 0; + border-top: 1px solid #dee2e6; +} +.p-datepicker .p-datepicker-buttonbar .p-button { + width: auto; +} +.p-datepicker .p-timepicker { + border-top: 1px solid #dee2e6; + padding: 0.5rem; +} +.p-datepicker .p-timepicker button { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-datepicker .p-timepicker button:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-datepicker .p-timepicker button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-datepicker .p-timepicker button:last-child { + margin-top: 0.2em; +} +.p-datepicker .p-timepicker span { + font-size: 1.25rem; +} +.p-datepicker .p-timepicker > div { + padding: 0 0.5rem; +} +.p-datepicker.p-datepicker-timeonly .p-timepicker { + border-top: 0 none; +} +.p-datepicker .p-monthpicker { + margin: 0.5rem 0; +} +.p-datepicker .p-monthpicker .p-monthpicker-month { + padding: 0.5rem; + transition: box-shadow 0.15s; + border-radius: 4px; +} +.p-datepicker .p-monthpicker .p-monthpicker-month.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-datepicker .p-yearpicker { + margin: 0.5rem 0; +} +.p-datepicker .p-yearpicker .p-yearpicker-year { + padding: 0.5rem; + transition: box-shadow 0.15s; + border-radius: 4px; +} +.p-datepicker .p-yearpicker .p-yearpicker-year.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-datepicker.p-datepicker-multiple-month .p-datepicker-group { + border-left: 1px solid #dee2e6; + padding-right: 0; + padding-left: 0; + padding-top: 0; + padding-bottom: 0; +} +.p-datepicker.p-datepicker-multiple-month .p-datepicker-group:first-child { + padding-left: 0; + border-left: 0 none; +} +.p-datepicker.p-datepicker-multiple-month .p-datepicker-group:last-child { + padding-right: 0; +} +.p-datepicker:not(.p-disabled) table td span:not(.p-highlight):not(.p-disabled):hover { + background: #e9ecef; +} +.p-datepicker:not(.p-disabled) table td span:not(.p-highlight):not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-datepicker:not(.p-disabled) .p-monthpicker .p-monthpicker-month:not(.p-disabled):not(.p-highlight):hover { + background: #e9ecef; +} +.p-datepicker:not(.p-disabled) .p-monthpicker .p-monthpicker-month:not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-datepicker:not(.p-disabled) .p-yearpicker .p-yearpicker-year:not(.p-disabled):not(.p-highlight):hover { + background: #e9ecef; +} +.p-datepicker:not(.p-disabled) .p-yearpicker .p-yearpicker-year:not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +@media screen and (max-width: 769px) { + .p-datepicker table th, .p-datepicker table td { + padding: 0; + } +} +.p-cascadeselect { + background: #ffffff; + border: 1px solid #ced4da; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.p-cascadeselect:not(.p-disabled):hover { + border-color: #ced4da; +} +.p-cascadeselect:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-cascadeselect .p-cascadeselect-label { + background: transparent; + border: 0 none; + padding: 0.5rem 0.75rem; +} +.p-cascadeselect .p-cascadeselect-label.p-placeholder { + color: #6c757d; +} +.p-cascadeselect .p-cascadeselect-label:enabled:focus { + outline: 0 none; + box-shadow: none; +} +.p-cascadeselect .p-cascadeselect-trigger { + background: transparent; + color: #495057; + width: 2.357rem; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.p-cascadeselect.p-invalid.p-component { + border-color: #dc3545; +} + +.p-cascadeselect-panel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + box-shadow: none; +} +.p-cascadeselect-panel .p-cascadeselect-items { + padding: 0.5rem 0; +} +.p-cascadeselect-panel .p-cascadeselect-items .p-cascadeselect-item { + margin: 0; + border: 0 none; + color: #212529; + background: transparent; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-cascadeselect-panel .p-cascadeselect-items .p-cascadeselect-item .p-cascadeselect-item-content { + padding: 0.5rem 1.5rem; +} +.p-cascadeselect-panel .p-cascadeselect-items .p-cascadeselect-item .p-cascadeselect-item-content:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-cascadeselect-panel .p-cascadeselect-items .p-cascadeselect-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-cascadeselect-panel .p-cascadeselect-items .p-cascadeselect-item:not(.p-highlight):not(.p-disabled):hover { + color: #212529; + background: #e9ecef; +} +.p-cascadeselect-panel .p-cascadeselect-items .p-cascadeselect-item .p-cascadeselect-group-icon { + font-size: 0.875rem; +} + +.p-input-filled .p-cascadeselect { + background: #efefef; +} +.p-input-filled .p-cascadeselect:not(.p-disabled):hover { + background-color: #efefef; +} +.p-input-filled .p-cascadeselect:not(.p-disabled).p-focus { + background-color: #efefef; +} + +.p-checkbox { + width: 20px; + height: 20px; +} +.p-checkbox .p-checkbox-box { + border: 2px solid #ced4da; + background: #ffffff; + width: 20px; + height: 20px; + color: #212529; + border-radius: 4px; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-checkbox .p-checkbox-box .p-checkbox-icon { + transition-duration: 0.15s; + color: #ffffff; + font-size: 14px; +} +.p-checkbox .p-checkbox-box.p-highlight { + border-color: #007bff; + background: #007bff; +} +.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover { + border-color: #ced4da; +} +.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-highlight:hover { + border-color: #0062cc; + background: #0062cc; + color: #ffffff; +} +.p-checkbox.p-invalid > .p-checkbox-box { + border-color: #dc3545; +} + +.p-input-filled .p-checkbox .p-checkbox-box { + background-color: #efefef; +} +.p-input-filled .p-checkbox .p-checkbox-box.p-highlight { + background: #007bff; +} +.p-input-filled .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover { + background-color: #efefef; +} +.p-input-filled .p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-highlight:hover { + background: #0062cc; +} + +.p-highlight .p-checkbox .p-checkbox-box { + border-color: #ffffff; +} + +.p-chips .p-chips-multiple-container { + padding: 0.25rem 0.75rem; +} +.p-chips .p-chips-multiple-container:not(.p-disabled):hover { + border-color: #ced4da; +} +.p-chips .p-chips-multiple-container:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-chips .p-chips-multiple-container .p-chips-token { + padding: 0.25rem 0.75rem; + margin-right: 0.5rem; + background: #dee2e6; + color: #212529; + border-radius: 16px; +} +.p-chips .p-chips-multiple-container .p-chips-token .p-chips-token-icon { + margin-left: 0.5rem; +} +.p-chips .p-chips-multiple-container .p-chips-input-token { + padding: 0.25rem 0; +} +.p-chips .p-chips-multiple-container .p-chips-input-token input { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 1rem; + color: #212529; + padding: 0; + margin: 0; +} +.p-chips.p-invalid.p-component > .p-inputtext { + border-color: #dc3545; +} + +.p-colorpicker-preview { + width: 2rem; + height: 2rem; +} + +.p-colorpicker-panel { + background: #212529; + border: 1px solid #212529; +} +.p-colorpicker-panel .p-colorpicker-color-handle, +.p-colorpicker-panel .p-colorpicker-hue-handle { + border-color: #ffffff; +} + +.p-colorpicker-overlay-panel { + box-shadow: none; +} + +.p-dropdown { + background: #ffffff; + border: 1px solid #ced4da; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.p-dropdown:not(.p-disabled):hover { + border-color: #ced4da; +} +.p-dropdown:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-dropdown.p-dropdown-clearable .p-dropdown-label { + padding-right: 1.75rem; +} +.p-dropdown .p-dropdown-label { + background: transparent; + border: 0 none; +} +.p-dropdown .p-dropdown-label.p-placeholder { + color: #6c757d; +} +.p-dropdown .p-dropdown-label:enabled:focus { + outline: 0 none; + box-shadow: none; +} +.p-dropdown .p-dropdown-trigger { + background: transparent; + color: #495057; + width: 2.357rem; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.p-dropdown .p-dropdown-clear-icon { + color: #495057; + right: 2.357rem; +} +.p-dropdown.p-invalid.p-component { + border-color: #dc3545; +} + +.p-dropdown-panel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + box-shadow: none; +} +.p-dropdown-panel .p-dropdown-header { + padding: 0.75rem 1.5rem; + border-bottom: 1px solid #dee2e6; + color: #212529; + background: #efefef; + margin: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-dropdown-panel .p-dropdown-header .p-dropdown-filter { + padding-right: 1.75rem; + margin-right: -1.75rem; +} +.p-dropdown-panel .p-dropdown-header .p-dropdown-filter-icon { + right: 0.75rem; + color: #495057; +} +.p-dropdown-panel .p-dropdown-items { + padding: 0.5rem 0; +} +.p-dropdown-panel .p-dropdown-items .p-dropdown-item { + margin: 0; + padding: 0.5rem 1.5rem; + border: 0 none; + color: #212529; + background: transparent; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-dropdown-panel .p-dropdown-items .p-dropdown-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-dropdown-panel .p-dropdown-items .p-dropdown-item:not(.p-highlight):not(.p-disabled):hover { + color: #212529; + background: #e9ecef; +} +.p-dropdown-panel .p-dropdown-items .p-dropdown-item-group { + margin: 0; + padding: 0.75rem 1rem; + color: #212529; + background: #ffffff; + font-weight: 600; +} +.p-dropdown-panel .p-dropdown-items .p-dropdown-empty-message { + padding: 0.5rem 1.5rem; + color: #212529; + background: transparent; +} + +.p-input-filled .p-dropdown { + background: #efefef; +} +.p-input-filled .p-dropdown:not(.p-disabled):hover { + background-color: #efefef; +} +.p-input-filled .p-dropdown:not(.p-disabled).p-focus { + background-color: #efefef; +} +.p-input-filled .p-dropdown:not(.p-disabled).p-focus .p-inputtext { + background-color: transparent; +} + +.p-editor-container .p-editor-toolbar { + background: #efefef; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-editor-container .p-editor-toolbar.ql-snow { + border: 1px solid #dee2e6; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-stroke { + stroke: #6c757d; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-fill { + fill: #6c757d; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker .ql-picker-label { + border: 0 none; + color: #6c757d; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker .ql-picker-label:hover { + color: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker .ql-picker-label:hover .ql-stroke { + stroke: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker .ql-picker-label:hover .ql-fill { + fill: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label { + color: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke { + stroke: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill { + fill: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options { + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + border-radius: 4px; + padding: 0.5rem 0; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options .ql-picker-item { + color: #212529; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options .ql-picker-item:hover { + color: #212529; + background: #e9ecef; +} +.p-editor-container .p-editor-toolbar.ql-snow .ql-picker.ql-expanded:not(.ql-icon-picker) .ql-picker-item { + padding: 0.5rem 1.5rem; +} +.p-editor-container .p-editor-content { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-editor-container .p-editor-content.ql-snow { + border: 1px solid #dee2e6; +} +.p-editor-container .p-editor-content .ql-editor { + background: #ffffff; + color: #495057; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-editor-container .ql-snow.ql-toolbar button:hover, +.p-editor-container .ql-snow.ql-toolbar button:focus { + color: #212529; +} +.p-editor-container .ql-snow.ql-toolbar button:hover .ql-stroke, +.p-editor-container .ql-snow.ql-toolbar button:focus .ql-stroke { + stroke: #212529; +} +.p-editor-container .ql-snow.ql-toolbar button:hover .ql-fill, +.p-editor-container .ql-snow.ql-toolbar button:focus .ql-fill { + fill: #212529; +} +.p-editor-container .ql-snow.ql-toolbar button.ql-active, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-label.ql-active, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-item.ql-selected { + color: #007bff; +} +.p-editor-container .ql-snow.ql-toolbar button.ql-active .ql-stroke, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke { + stroke: #007bff; +} +.p-editor-container .ql-snow.ql-toolbar button.ql-active .ql-fill, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill { + fill: #007bff; +} +.p-editor-container .ql-snow.ql-toolbar button.ql-active .ql-picker-label, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-picker-label, +.p-editor-container .ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-picker-label { + color: #007bff; +} + +.p-inputgroup-addon { + background: #e9ecef; + color: #495057; + border-top: 1px solid #ced4da; + border-left: 1px solid #ced4da; + border-bottom: 1px solid #ced4da; + padding: 0.5rem 0.75rem; + min-width: 2.357rem; +} +.p-inputgroup-addon:last-child { + border-right: 1px solid #ced4da; +} + +.p-inputgroup > .p-component, +.p-inputgroup > .p-inputwrapper > .p-inputtext, +.p-inputgroup > .p-float-label > .p-component { + border-radius: 0; + margin: 0; +} +.p-inputgroup > .p-component + .p-inputgroup-addon, +.p-inputgroup > .p-inputwrapper > .p-inputtext + .p-inputgroup-addon, +.p-inputgroup > .p-float-label > .p-component + .p-inputgroup-addon { + border-left: 0 none; +} +.p-inputgroup > .p-component:focus, +.p-inputgroup > .p-inputwrapper > .p-inputtext:focus, +.p-inputgroup > .p-float-label > .p-component:focus { + z-index: 1; +} +.p-inputgroup > .p-component:focus ~ label, +.p-inputgroup > .p-inputwrapper > .p-inputtext:focus ~ label, +.p-inputgroup > .p-float-label > .p-component:focus ~ label { + z-index: 1; +} + +.p-inputgroup-addon:first-child, +.p-inputgroup button:first-child, +.p-inputgroup input:first-child, +.p-inputgroup > .p-inputwrapper:first-child, +.p-inputgroup > .p-inputwrapper:first-child > .p-inputtext { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.p-inputgroup .p-float-label:first-child input { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.p-inputgroup-addon:last-child, +.p-inputgroup button:last-child, +.p-inputgroup input:last-child, +.p-inputgroup > .p-inputwrapper:last-child, +.p-inputgroup > .p-inputwrapper:last-child > .p-inputtext { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.p-inputgroup .p-float-label:last-child input { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.p-fluid .p-inputgroup .p-button { + width: auto; +} +.p-fluid .p-inputgroup .p-button.p-button-icon-only { + width: 2.357rem; +} + +.p-inputnumber.p-invalid.p-component > .p-inputtext { + border-color: #dc3545; +} + +.p-inputswitch { + width: 3rem; + height: 1.75rem; +} +.p-inputswitch .p-inputswitch-slider { + background: #ced4da; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.p-inputswitch .p-inputswitch-slider:before { + background: #ffffff; + width: 1.25rem; + height: 1.25rem; + left: 0.25rem; + margin-top: -0.625rem; + border-radius: 4px; + transition-duration: 0.15s; +} +.p-inputswitch.p-inputswitch-checked .p-inputswitch-slider:before { + transform: translateX(1.25rem); +} +.p-inputswitch.p-focus .p-inputswitch-slider { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-inputswitch:not(.p-disabled):hover .p-inputswitch-slider { + background: #ced4da; +} +.p-inputswitch.p-inputswitch-checked .p-inputswitch-slider { + background: #007bff; +} +.p-inputswitch.p-inputswitch-checked .p-inputswitch-slider:before { + background: #ffffff; +} +.p-inputswitch.p-inputswitch-checked:not(.p-disabled):hover .p-inputswitch-slider { + background: #007bff; +} +.p-inputswitch.p-invalid { + border-color: #dc3545; +} + + +.p-float-label > label { + left: 0.75rem; + color: #6c757d; + transition-duration: 0.15s; +} + +.p-float-label > .p-invalid + label { + color: #dc3545; +} + +.p-input-icon-left > i:first-of-type { + left: 0.75rem; + color: #495057; +} + +.p-input-icon-left > .p-inputtext { + padding-left: 2.5rem; +} + +.p-input-icon-left.p-float-label > label { + left: 2.5rem; +} + +.p-input-icon-right > i:last-of-type { + right: 0.75rem; + color: #495057; +} + +.p-input-icon-right > .p-inputtext { + padding-right: 2.5rem; +} + +::-webkit-input-placeholder { + color: #6c757d; +} + +:-moz-placeholder { + color: #6c757d; +} + +::-moz-placeholder { + color: #6c757d; +} + +:-ms-input-placeholder { + color: #6c757d; +} + +.p-input-filled .p-inputtext { + background-color: #efefef; +} +.p-input-filled .p-inputtext:enabled:hover { + background-color: #efefef; +} +.p-input-filled .p-inputtext:enabled:focus { + background-color: #efefef; +} + +.p-inputtext-sm .p-inputtext { + font-size: 0.875rem; + padding: 0.4375rem 0.65625rem; +} + +.p-inputtext-lg .p-inputtext { + font-size: 1.25rem; + padding: 0.625rem 0.9375rem; +} + +.p-listbox { + background: #ffffff; + color: #212529; + border: 1px solid #ced4da; + border-radius: 4px; +} +.p-listbox .p-listbox-header { + padding: 0.75rem 1.5rem; + border-bottom: 1px solid #dee2e6; + color: #212529; + background: #efefef; + margin: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-listbox .p-listbox-header .p-listbox-filter { + padding-right: 1.75rem; +} +.p-listbox .p-listbox-header .p-listbox-filter-icon { + right: 0.75rem; + color: #495057; +} +.p-listbox .p-listbox-list { + padding: 0.5rem 0; +} +.p-listbox .p-listbox-list .p-listbox-item { + margin: 0; + padding: 0.5rem 1.5rem; + border: 0 none; + color: #212529; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-listbox .p-listbox-list .p-listbox-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-listbox .p-listbox-list .p-listbox-item:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-listbox .p-listbox-list .p-listbox-item-group { + margin: 0; + padding: 0.75rem 1rem; + color: #212529; + background: #ffffff; + font-weight: 600; +} +.p-listbox .p-listbox-list .p-listbox-empty-message { + padding: 0.5rem 1.5rem; + color: #212529; + background: transparent; +} +.p-listbox:not(.p-disabled) .p-listbox-item:not(.p-highlight):not(.p-disabled):hover { + color: #212529; + background: #e9ecef; +} +.p-listbox.p-invalid { + border-color: #dc3545; +} + +.p-multiselect { + background: #ffffff; + border: 1px solid #ced4da; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.p-multiselect:not(.p-disabled):hover { + border-color: #ced4da; +} +.p-multiselect:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-multiselect .p-multiselect-label { + padding: 0.5rem 0.75rem; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-multiselect .p-multiselect-label.p-placeholder { + color: #6c757d; +} +.p-multiselect.p-multiselect-chip .p-multiselect-token { + padding: 0.25rem 0.75rem; + margin-right: 0.5rem; + background: #dee2e6; + color: #212529; + border-radius: 16px; +} +.p-multiselect.p-multiselect-chip .p-multiselect-token .p-multiselect-token-icon { + margin-left: 0.5rem; +} +.p-multiselect .p-multiselect-trigger { + background: transparent; + color: #495057; + width: 2.357rem; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.p-multiselect.p-invalid.p-component { + border-color: #dc3545; +} + +.p-inputwrapper-filled.p-multiselect.p-multiselect-chip .p-multiselect-label { + padding: 0.25rem 0.75rem; +} + +.p-multiselect-panel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + box-shadow: none; +} +.p-multiselect-panel .p-multiselect-header { + padding: 0.75rem 1.5rem; + border-bottom: 1px solid #dee2e6; + color: #212529; + background: #efefef; + margin: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-inputtext { + padding-right: 1.75rem; +} +.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-multiselect-filter-icon { + right: 0.75rem; + color: #495057; +} +.p-multiselect-panel .p-multiselect-header .p-checkbox { + margin-right: 0.5rem; +} +.p-multiselect-panel .p-multiselect-header .p-multiselect-close { + margin-left: 0.5rem; + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-multiselect-panel .p-multiselect-header .p-multiselect-close:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-multiselect-panel .p-multiselect-header .p-multiselect-close:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-multiselect-panel .p-multiselect-items { + padding: 0.5rem 0; +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-item { + margin: 0; + padding: 0.5rem 1.5rem; + border: 0 none; + color: #212529; + background: transparent; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-item:not(.p-highlight):not(.p-disabled):hover { + color: #212529; + background: #e9ecef; +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-item:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-item .p-checkbox { + margin-right: 0.5rem; +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-item-group { + margin: 0; + padding: 0.75rem 1rem; + color: #212529; + background: #ffffff; + font-weight: 600; +} +.p-multiselect-panel .p-multiselect-items .p-multiselect-empty-message { + padding: 0.5rem 1.5rem; + color: #212529; + background: transparent; +} + +.p-input-filled .p-multiselect { + background: #efefef; +} +.p-input-filled .p-multiselect:not(.p-disabled):hover { + background-color: #efefef; +} +.p-input-filled .p-multiselect:not(.p-disabled).p-focus { + background-color: #efefef; +} + +.p-password.p-invalid.p-component > .p-inputtext { + border-color: #dc3545; +} + +.p-password-panel { + padding: 1.25rem; + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: none; + border-radius: 4px; +} +.p-password-panel .p-password-meter { + margin-bottom: 0.5rem; + background: #e9ecef; +} +.p-password-panel .p-password-meter .p-password-strength.weak { + background: #dc3545; +} +.p-password-panel .p-password-meter .p-password-strength.medium { + background: #ffc107; +} +.p-password-panel .p-password-meter .p-password-strength.strong { + background: #28a745; +} + +.p-radiobutton { + width: 20px; + height: 20px; +} +.p-radiobutton .p-radiobutton-box { + border: 2px solid #ced4da; + background: #ffffff; + width: 20px; + height: 20px; + color: #212529; + border-radius: 50%; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-radiobutton .p-radiobutton-box:not(.p-disabled):not(.p-highlight):hover { + border-color: #ced4da; +} +.p-radiobutton .p-radiobutton-box:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-radiobutton .p-radiobutton-box .p-radiobutton-icon { + width: 12px; + height: 12px; + transition-duration: 0.15s; + background-color: #ffffff; +} +.p-radiobutton .p-radiobutton-box.p-highlight { + border-color: #007bff; + background: #007bff; +} +.p-radiobutton .p-radiobutton-box.p-highlight:not(.p-disabled):hover { + border-color: #0062cc; + background: #0062cc; + color: #ffffff; +} +.p-radiobutton.p-invalid > .p-radiobutton-box { + border-color: #dc3545; +} +.p-radiobutton:focus { + outline: 0 none; +} + +.p-input-filled .p-radiobutton .p-radiobutton-box { + background-color: #efefef; +} +.p-input-filled .p-radiobutton .p-radiobutton-box:not(.p-disabled):hover { + background-color: #efefef; +} +.p-input-filled .p-radiobutton .p-radiobutton-box.p-highlight { + background: #007bff; +} +.p-input-filled .p-radiobutton .p-radiobutton-box.p-highlight:not(.p-disabled):hover { + background: #0062cc; +} + +.p-highlight .p-radiobutton .p-radiobutton-box { + border-color: #ffffff; +} + +.p-rating .p-rating-icon { + color: #495057; + margin-left: 0.5rem; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + font-size: 1.143rem; +} +.p-rating .p-rating-icon.p-rating-cancel { + color: #dc3545; +} +.p-rating .p-rating-icon:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-rating .p-rating-icon:first-child { + margin-left: 0; +} +.p-rating .p-rating-icon.pi-star-fill { + color: #007bff; +} +.p-rating:not(.p-disabled):not(.p-readonly) .p-rating-icon:hover { + color: #007bff; +} +.p-rating:not(.p-disabled):not(.p-readonly) .p-rating-icon.p-rating-cancel:hover { + color: #dc3545; +} + +.p-highlight .p-rating .p-rating-icon { + color: #ffffff; +} + +.p-selectbutton .p-button { + background: #6c757d; + border: 1px solid #6c757d; + color: #ffffff; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-selectbutton .p-button .p-button-icon-left, +.p-selectbutton .p-button .p-button-icon-right { + color: #ffffff; +} +.p-selectbutton .p-button:not(.p-disabled):not(.p-highlight):hover { + background: #5a6268; + border-color: #545b62; + color: #ffffff; +} +.p-selectbutton .p-button:not(.p-disabled):not(.p-highlight):hover .p-button-icon-left, +.p-selectbutton .p-button:not(.p-disabled):not(.p-highlight):hover .p-button-icon-right { + color: #ffffff; +} +.p-selectbutton .p-button.p-highlight { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.p-selectbutton .p-button.p-highlight .p-button-icon-left, +.p-selectbutton .p-button.p-highlight .p-button-icon-right { + color: #ffffff; +} +.p-selectbutton .p-button.p-highlight:hover { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.p-selectbutton .p-button.p-highlight:hover .p-button-icon-left, +.p-selectbutton .p-button.p-highlight:hover .p-button-icon-right { + color: #ffffff; +} +.p-selectbutton.p-invalid > .p-button { + border-color: #dc3545; +} + +.p-slider { + background: #e9ecef; + border: 0 none; + border-radius: 4px; +} +.p-slider.p-slider-horizontal { + height: 0.286rem; +} +.p-slider.p-slider-horizontal .p-slider-handle { + margin-top: -0.5715rem; + margin-left: -0.5715rem; +} +.p-slider.p-slider-vertical { + width: 0.286rem; +} +.p-slider.p-slider-vertical .p-slider-handle { + margin-left: -0.5715rem; + margin-bottom: -0.5715rem; +} +.p-slider .p-slider-handle { + height: 1.143rem; + width: 1.143rem; + background: #007bff; + border: 2px solid #007bff; + border-radius: 4px; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-slider .p-slider-handle:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-slider .p-slider-range { + background: #007bff; +} +.p-slider:not(.p-disabled) .p-slider-handle:hover { + background: #0069d9; + border-color: #0069d9; +} + +.p-treeselect { + background: #ffffff; + border: 1px solid #ced4da; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.p-treeselect:not(.p-disabled):hover { + border-color: #ced4da; +} +.p-treeselect:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: #007bff; +} +.p-treeselect .p-treeselect-label { + padding: 0.5rem 0.75rem; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-treeselect .p-treeselect-label.p-placeholder { + color: #6c757d; +} +.p-treeselect.p-treeselect-chip .p-treeselect-token { + padding: 0.25rem 0.75rem; + margin-right: 0.5rem; + background: #dee2e6; + color: #212529; + border-radius: 16px; +} +.p-treeselect .p-treeselect-trigger { + background: transparent; + color: #495057; + width: 2.357rem; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.p-treeselect.p-invalid.p-component { + border-color: #dc3545; +} + +.p-inputwrapper-filled.p-treeselect.p-treeselect-chip .p-treeselect-label { + padding: 0.25rem 0.75rem; +} + +.p-treeselect-panel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + box-shadow: none; +} +.p-treeselect-panel .p-treeselect-items-wrapper .p-tree { + border: 0 none; +} +.p-treeselect-panel .p-treeselect-items-wrapper .p-treeselect-empty-message { + padding: 0.5rem 1.5rem; + color: #212529; + background: transparent; +} + +.p-input-filled .p-treeselect { + background: #efefef; +} +.p-input-filled .p-treeselect:not(.p-disabled):hover { + background-color: #efefef; +} +.p-input-filled .p-treeselect:not(.p-disabled).p-focus { + background-color: #efefef; +} + +.p-togglebutton.p-button { + background: #6c757d; + border: 1px solid #6c757d; + color: #ffffff; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-togglebutton.p-button .p-button-icon-left, +.p-togglebutton.p-button .p-button-icon-right { + color: #ffffff; +} +.p-togglebutton.p-button:not(.p-disabled):not(.p-highlight):hover { + background: #5a6268; + border-color: #545b62; + color: #ffffff; +} +.p-togglebutton.p-button:not(.p-disabled):not(.p-highlight):hover .p-button-icon-left, +.p-togglebutton.p-button:not(.p-disabled):not(.p-highlight):hover .p-button-icon-right { + color: #ffffff; +} +.p-togglebutton.p-button.p-highlight { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.p-togglebutton.p-button.p-highlight .p-button-icon-left, +.p-togglebutton.p-button.p-highlight .p-button-icon-right { + color: #ffffff; +} +.p-togglebutton.p-button.p-highlight:hover { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.p-togglebutton.p-button.p-highlight:hover .p-button-icon-left, +.p-togglebutton.p-button.p-highlight:hover .p-button-icon-right { + color: #ffffff; +} +.p-togglebutton.p-button.p-invalid > .p-button { + border-color: #dc3545; +} + +.p-button { + color: #ffffff; + background: #007bff; + border: 1px solid #007bff; + padding: 0.5rem 0.75rem; + font-size: 1rem; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.p-button:enabled:hover { + background: #0069d9; + color: #ffffff; + border-color: #0069d9; +} +.p-button:enabled:active { + background: #0062cc; + color: #ffffff; + border-color: #0062cc; +} +.p-button.p-button-outlined { + background-color: transparent; + color: #007bff; + border: 1px solid; +} +.p-button.p-button-outlined:enabled:hover { + background: rgba(0, 123, 255, 0.04); + color: #007bff; + border: 1px solid; +} +.p-button.p-button-outlined:enabled:active { + background: rgba(0, 123, 255, 0.16); + color: #007bff; + border: 1px solid; +} +.p-button.p-button-outlined.p-button-plain { + color: #6c757d; + border-color: #6c757d; +} +.p-button.p-button-outlined.p-button-plain:enabled:hover { + background: #e9ecef; + color: #6c757d; +} +.p-button.p-button-outlined.p-button-plain:enabled:active { + background: #dee2e6; + color: #6c757d; +} +.p-button.p-button-text { + background-color: transparent; + color: #007bff; + border-color: transparent; +} +.p-button.p-button-text:enabled:hover { + background: rgba(0, 123, 255, 0.04); + color: #007bff; + border-color: transparent; +} +.p-button.p-button-text:enabled:active { + background: rgba(0, 123, 255, 0.16); + color: #007bff; + border-color: transparent; +} +.p-button.p-button-text.p-button-plain { + color: #6c757d; +} +.p-button.p-button-text.p-button-plain:enabled:hover { + background: #e9ecef; + color: #6c757d; +} +.p-button.p-button-text.p-button-plain:enabled:active { + background: #dee2e6; + color: #6c757d; +} +.p-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-button .p-button-icon-left { + margin-right: 0.5rem; +} +.p-button .p-button-icon-right { + margin-left: 0.5rem; +} +.p-button .p-button-icon-bottom { + margin-top: 0.5rem; +} +.p-button .p-button-icon-top { + margin-bottom: 0.5rem; +} +.p-button .p-badge { + margin-left: 0.5rem; + min-width: 1rem; + height: 1rem; + line-height: 1rem; + color: #007bff; + background-color: #ffffff; +} +.p-button.p-button-raised { + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); +} +.p-button.p-button-rounded { + border-radius: 2rem; +} +.p-button.p-button-icon-only { + width: 2.357rem; + padding: 0.5rem 0; +} +.p-button.p-button-icon-only .p-button-icon-left, +.p-button.p-button-icon-only .p-button-icon-right { + margin: 0; +} +.p-button.p-button-icon-only.p-button-rounded { + border-radius: 50%; + height: 2.357rem; +} +.p-button.p-button-sm { + font-size: 0.875rem; + padding: 0.4375rem 0.65625rem; +} +.p-button.p-button-sm .p-button-icon { + font-size: 0.875rem; +} +.p-button.p-button-lg { + font-size: 1.25rem; + padding: 0.625rem 0.9375rem; +} +.p-button.p-button-lg .p-button-icon { + font-size: 1.25rem; +} +.p-button.p-button-loading-label-only .p-button-label { + margin-left: 0.5rem; +} +.p-button.p-button-loading-label-only .p-button-loading-icon { + margin-right: 0; +} + +.p-fluid .p-button { + width: 100%; +} +.p-fluid .p-button-icon-only { + width: 2.357rem; +} +.p-fluid .p-buttonset { + display: flex; +} +.p-fluid .p-buttonset .p-button { + flex: 1; +} + +.p-button.p-button-secondary, .p-buttonset.p-button-secondary > .p-button, .p-splitbutton.p-button-secondary > .p-button { + color: #ffffff; + background: #6c757d; + border: 1px solid #6c757d; +} +.p-button.p-button-secondary:enabled:hover, .p-buttonset.p-button-secondary > .p-button:enabled:hover, .p-splitbutton.p-button-secondary > .p-button:enabled:hover { + background: #5a6268; + color: #ffffff; + border-color: #5a6268; +} +.p-button.p-button-secondary:enabled:focus, .p-buttonset.p-button-secondary > .p-button:enabled:focus, .p-splitbutton.p-button-secondary > .p-button:enabled:focus { + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); +} +.p-button.p-button-secondary:enabled:active, .p-buttonset.p-button-secondary > .p-button:enabled:active, .p-splitbutton.p-button-secondary > .p-button:enabled:active { + background: #545b62; + color: #ffffff; + border-color: #4e555b; +} +.p-button.p-button-secondary.p-button-outlined, .p-buttonset.p-button-secondary > .p-button.p-button-outlined, .p-splitbutton.p-button-secondary > .p-button.p-button-outlined { + background-color: transparent; + color: #6c757d; + border: 1px solid; +} +.p-button.p-button-secondary.p-button-outlined:enabled:hover, .p-buttonset.p-button-secondary > .p-button.p-button-outlined:enabled:hover, .p-splitbutton.p-button-secondary > .p-button.p-button-outlined:enabled:hover { + background: rgba(108, 117, 125, 0.04); + color: #6c757d; + border: 1px solid; +} +.p-button.p-button-secondary.p-button-outlined:enabled:active, .p-buttonset.p-button-secondary > .p-button.p-button-outlined:enabled:active, .p-splitbutton.p-button-secondary > .p-button.p-button-outlined:enabled:active { + background: rgba(108, 117, 125, 0.16); + color: #6c757d; + border: 1px solid; +} +.p-button.p-button-secondary.p-button-text, .p-buttonset.p-button-secondary > .p-button.p-button-text, .p-splitbutton.p-button-secondary > .p-button.p-button-text { + background-color: transparent; + color: #6c757d; + border-color: transparent; +} +.p-button.p-button-secondary.p-button-text:enabled:hover, .p-buttonset.p-button-secondary > .p-button.p-button-text:enabled:hover, .p-splitbutton.p-button-secondary > .p-button.p-button-text:enabled:hover { + background: rgba(108, 117, 125, 0.04); + border-color: transparent; + color: #6c757d; +} +.p-button.p-button-secondary.p-button-text:enabled:active, .p-buttonset.p-button-secondary > .p-button.p-button-text:enabled:active, .p-splitbutton.p-button-secondary > .p-button.p-button-text:enabled:active { + background: rgba(108, 117, 125, 0.16); + border-color: transparent; + color: #6c757d; +} + +.p-button.p-button-info, .p-buttonset.p-button-info > .p-button, .p-splitbutton.p-button-info > .p-button { + color: #ffffff; + background: #17a2b8; + border: 1px solid #17a2b8; +} +.p-button.p-button-info:enabled:hover, .p-buttonset.p-button-info > .p-button:enabled:hover, .p-splitbutton.p-button-info > .p-button:enabled:hover { + background: #138496; + color: #ffffff; + border-color: #117a8b; +} +.p-button.p-button-info:enabled:focus, .p-buttonset.p-button-info > .p-button:enabled:focus, .p-splitbutton.p-button-info > .p-button:enabled:focus { + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} +.p-button.p-button-info:enabled:active, .p-buttonset.p-button-info > .p-button:enabled:active, .p-splitbutton.p-button-info > .p-button:enabled:active { + background: #138496; + color: #ffffff; + border-color: #117a8b; +} +.p-button.p-button-info.p-button-outlined, .p-buttonset.p-button-info > .p-button.p-button-outlined, .p-splitbutton.p-button-info > .p-button.p-button-outlined { + background-color: transparent; + color: #17a2b8; + border: 1px solid; +} +.p-button.p-button-info.p-button-outlined:enabled:hover, .p-buttonset.p-button-info > .p-button.p-button-outlined:enabled:hover, .p-splitbutton.p-button-info > .p-button.p-button-outlined:enabled:hover { + background: rgba(23, 162, 184, 0.04); + color: #17a2b8; + border: 1px solid; +} +.p-button.p-button-info.p-button-outlined:enabled:active, .p-buttonset.p-button-info > .p-button.p-button-outlined:enabled:active, .p-splitbutton.p-button-info > .p-button.p-button-outlined:enabled:active { + background: rgba(23, 162, 184, 0.16); + color: #17a2b8; + border: 1px solid; +} +.p-button.p-button-info.p-button-text, .p-buttonset.p-button-info > .p-button.p-button-text, .p-splitbutton.p-button-info > .p-button.p-button-text { + background-color: transparent; + color: #17a2b8; + border-color: transparent; +} +.p-button.p-button-info.p-button-text:enabled:hover, .p-buttonset.p-button-info > .p-button.p-button-text:enabled:hover, .p-splitbutton.p-button-info > .p-button.p-button-text:enabled:hover { + background: rgba(23, 162, 184, 0.04); + border-color: transparent; + color: #17a2b8; +} +.p-button.p-button-info.p-button-text:enabled:active, .p-buttonset.p-button-info > .p-button.p-button-text:enabled:active, .p-splitbutton.p-button-info > .p-button.p-button-text:enabled:active { + background: rgba(23, 162, 184, 0.16); + border-color: transparent; + color: #17a2b8; +} + +.p-button.p-button-success, .p-buttonset.p-button-success > .p-button, .p-splitbutton.p-button-success > .p-button { + color: #ffffff; + background: #28a745; + border: 1px solid #28a745; +} +.p-button.p-button-success:enabled:hover, .p-buttonset.p-button-success > .p-button:enabled:hover, .p-splitbutton.p-button-success > .p-button:enabled:hover { + background: #218838; + color: #ffffff; + border-color: #1e7e34; +} +.p-button.p-button-success:enabled:focus, .p-buttonset.p-button-success > .p-button:enabled:focus, .p-splitbutton.p-button-success > .p-button:enabled:focus { + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} +.p-button.p-button-success:enabled:active, .p-buttonset.p-button-success > .p-button:enabled:active, .p-splitbutton.p-button-success > .p-button:enabled:active { + background: #1e7e34; + color: #ffffff; + border-color: #1c7430; +} +.p-button.p-button-success.p-button-outlined, .p-buttonset.p-button-success > .p-button.p-button-outlined, .p-splitbutton.p-button-success > .p-button.p-button-outlined { + background-color: transparent; + color: #28a745; + border: 1px solid; +} +.p-button.p-button-success.p-button-outlined:enabled:hover, .p-buttonset.p-button-success > .p-button.p-button-outlined:enabled:hover, .p-splitbutton.p-button-success > .p-button.p-button-outlined:enabled:hover { + background: rgba(40, 167, 69, 0.04); + color: #28a745; + border: 1px solid; +} +.p-button.p-button-success.p-button-outlined:enabled:active, .p-buttonset.p-button-success > .p-button.p-button-outlined:enabled:active, .p-splitbutton.p-button-success > .p-button.p-button-outlined:enabled:active { + background: rgba(40, 167, 69, 0.16); + color: #28a745; + border: 1px solid; +} +.p-button.p-button-success.p-button-text, .p-buttonset.p-button-success > .p-button.p-button-text, .p-splitbutton.p-button-success > .p-button.p-button-text { + background-color: transparent; + color: #28a745; + border-color: transparent; +} +.p-button.p-button-success.p-button-text:enabled:hover, .p-buttonset.p-button-success > .p-button.p-button-text:enabled:hover, .p-splitbutton.p-button-success > .p-button.p-button-text:enabled:hover { + background: rgba(40, 167, 69, 0.04); + border-color: transparent; + color: #28a745; +} +.p-button.p-button-success.p-button-text:enabled:active, .p-buttonset.p-button-success > .p-button.p-button-text:enabled:active, .p-splitbutton.p-button-success > .p-button.p-button-text:enabled:active { + background: rgba(40, 167, 69, 0.16); + border-color: transparent; + color: #28a745; +} + +.p-button.p-button-warning, .p-buttonset.p-button-warning > .p-button, .p-splitbutton.p-button-warning > .p-button { + color: #212529; + background: #ffc107; + border: 1px solid #ffc107; +} +.p-button.p-button-warning:enabled:hover, .p-buttonset.p-button-warning > .p-button:enabled:hover, .p-splitbutton.p-button-warning > .p-button:enabled:hover { + background: #e0a800; + color: #212529; + border-color: #d39e00; +} +.p-button.p-button-warning:enabled:focus, .p-buttonset.p-button-warning > .p-button:enabled:focus, .p-splitbutton.p-button-warning > .p-button:enabled:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); +} +.p-button.p-button-warning:enabled:active, .p-buttonset.p-button-warning > .p-button:enabled:active, .p-splitbutton.p-button-warning > .p-button:enabled:active { + background: #d39e00; + color: #212529; + border-color: #c69500; +} +.p-button.p-button-warning.p-button-outlined, .p-buttonset.p-button-warning > .p-button.p-button-outlined, .p-splitbutton.p-button-warning > .p-button.p-button-outlined { + background-color: transparent; + color: #ffc107; + border: 1px solid; +} +.p-button.p-button-warning.p-button-outlined:enabled:hover, .p-buttonset.p-button-warning > .p-button.p-button-outlined:enabled:hover, .p-splitbutton.p-button-warning > .p-button.p-button-outlined:enabled:hover { + background: rgba(255, 193, 7, 0.04); + color: #ffc107; + border: 1px solid; +} +.p-button.p-button-warning.p-button-outlined:enabled:active, .p-buttonset.p-button-warning > .p-button.p-button-outlined:enabled:active, .p-splitbutton.p-button-warning > .p-button.p-button-outlined:enabled:active { + background: rgba(255, 193, 7, 0.16); + color: #ffc107; + border: 1px solid; +} +.p-button.p-button-warning.p-button-text, .p-buttonset.p-button-warning > .p-button.p-button-text, .p-splitbutton.p-button-warning > .p-button.p-button-text { + background-color: transparent; + color: #ffc107; + border-color: transparent; +} +.p-button.p-button-warning.p-button-text:enabled:hover, .p-buttonset.p-button-warning > .p-button.p-button-text:enabled:hover, .p-splitbutton.p-button-warning > .p-button.p-button-text:enabled:hover { + background: rgba(255, 193, 7, 0.04); + border-color: transparent; + color: #ffc107; +} +.p-button.p-button-warning.p-button-text:enabled:active, .p-buttonset.p-button-warning > .p-button.p-button-text:enabled:active, .p-splitbutton.p-button-warning > .p-button.p-button-text:enabled:active { + background: rgba(255, 193, 7, 0.16); + border-color: transparent; + color: #ffc107; +} + +.p-button.p-button-help, .p-buttonset.p-button-help > .p-button, .p-splitbutton.p-button-help > .p-button { + color: #ffffff; + background: #6f42c1; + border: 1px solid #6f42c1; +} +.p-button.p-button-help:enabled:hover, .p-buttonset.p-button-help > .p-button:enabled:hover, .p-splitbutton.p-button-help > .p-button:enabled:hover { + background: #633bad; + color: #ffffff; + border-color: #58349a; +} +.p-button.p-button-help:enabled:focus, .p-buttonset.p-button-help > .p-button:enabled:focus, .p-splitbutton.p-button-help > .p-button:enabled:focus { + box-shadow: 0 0 0 0.2rem #d3c6ec; +} +.p-button.p-button-help:enabled:active, .p-buttonset.p-button-help > .p-button:enabled:active, .p-splitbutton.p-button-help > .p-button:enabled:active { + background: #58349a; + color: #ffffff; + border-color: #4d2e87; +} +.p-button.p-button-help.p-button-outlined, .p-buttonset.p-button-help > .p-button.p-button-outlined, .p-splitbutton.p-button-help > .p-button.p-button-outlined { + background-color: transparent; + color: #6f42c1; + border: 1px solid; +} +.p-button.p-button-help.p-button-outlined:enabled:hover, .p-buttonset.p-button-help > .p-button.p-button-outlined:enabled:hover, .p-splitbutton.p-button-help > .p-button.p-button-outlined:enabled:hover { + background: rgba(111, 66, 193, 0.04); + color: #6f42c1; + border: 1px solid; +} +.p-button.p-button-help.p-button-outlined:enabled:active, .p-buttonset.p-button-help > .p-button.p-button-outlined:enabled:active, .p-splitbutton.p-button-help > .p-button.p-button-outlined:enabled:active { + background: rgba(111, 66, 193, 0.16); + color: #6f42c1; + border: 1px solid; +} +.p-button.p-button-help.p-button-text, .p-buttonset.p-button-help > .p-button.p-button-text, .p-splitbutton.p-button-help > .p-button.p-button-text { + background-color: transparent; + color: #6f42c1; + border-color: transparent; +} +.p-button.p-button-help.p-button-text:enabled:hover, .p-buttonset.p-button-help > .p-button.p-button-text:enabled:hover, .p-splitbutton.p-button-help > .p-button.p-button-text:enabled:hover { + background: rgba(111, 66, 193, 0.04); + border-color: transparent; + color: #6f42c1; +} +.p-button.p-button-help.p-button-text:enabled:active, .p-buttonset.p-button-help > .p-button.p-button-text:enabled:active, .p-splitbutton.p-button-help > .p-button.p-button-text:enabled:active { + background: rgba(111, 66, 193, 0.16); + border-color: transparent; + color: #6f42c1; +} + +.p-button.p-button-danger, .p-buttonset.p-button-danger > .p-button, .p-splitbutton.p-button-danger > .p-button { + color: #ffffff; + background: #dc3545; + border: 1px solid #dc3545; +} +.p-button.p-button-danger:enabled:hover, .p-buttonset.p-button-danger > .p-button:enabled:hover, .p-splitbutton.p-button-danger > .p-button:enabled:hover { + background: #c82333; + color: #ffffff; + border-color: #bd2130; +} +.p-button.p-button-danger:enabled:focus, .p-buttonset.p-button-danger > .p-button:enabled:focus, .p-splitbutton.p-button-danger > .p-button:enabled:focus { + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} +.p-button.p-button-danger:enabled:active, .p-buttonset.p-button-danger > .p-button:enabled:active, .p-splitbutton.p-button-danger > .p-button:enabled:active { + background: #bd2130; + color: #ffffff; + border-color: #b21f2d; +} +.p-button.p-button-danger.p-button-outlined, .p-buttonset.p-button-danger > .p-button.p-button-outlined, .p-splitbutton.p-button-danger > .p-button.p-button-outlined { + background-color: transparent; + color: #dc3545; + border: 1px solid; +} +.p-button.p-button-danger.p-button-outlined:enabled:hover, .p-buttonset.p-button-danger > .p-button.p-button-outlined:enabled:hover, .p-splitbutton.p-button-danger > .p-button.p-button-outlined:enabled:hover { + background: rgba(220, 53, 69, 0.04); + color: #dc3545; + border: 1px solid; +} +.p-button.p-button-danger.p-button-outlined:enabled:active, .p-buttonset.p-button-danger > .p-button.p-button-outlined:enabled:active, .p-splitbutton.p-button-danger > .p-button.p-button-outlined:enabled:active { + background: rgba(220, 53, 69, 0.16); + color: #dc3545; + border: 1px solid; +} +.p-button.p-button-danger.p-button-text, .p-buttonset.p-button-danger > .p-button.p-button-text, .p-splitbutton.p-button-danger > .p-button.p-button-text { + background-color: transparent; + color: #dc3545; + border-color: transparent; +} +.p-button.p-button-danger.p-button-text:enabled:hover, .p-buttonset.p-button-danger > .p-button.p-button-text:enabled:hover, .p-splitbutton.p-button-danger > .p-button.p-button-text:enabled:hover { + background: rgba(220, 53, 69, 0.04); + border-color: transparent; + color: #dc3545; +} +.p-button.p-button-danger.p-button-text:enabled:active, .p-buttonset.p-button-danger > .p-button.p-button-text:enabled:active, .p-splitbutton.p-button-danger > .p-button.p-button-text:enabled:active { + background: rgba(220, 53, 69, 0.16); + border-color: transparent; + color: #dc3545; +} + +.p-button.p-button-link { + color: #007bff; + background: transparent; + border: transparent; +} +.p-button.p-button-link:enabled:hover { + background: transparent; + color: #0069d9; + border-color: transparent; +} +.p-button.p-button-link:enabled:hover .p-button-label { + text-decoration: underline; +} +.p-button.p-button-link:enabled:focus { + background: transparent; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + border-color: transparent; +} +.p-button.p-button-link:enabled:active { + background: transparent; + color: #007bff; + border-color: transparent; +} + +.p-speeddial-button.p-button.p-button-icon-only { + width: 4rem; + height: 4rem; +} +.p-speeddial-button.p-button.p-button-icon-only .p-button-icon { + font-size: 1.3rem; +} + +.p-speeddial-action { + width: 3rem; + height: 3rem; + background: #495057; + color: #fff; +} +.p-speeddial-action:hover { + background: #343a40; + color: #fff; +} + +.p-speeddial-direction-up .p-speeddial-item { + margin: 0.25rem 0; +} +.p-speeddial-direction-up .p-speeddial-item:first-child { + margin-bottom: 0.5rem; +} + +.p-speeddial-direction-down .p-speeddial-item { + margin: 0.25rem 0; +} +.p-speeddial-direction-down .p-speeddial-item:first-child { + margin-top: 0.5rem; +} + +.p-speeddial-direction-left .p-speeddial-item { + margin: 0 0.25rem; +} +.p-speeddial-direction-left .p-speeddial-item:first-child { + margin-right: 0.5rem; +} + +.p-speeddial-direction-right .p-speeddial-item { + margin: 0 0.25rem; +} +.p-speeddial-direction-right .p-speeddial-item:first-child { + margin-left: 0.5rem; +} + +.p-speeddial-circle .p-speeddial-item, +.p-speeddial-semi-circle .p-speeddial-item, +.p-speeddial-quarter-circle .p-speeddial-item { + margin: 0; +} +.p-speeddial-circle .p-speeddial-item:first-child, .p-speeddial-circle .p-speeddial-item:last-child, +.p-speeddial-semi-circle .p-speeddial-item:first-child, +.p-speeddial-semi-circle .p-speeddial-item:last-child, +.p-speeddial-quarter-circle .p-speeddial-item:first-child, +.p-speeddial-quarter-circle .p-speeddial-item:last-child { + margin: 0; +} + +.p-speeddial-mask { + background-color: rgba(0, 0, 0, 0.4); +} + +.p-splitbutton { + border-radius: 4px; +} +.p-splitbutton.p-button-outlined > .p-button { + background-color: transparent; + color: #007bff; + border: 1px solid; +} +.p-splitbutton.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(0, 123, 255, 0.04); + color: #007bff; +} +.p-splitbutton.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(0, 123, 255, 0.16); + color: #007bff; +} +.p-splitbutton.p-button-outlined.p-button-plain > .p-button { + color: #6c757d; + border-color: #6c757d; +} +.p-splitbutton.p-button-outlined.p-button-plain > .p-button:enabled:hover, .p-splitbutton.p-button-outlined.p-button-plain > .p-button:not(button):not(a):not(.p-disabled):hover { + background: #e9ecef; + color: #6c757d; +} +.p-splitbutton.p-button-outlined.p-button-plain > .p-button:enabled:active, .p-splitbutton.p-button-outlined.p-button-plain > .p-button:not(button):not(a):not(.p-disabled):active { + background: #dee2e6; + color: #6c757d; +} +.p-splitbutton.p-button-text > .p-button { + background-color: transparent; + color: #007bff; + border-color: transparent; +} +.p-splitbutton.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(0, 123, 255, 0.04); + color: #007bff; + border-color: transparent; +} +.p-splitbutton.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(0, 123, 255, 0.16); + color: #007bff; + border-color: transparent; +} +.p-splitbutton.p-button-text.p-button-plain > .p-button { + color: #6c757d; +} +.p-splitbutton.p-button-text.p-button-plain > .p-button:enabled:hover, .p-splitbutton.p-button-text.p-button-plain > .p-button:not(button):not(a):not(.p-disabled):hover { + background: #e9ecef; + color: #6c757d; +} +.p-splitbutton.p-button-text.p-button-plain > .p-button:enabled:active, .p-splitbutton.p-button-text.p-button-plain > .p-button:not(button):not(a):not(.p-disabled):active { + background: #dee2e6; + color: #6c757d; +} +.p-splitbutton.p-button-raised { + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); +} +.p-splitbutton.p-button-rounded { + border-radius: 2rem; +} +.p-splitbutton.p-button-rounded > .p-button { + border-radius: 2rem; +} +.p-splitbutton.p-button-sm > .p-button { + font-size: 0.875rem; + padding: 0.4375rem 0.65625rem; +} +.p-splitbutton.p-button-sm > .p-button .p-button-icon { + font-size: 0.875rem; +} +.p-splitbutton.p-button-lg > .p-button { + font-size: 1.25rem; + padding: 0.625rem 0.9375rem; +} +.p-splitbutton.p-button-lg > .p-button .p-button-icon { + font-size: 1.25rem; +} + +.p-splitbutton.p-button-secondary.p-button-outlined > .p-button { + background-color: transparent; + color: #6c757d; + border: 1px solid; +} +.p-splitbutton.p-button-secondary.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-secondary.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(108, 117, 125, 0.04); + color: #6c757d; +} +.p-splitbutton.p-button-secondary.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-secondary.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(108, 117, 125, 0.16); + color: #6c757d; +} +.p-splitbutton.p-button-secondary.p-button-text > .p-button { + background-color: transparent; + color: #6c757d; + border-color: transparent; +} +.p-splitbutton.p-button-secondary.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-secondary.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(108, 117, 125, 0.04); + border-color: transparent; + color: #6c757d; +} +.p-splitbutton.p-button-secondary.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-secondary.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(108, 117, 125, 0.16); + border-color: transparent; + color: #6c757d; +} + +.p-splitbutton.p-button-info.p-button-outlined > .p-button { + background-color: transparent; + color: #17a2b8; + border: 1px solid; +} +.p-splitbutton.p-button-info.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-info.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(23, 162, 184, 0.04); + color: #17a2b8; +} +.p-splitbutton.p-button-info.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-info.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(23, 162, 184, 0.16); + color: #17a2b8; +} +.p-splitbutton.p-button-info.p-button-text > .p-button { + background-color: transparent; + color: #17a2b8; + border-color: transparent; +} +.p-splitbutton.p-button-info.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-info.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(23, 162, 184, 0.04); + border-color: transparent; + color: #17a2b8; +} +.p-splitbutton.p-button-info.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-info.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(23, 162, 184, 0.16); + border-color: transparent; + color: #17a2b8; +} + +.p-splitbutton.p-button-success.p-button-outlined > .p-button { + background-color: transparent; + color: #28a745; + border: 1px solid; +} +.p-splitbutton.p-button-success.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-success.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(40, 167, 69, 0.04); + color: #28a745; +} +.p-splitbutton.p-button-success.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-success.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(40, 167, 69, 0.16); + color: #28a745; +} +.p-splitbutton.p-button-success.p-button-text > .p-button { + background-color: transparent; + color: #28a745; + border-color: transparent; +} +.p-splitbutton.p-button-success.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-success.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(40, 167, 69, 0.04); + border-color: transparent; + color: #28a745; +} +.p-splitbutton.p-button-success.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-success.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(40, 167, 69, 0.16); + border-color: transparent; + color: #28a745; +} + +.p-splitbutton.p-button-warning.p-button-outlined > .p-button { + background-color: transparent; + color: #ffc107; + border: 1px solid; +} +.p-splitbutton.p-button-warning.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-warning.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(255, 193, 7, 0.04); + color: #ffc107; +} +.p-splitbutton.p-button-warning.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-warning.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(255, 193, 7, 0.16); + color: #ffc107; +} +.p-splitbutton.p-button-warning.p-button-text > .p-button { + background-color: transparent; + color: #ffc107; + border-color: transparent; +} +.p-splitbutton.p-button-warning.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-warning.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(255, 193, 7, 0.04); + border-color: transparent; + color: #ffc107; +} +.p-splitbutton.p-button-warning.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-warning.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(255, 193, 7, 0.16); + border-color: transparent; + color: #ffc107; +} + +.p-splitbutton.p-button-help.p-button-outlined > .p-button { + background-color: transparent; + color: #6f42c1; + border: 1px solid; +} +.p-splitbutton.p-button-help.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-help.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(111, 66, 193, 0.04); + color: #6f42c1; +} +.p-splitbutton.p-button-help.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-help.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(111, 66, 193, 0.16); + color: #6f42c1; +} +.p-splitbutton.p-button-help.p-button-text > .p-button { + background-color: transparent; + color: #6f42c1; + border-color: transparent; +} +.p-splitbutton.p-button-help.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-help.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(111, 66, 193, 0.04); + border-color: transparent; + color: #6f42c1; +} +.p-splitbutton.p-button-help.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-help.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(111, 66, 193, 0.16); + border-color: transparent; + color: #6f42c1; +} + +.p-splitbutton.p-button-danger.p-button-outlined > .p-button { + background-color: transparent; + color: #dc3545; + border: 1px solid; +} +.p-splitbutton.p-button-danger.p-button-outlined > .p-button:enabled:hover, .p-splitbutton.p-button-danger.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(220, 53, 69, 0.04); + color: #dc3545; +} +.p-splitbutton.p-button-danger.p-button-outlined > .p-button:enabled:active, .p-splitbutton.p-button-danger.p-button-outlined > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(220, 53, 69, 0.16); + color: #dc3545; +} +.p-splitbutton.p-button-danger.p-button-text > .p-button { + background-color: transparent; + color: #dc3545; + border-color: transparent; +} +.p-splitbutton.p-button-danger.p-button-text > .p-button:enabled:hover, .p-splitbutton.p-button-danger.p-button-text > .p-button:not(button):not(a):not(.p-disabled):hover { + background: rgba(220, 53, 69, 0.04); + border-color: transparent; + color: #dc3545; +} +.p-splitbutton.p-button-danger.p-button-text > .p-button:enabled:active, .p-splitbutton.p-button-danger.p-button-text > .p-button:not(button):not(a):not(.p-disabled):active { + background: rgba(220, 53, 69, 0.16); + border-color: transparent; + color: #dc3545; +} + +.p-carousel .p-carousel-content .p-carousel-prev, +.p-carousel .p-carousel-content .p-carousel-next { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; + margin: 0.5rem; +} +.p-carousel .p-carousel-content .p-carousel-prev:enabled:hover, +.p-carousel .p-carousel-content .p-carousel-next:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-carousel .p-carousel-content .p-carousel-prev:focus, +.p-carousel .p-carousel-content .p-carousel-next:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-carousel .p-carousel-indicators { + padding: 1rem; +} +.p-carousel .p-carousel-indicators .p-carousel-indicator { + margin-right: 0.5rem; + margin-bottom: 0.5rem; +} +.p-carousel .p-carousel-indicators .p-carousel-indicator button { + background-color: #e9ecef; + width: 2rem; + height: 0.5rem; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-carousel .p-carousel-indicators .p-carousel-indicator button:hover { + background: #dee2e6; +} +.p-carousel .p-carousel-indicators .p-carousel-indicator.p-highlight button { + background: #007bff; + color: #ffffff; +} + +.p-datatable .p-paginator-top { + border-width: 1px 0 0 0; + border-radius: 0; +} +.p-datatable .p-paginator-bottom { + border-width: 1px 0 0 0; + border-radius: 0; +} +.p-datatable .p-datatable-header { + background: #efefef; + color: #212529; + border: solid #dee2e6; + border-width: 1px 0 0 0; + padding: 1rem 1rem; + font-weight: 600; +} +.p-datatable .p-datatable-footer { + background: #efefef; + color: #212529; + border: 1px solid #dee2e6; + border-width: 1px 0 1px 0; + padding: 1rem 1rem; + font-weight: 600; +} +.p-datatable .p-datatable-thead > tr > th { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #dee2e6; + border-width: 1px 0 2px 0; + font-weight: 600; + color: #212529; + background: #ffffff; + transition: box-shadow 0.15s; +} +.p-datatable .p-datatable-tfoot > tr > td { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #dee2e6; + border-width: 1px 0 1px 0; + font-weight: 600; + color: #212529; + background: #ffffff; +} +.p-datatable .p-sortable-column .p-sortable-column-icon { + color: #6c757d; + margin-left: 0.5rem; +} +.p-datatable .p-sortable-column .p-sortable-column-badge { + border-radius: 50%; + height: 1.143rem; + min-width: 1.143rem; + line-height: 1.143rem; + color: #ffffff; + background: #007bff; + margin-left: 0.5rem; +} +.p-datatable .p-sortable-column:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-datatable .p-sortable-column:not(.p-highlight):hover .p-sortable-column-icon { + color: #6c757d; +} +.p-datatable .p-sortable-column.p-highlight { + background: #ffffff; + color: #007bff; +} +.p-datatable .p-sortable-column.p-highlight .p-sortable-column-icon { + color: #007bff; +} +.p-datatable .p-sortable-column.p-highlight:hover { + background: #e9ecef; + color: #007bff; +} +.p-datatable .p-sortable-column.p-highlight:hover .p-sortable-column-icon { + color: #007bff; +} +.p-datatable .p-sortable-column:focus { + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); + outline: 0 none; +} +.p-datatable .p-datatable-tbody > tr { + background: #ffffff; + color: #212529; + transition: box-shadow 0.15s; +} +.p-datatable .p-datatable-tbody > tr > td { + text-align: left; + border: 1px solid #dee2e6; + border-width: 1px 0 0 0; + padding: 1rem 1rem; +} +.p-datatable .p-datatable-tbody > tr > td .p-row-toggler, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-init, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-save, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-cancel { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-datatable .p-datatable-tbody > tr > td .p-row-toggler:enabled:hover, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-init:enabled:hover, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-save:enabled:hover, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-cancel:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-datatable .p-datatable-tbody > tr > td .p-row-toggler:focus, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-init:focus, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-save:focus, +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-cancel:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-datatable .p-datatable-tbody > tr > td .p-row-editor-save { + margin-right: 0.5rem; +} +.p-datatable .p-datatable-tbody > tr > td > .p-column-title { + font-weight: 600; +} +.p-datatable .p-datatable-tbody > tr:focus { + outline: 0.15rem solid rgba(38, 143, 255, 0.5); + outline-offset: -0.15rem; +} +.p-datatable .p-datatable-tbody > tr.p-highlight { + background: #007bff; + color: #ffffff; +} +.p-datatable .p-datatable-tbody > tr.p-datatable-dragpoint-top > td { + box-shadow: inset 0 2px 0 0 #007bff; +} +.p-datatable .p-datatable-tbody > tr.p-datatable-dragpoint-bottom > td { + box-shadow: inset 0 -2px 0 0 #007bff; +} +.p-datatable.p-datatable-hoverable-rows .p-datatable-tbody > tr:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-datatable .p-column-resizer-helper { + background: #007bff; +} +.p-datatable .p-datatable-scrollable-header, +.p-datatable .p-datatable-scrollable-footer { + background: #efefef; +} +.p-datatable.p-datatable-scrollable > .p-datatable-wrapper > .p-datatable-table > .p-datatable-thead, +.p-datatable.p-datatable-scrollable > .p-datatable-wrapper > .p-datatable-table > .p-datatable-tfoot { + background-color: #ffffff; +} +.p-datatable .p-datatable-loading-icon { + font-size: 2rem; +} +.p-datatable.p-datatable-gridlines .p-datatable-header { + border-width: 1px 1px 0 1px; +} +.p-datatable.p-datatable-gridlines .p-datatable-footer { + border-width: 0 1px 1px 1px; +} +.p-datatable.p-datatable-gridlines .p-paginator-top { + border-width: 0 1px 0 1px; +} +.p-datatable.p-datatable-gridlines .p-paginator-bottom { + border-width: 0 1px 1px 1px; +} +.p-datatable.p-datatable-gridlines .p-datatable-thead > tr > th { + border-width: 1px 1px 2px 1px; +} +.p-datatable.p-datatable-gridlines .p-datatable-tbody > tr > td { + border-width: 1px; +} +.p-datatable.p-datatable-gridlines .p-datatable-tfoot > tr > td { + border-width: 1px; +} +.p-datatable.p-datatable-gridlines.p-datatable-scrollable .p-datatable-thead > tr > th + th { + border-left-width: 0; +} +.p-datatable.p-datatable-gridlines.p-datatable-scrollable .p-datatable-tbody > tr > td + td { + border-left-width: 0; +} +.p-datatable.p-datatable-gridlines.p-datatable-scrollable .p-datatable-tbody > tr + tr > td, .p-datatable.p-datatable-gridlines.p-datatable-scrollable .p-datatable-tbody > tr:first-child > td { + border-top-width: 0; +} +.p-datatable.p-datatable-gridlines.p-datatable-scrollable .p-datatable-tfoot > tr > td + td { + border-left-width: 0; +} +.p-datatable.p-datatable-striped .p-datatable-tbody > tr:nth-child(even) { + background: rgba(0, 0, 0, 0.05); +} +.p-datatable.p-datatable-striped .p-datatable-tbody > tr:nth-child(even).p-highlight { + background: #007bff; + color: #ffffff; +} +.p-datatable.p-datatable-striped .p-datatable-tbody > tr:nth-child(even).p-highlight .p-row-toggler { + color: #ffffff; +} +.p-datatable.p-datatable-striped .p-datatable-tbody > tr:nth-child(even).p-highlight .p-row-toggler:hover { + color: #ffffff; +} +.p-datatable.p-datatable-sm .p-datatable-header { + padding: 0.5rem 0.5rem; +} +.p-datatable.p-datatable-sm .p-datatable-thead > tr > th { + padding: 0.5rem 0.5rem; +} +.p-datatable.p-datatable-sm .p-datatable-tbody > tr > td { + padding: 0.5rem 0.5rem; +} +.p-datatable.p-datatable-sm .p-datatable-tfoot > tr > td { + padding: 0.5rem 0.5rem; +} +.p-datatable.p-datatable-sm .p-datatable-footer { + padding: 0.5rem 0.5rem; +} +.p-datatable.p-datatable-lg .p-datatable-header { + padding: 1.25rem 1.25rem; +} +.p-datatable.p-datatable-lg .p-datatable-thead > tr > th { + padding: 1.25rem 1.25rem; +} +.p-datatable.p-datatable-lg .p-datatable-tbody > tr > td { + padding: 1.25rem 1.25rem; +} +.p-datatable.p-datatable-lg .p-datatable-tfoot > tr > td { + padding: 1.25rem 1.25rem; +} +.p-datatable.p-datatable-lg .p-datatable-footer { + padding: 1.25rem 1.25rem; +} + +.p-dataview .p-paginator-top { + border-width: 1px 0 0 0; + border-radius: 0; +} +.p-dataview .p-paginator-bottom { + border-width: 1px 0 0 0; + border-radius: 0; +} +.p-dataview .p-dataview-header { + background: #efefef; + color: #212529; + border: solid #dee2e6; + border-width: 1px 0 0 0; + padding: 1rem 1rem; + font-weight: 600; +} +.p-dataview .p-dataview-content { + background: #ffffff; + color: #212529; + border: 0 none; + padding: 0; +} +.p-dataview.p-dataview-list .p-dataview-content > .p-grid > div { + border: 1px solid #dee2e6; + border-width: 1px 0 0 0; +} +.p-dataview .p-dataview-footer { + background: #efefef; + color: #212529; + border: 1px solid #dee2e6; + border-width: 1px 0 1px 0; + padding: 1rem 1rem; + font-weight: 600; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +.p-column-filter-row .p-column-filter-menu-button, +.p-column-filter-row .p-column-filter-clear-button { + margin-left: 0.5rem; +} + +.p-column-filter-menu-button { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-column-filter-menu-button:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-column-filter-menu-button.p-column-filter-menu-button-open, .p-column-filter-menu-button.p-column-filter-menu-button-open:hover { + background: transparent; + color: #495057; +} +.p-column-filter-menu-button.p-column-filter-menu-button-active, .p-column-filter-menu-button.p-column-filter-menu-button-active:hover { + background: #007bff; + color: #ffffff; +} +.p-column-filter-menu-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-column-filter-clear-button { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-column-filter-clear-button:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-column-filter-clear-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-column-filter-overlay { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + box-shadow: none; + min-width: 12.5rem; +} +.p-column-filter-overlay .p-column-filter-row-items { + padding: 0.5rem 0; +} +.p-column-filter-overlay .p-column-filter-row-items .p-column-filter-row-item { + margin: 0; + padding: 0.5rem 1.5rem; + border: 0 none; + color: #212529; + background: transparent; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-column-filter-overlay .p-column-filter-row-items .p-column-filter-row-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-column-filter-overlay .p-column-filter-row-items .p-column-filter-row-item:not(.p-highlight):not(.p-disabled):hover { + color: #212529; + background: #e9ecef; +} +.p-column-filter-overlay .p-column-filter-row-items .p-column-filter-row-item:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-column-filter-overlay .p-column-filter-row-items .p-column-filter-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; +} + +.p-column-filter-overlay-menu .p-column-filter-operator { + padding: 0.75rem 1.5rem; + border-bottom: 1px solid #dee2e6; + color: #212529; + background: #efefef; + margin: 0; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-column-filter-overlay-menu .p-column-filter-constraint { + padding: 1.25rem; + border-bottom: 1px solid #dee2e6; +} +.p-column-filter-overlay-menu .p-column-filter-constraint .p-column-filter-matchmode-dropdown { + margin-bottom: 0.5rem; +} +.p-column-filter-overlay-menu .p-column-filter-constraint .p-column-filter-remove-button { + margin-top: 0.5rem; +} +.p-column-filter-overlay-menu .p-column-filter-constraint:last-child { + border-bottom: 0 none; +} +.p-column-filter-overlay-menu .p-column-filter-add-rule { + padding: 0.5rem 1.25rem; +} +.p-column-filter-overlay-menu .p-column-filter-buttonbar { + padding: 1.25rem; +} + +.fc { + /* FullCalendar 4 */ + /* FullCalendar 5 */ +} +.fc.fc-unthemed .fc-view-container th { + background: #efefef; + border: 1px solid #dee2e6; + color: #212529; +} +.fc.fc-unthemed .fc-view-container td.fc-widget-content { + border: 1px solid #dee2e6; + color: #212529; +} +.fc.fc-unthemed .fc-view-container td.fc-head-container { + border: 1px solid #dee2e6; +} +.fc.fc-unthemed .fc-view-container .fc-view { + background: #ffffff; +} +.fc.fc-unthemed .fc-view-container .fc-row { + border-right: 1px solid #dee2e6; +} +.fc.fc-unthemed .fc-view-container .fc-event { + background: #0069d9; + border: 1px solid #0069d9; + color: #ffffff; +} +.fc.fc-unthemed .fc-view-container .fc-divider { + background: #efefef; + border: 1px solid #dee2e6; +} +.fc.fc-unthemed .fc-toolbar .fc-button { + color: #ffffff; + background: #007bff; + border: 1px solid #007bff; + font-size: 1rem; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; + display: flex; + align-items: center; +} +.fc.fc-unthemed .fc-toolbar .fc-button:enabled:hover { + background: #0069d9; + color: #ffffff; + border-color: #0069d9; +} +.fc.fc-unthemed .fc-toolbar .fc-button:enabled:active { + background: #0062cc; + color: #ffffff; + border-color: #0062cc; +} +.fc.fc-unthemed .fc-toolbar .fc-button:enabled:active:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.fc.fc-unthemed .fc-toolbar .fc-button .fc-icon-chevron-left { + font-family: "PrimeIcons" !important; + text-indent: 0; + font-size: 1rem; +} +.fc.fc-unthemed .fc-toolbar .fc-button .fc-icon-chevron-left:before { + content: "\e900"; +} +.fc.fc-unthemed .fc-toolbar .fc-button .fc-icon-chevron-right { + font-family: "PrimeIcons" !important; + text-indent: 0; + font-size: 1rem; +} +.fc.fc-unthemed .fc-toolbar .fc-button .fc-icon-chevron-right:before { + content: "\e901"; +} +.fc.fc-unthemed .fc-toolbar .fc-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button { + background: #6c757d; + border: 1px solid #6c757d; + color: #ffffff; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button:hover, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button:hover, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button:hover { + background: #5a6268; + border-color: #545b62; + color: #ffffff; +} +.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active:hover, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active:hover, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active:hover { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.fc.fc-unthemed .fc-toolbar .fc-button.fc-dayGridMonth-button:focus, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridWeek-button:focus, .fc.fc-unthemed .fc-toolbar .fc-button.fc-timeGridDay-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + z-index: 1; +} +.fc.fc-unthemed .fc-toolbar .fc-button-group .fc-button { + border-radius: 0; +} +.fc.fc-unthemed .fc-toolbar .fc-button-group .fc-button:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.fc.fc-unthemed .fc-toolbar .fc-button-group .fc-button:last-child { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.fc.fc-theme-standard .fc-view-harness .fc-scrollgrid { + border-color: #dee2e6; +} +.fc.fc-theme-standard .fc-view-harness th { + background: #efefef; + border-color: #dee2e6; + color: #212529; +} +.fc.fc-theme-standard .fc-view-harness td { + color: #212529; + border-color: #dee2e6; +} +.fc.fc-theme-standard .fc-view-harness .fc-view { + background: #ffffff; +} +.fc.fc-theme-standard .fc-view-harness .fc-popover { + background: none; + border: 0 none; +} +.fc.fc-theme-standard .fc-view-harness .fc-popover .fc-popover-header { + border: 1px solid #dee2e6; + padding: 1rem 1.25rem; + background: #efefef; + color: #212529; +} +.fc.fc-theme-standard .fc-view-harness .fc-popover .fc-popover-header .fc-popover-close { + opacity: 1; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + font-family: "PrimeIcons" !important; + font-size: 1rem; + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.fc.fc-theme-standard .fc-view-harness .fc-popover .fc-popover-header .fc-popover-close:before { + content: "\e90b"; +} +.fc.fc-theme-standard .fc-view-harness .fc-popover .fc-popover-header .fc-popover-close:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.fc.fc-theme-standard .fc-view-harness .fc-popover .fc-popover-header .fc-popover-close:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.fc.fc-theme-standard .fc-view-harness .fc-popover .fc-popover-body { + padding: 1.25rem; + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + border-top: 0 none; +} +.fc.fc-theme-standard .fc-view-harness .fc-event.fc-daygrid-block-event { + color: #ffffff; + background: #0069d9; + border-color: #0069d9; +} +.fc.fc-theme-standard .fc-view-harness .fc-event.fc-daygrid-block-event .fc-event-main { + color: #ffffff; +} +.fc.fc-theme-standard .fc-view-harness .fc-event.fc-daygrid-dot-event .fc-daygrid-event-dot { + background: #0069d9; + border-color: #0069d9; +} +.fc.fc-theme-standard .fc-view-harness .fc-event.fc-daygrid-dot-event:hover { + background: #e9ecef; + color: #212529; +} +.fc.fc-theme-standard .fc-view-harness .fc-cell-shaded { + background: #efefef; +} +.fc.fc-theme-standard .fc-toolbar .fc-button { + color: #ffffff; + background: #007bff; + border: 1px solid #007bff; + font-size: 1rem; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; + border-radius: 4px; +} +.fc.fc-theme-standard .fc-toolbar .fc-button:enabled:hover { + background: #0069d9; + color: #ffffff; + border-color: #0069d9; +} +.fc.fc-theme-standard .fc-toolbar .fc-button:enabled:active { + background: #0062cc; + color: #ffffff; + border-color: #0062cc; +} +.fc.fc-theme-standard .fc-toolbar .fc-button:enabled:active:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.fc.fc-theme-standard .fc-toolbar .fc-button:disabled { + opacity: 0.65; + color: #ffffff; + background: #007bff; + border: 1px solid #007bff; +} +.fc.fc-theme-standard .fc-toolbar .fc-button .fc-icon-chevron-left { + font-family: "PrimeIcons" !important; + text-indent: 0; + font-size: 1rem; +} +.fc.fc-theme-standard .fc-toolbar .fc-button .fc-icon-chevron-left:before { + content: "\e900"; +} +.fc.fc-theme-standard .fc-toolbar .fc-button .fc-icon-chevron-right { + font-family: "PrimeIcons" !important; + text-indent: 0; + font-size: 1rem; +} +.fc.fc-theme-standard .fc-toolbar .fc-button .fc-icon-chevron-right:before { + content: "\e901"; +} +.fc.fc-theme-standard .fc-toolbar .fc-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.fc.fc-theme-standard .fc-toolbar .fc-button.fc-dayGridMonth-button, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridWeek-button, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridDay-button { + background: #6c757d; + border: 1px solid #6c757d; + color: #ffffff; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.fc.fc-theme-standard .fc-toolbar .fc-button.fc-dayGridMonth-button:hover, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridWeek-button:hover, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridDay-button:hover { + background: #5a6268; + border-color: #545b62; + color: #ffffff; +} +.fc.fc-theme-standard .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.fc.fc-theme-standard .fc-toolbar .fc-button.fc-dayGridMonth-button.fc-button-active:hover, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridWeek-button.fc-button-active:hover, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridDay-button.fc-button-active:hover { + background: #545b62; + border-color: #4e555b; + color: #ffffff; +} +.fc.fc-theme-standard .fc-toolbar .fc-button.fc-dayGridMonth-button:not(:disabled):focus, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridWeek-button:not(:disabled):focus, .fc.fc-theme-standard .fc-toolbar .fc-button.fc-timeGridDay-button:not(:disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + z-index: 1; +} +.fc.fc-theme-standard .fc-toolbar .fc-button-group .fc-button { + border-radius: 0; +} +.fc.fc-theme-standard .fc-toolbar .fc-button-group .fc-button:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.fc.fc-theme-standard .fc-toolbar .fc-button-group .fc-button:last-child { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.fc.fc-theme-standard .fc-highlight { + color: #ffffff; + background: #007bff; +} + +.p-orderlist .p-orderlist-controls { + padding: 1.25rem; +} +.p-orderlist .p-orderlist-controls .p-button { + margin-bottom: 0.5rem; +} +.p-orderlist .p-orderlist-header { + background: #efefef; + color: #212529; + border: 1px solid #dee2e6; + padding: 1rem 1.25rem; + font-weight: 600; + border-bottom: 0 none; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-orderlist .p-orderlist-list { + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + padding: 0.5rem 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-orderlist .p-orderlist-list .p-orderlist-item { + padding: 0.5rem 1.5rem; + margin: 0; + border: 0 none; + color: #212529; + background: transparent; + transition: transform 0.15s, box-shadow 0.15s; +} +.p-orderlist .p-orderlist-list .p-orderlist-item:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-orderlist .p-orderlist-list .p-orderlist-item:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-orderlist .p-orderlist-list .p-orderlist-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-orderlist.p-orderlist-striped .p-orderlist-list .p-orderlist-item:nth-child(even) { + background: rgba(0, 0, 0, 0.05); +} +.p-orderlist.p-orderlist-striped .p-orderlist-list .p-orderlist-item:nth-child(even):hover { + background: #e9ecef; +} + +.p-organizationchart .p-organizationchart-node-content.p-organizationchart-selectable-node:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-organizationchart .p-organizationchart-node-content.p-highlight { + background: #007bff; + color: #ffffff; +} +.p-organizationchart .p-organizationchart-node-content.p-highlight .p-node-toggler i { + color: #003e80; +} +.p-organizationchart .p-organizationchart-line-down { + background: #dee2e6; +} +.p-organizationchart .p-organizationchart-line-left { + border-right: 1px solid #dee2e6; + border-color: #dee2e6; +} +.p-organizationchart .p-organizationchart-line-top { + border-top: 1px solid #dee2e6; + border-color: #dee2e6; +} +.p-organizationchart .p-organizationchart-node-content { + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + padding: 1.25rem; +} +.p-organizationchart .p-organizationchart-node-content .p-node-toggler { + background: inherit; + color: inherit; + border-radius: 50%; +} +.p-organizationchart .p-organizationchart-node-content .p-node-toggler:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-paginator { + background: #ffffff; + color: #007bff; + border: solid #dee2e6; + border-width: 0; + padding: 0.75rem; + border-radius: 4px; +} +.p-paginator .p-paginator-first, +.p-paginator .p-paginator-prev, +.p-paginator .p-paginator-next, +.p-paginator .p-paginator-last { + background-color: #ffffff; + border: 1px solid #dee2e6; + color: #007bff; + min-width: 2.357rem; + height: 2.357rem; + margin: 0 0 0 -1px; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-paginator .p-paginator-first:not(.p-disabled):not(.p-highlight):hover, +.p-paginator .p-paginator-prev:not(.p-disabled):not(.p-highlight):hover, +.p-paginator .p-paginator-next:not(.p-disabled):not(.p-highlight):hover, +.p-paginator .p-paginator-last:not(.p-disabled):not(.p-highlight):hover { + background: #e9ecef; + border-color: #dee2e6; + color: #007bff; +} +.p-paginator .p-paginator-first { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.p-paginator .p-paginator-last { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.p-paginator .p-dropdown { + margin-left: 0.5rem; + margin-right: 0.5rem; + height: 2.357rem; +} +.p-paginator .p-dropdown .p-dropdown-label { + padding-right: 0; +} +.p-paginator .p-paginator-page-input { + margin-left: 0.5rem; + margin-right: 0.5rem; +} +.p-paginator .p-paginator-page-input .p-inputtext { + max-width: 2.357rem; +} +.p-paginator .p-paginator-current { + background-color: #ffffff; + border: 1px solid #dee2e6; + color: #007bff; + min-width: 2.357rem; + height: 2.357rem; + margin: 0 0 0 -1px; + padding: 0 0.5rem; +} +.p-paginator .p-paginator-pages .p-paginator-page { + background-color: #ffffff; + border: 1px solid #dee2e6; + color: #007bff; + min-width: 2.357rem; + height: 2.357rem; + margin: 0 0 0 -1px; + transition: box-shadow 0.15s; + border-radius: 0; +} +.p-paginator .p-paginator-pages .p-paginator-page.p-highlight { + background: #007bff; + border-color: #007bff; + color: #ffffff; +} +.p-paginator .p-paginator-pages .p-paginator-page:not(.p-highlight):hover { + background: #e9ecef; + border-color: #dee2e6; + color: #007bff; +} + +.p-picklist .p-picklist-buttons { + padding: 1.25rem; +} +.p-picklist .p-picklist-buttons .p-button { + margin-bottom: 0.5rem; +} +.p-picklist .p-picklist-header { + background: #efefef; + color: #212529; + border: 1px solid #dee2e6; + padding: 1rem 1.25rem; + font-weight: 600; + border-bottom: 0 none; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-picklist .p-picklist-list { + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + padding: 0.5rem 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-picklist .p-picklist-list .p-picklist-item { + padding: 0.5rem 1.5rem; + margin: 0; + border: 0 none; + color: #212529; + background: transparent; + transition: transform 0.15s, box-shadow 0.15s; +} +.p-picklist .p-picklist-list .p-picklist-item:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-picklist .p-picklist-list .p-picklist-item:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-picklist .p-picklist-list .p-picklist-item.p-highlight { + color: #ffffff; + background: #007bff; +} +.p-picklist.p-picklist-striped .p-picklist-list .p-picklist-item:nth-child(even) { + background: rgba(0, 0, 0, 0.05); +} +.p-picklist.p-picklist-striped .p-picklist-list .p-picklist-item:nth-child(even):hover { + background: #e9ecef; +} + +.p-timeline .p-timeline-event-marker { + border: 0 none; + border-radius: 50%; + width: 1rem; + height: 1rem; + background-color: #007bff; +} +.p-timeline .p-timeline-event-connector { + background-color: #dee2e6; +} +.p-timeline.p-timeline-vertical .p-timeline-event-opposite, +.p-timeline.p-timeline-vertical .p-timeline-event-content { + padding: 0 1rem; +} +.p-timeline.p-timeline-vertical .p-timeline-event-connector { + width: 2px; +} +.p-timeline.p-timeline-horizontal .p-timeline-event-opposite, +.p-timeline.p-timeline-horizontal .p-timeline-event-content { + padding: 1rem 0; +} +.p-timeline.p-timeline-horizontal .p-timeline-event-connector { + height: 2px; +} + +.p-tree { + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + padding: 1.25rem; + border-radius: 4px; +} +.p-tree .p-tree-container .p-treenode { + padding: 0.143rem; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content { + border-radius: 4px; + transition: box-shadow 0.15s; + padding: 0.286rem; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content .p-tree-toggler { + margin-right: 0.5rem; + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content .p-tree-toggler:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content .p-tree-toggler:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-tree .p-tree-container .p-treenode .p-treenode-content .p-treenode-icon { + margin-right: 0.5rem; + color: #6c757d; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content .p-checkbox { + margin-right: 0.5rem; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content .p-checkbox .p-indeterminate .p-checkbox-icon { + color: #212529; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-tree .p-tree-container .p-treenode .p-treenode-content.p-highlight { + background: #007bff; + color: #ffffff; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content.p-highlight .p-tree-toggler, +.p-tree .p-tree-container .p-treenode .p-treenode-content.p-highlight .p-treenode-icon { + color: #ffffff; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content.p-highlight .p-tree-toggler:hover, +.p-tree .p-tree-container .p-treenode .p-treenode-content.p-highlight .p-treenode-icon:hover { + color: #ffffff; +} +.p-tree .p-tree-container .p-treenode .p-treenode-content.p-treenode-selectable:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-tree .p-tree-filter-container { + margin-bottom: 0.5rem; +} +.p-tree .p-tree-filter-container .p-tree-filter { + width: 100%; + padding-right: 1.75rem; +} +.p-tree .p-tree-filter-container .p-tree-filter-icon { + right: 0.75rem; + color: #495057; +} +.p-tree .p-treenode-children { + padding: 0 0 0 1rem; +} +.p-tree .p-tree-loading-icon { + font-size: 2rem; +} + +.p-treetable .p-paginator-top { + border-width: 1px 0 0 0; + border-radius: 0; +} +.p-treetable .p-paginator-bottom { + border-width: 1px 0 0 0; + border-radius: 0; +} +.p-treetable .p-treetable-header { + background: #efefef; + color: #212529; + border: solid #dee2e6; + border-width: 1px 0 0 0; + padding: 1rem 1rem; + font-weight: 600; +} +.p-treetable .p-treetable-footer { + background: #efefef; + color: #212529; + border: 1px solid #dee2e6; + border-width: 1px 0 1px 0; + padding: 1rem 1rem; + font-weight: 600; +} +.p-treetable .p-treetable-thead > tr > th { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #dee2e6; + border-width: 1px 0 2px 0; + font-weight: 600; + color: #212529; + background: #ffffff; + transition: box-shadow 0.15s; +} +.p-treetable .p-treetable-tfoot > tr > td { + text-align: left; + padding: 1rem 1rem; + border: 1px solid #dee2e6; + border-width: 1px 0 1px 0; + font-weight: 600; + color: #212529; + background: #ffffff; +} +.p-treetable .p-sortable-column { + outline-color: rgba(38, 143, 255, 0.5); +} +.p-treetable .p-sortable-column .p-sortable-column-icon { + color: #6c757d; + margin-left: 0.5rem; +} +.p-treetable .p-sortable-column .p-sortable-column-badge { + border-radius: 50%; + height: 1.143rem; + min-width: 1.143rem; + line-height: 1.143rem; + color: #ffffff; + background: #007bff; + margin-left: 0.5rem; +} +.p-treetable .p-sortable-column:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-treetable .p-sortable-column:not(.p-highlight):hover .p-sortable-column-icon { + color: #6c757d; +} +.p-treetable .p-sortable-column.p-highlight { + background: #ffffff; + color: #007bff; +} +.p-treetable .p-sortable-column.p-highlight .p-sortable-column-icon { + color: #007bff; +} +.p-treetable .p-treetable-tbody > tr { + background: #ffffff; + color: #212529; + transition: box-shadow 0.15s; +} +.p-treetable .p-treetable-tbody > tr > td { + text-align: left; + border: 1px solid #dee2e6; + border-width: 1px 0 0 0; + padding: 1rem 1rem; +} +.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; + margin-right: 0.5rem; +} +.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler + .p-checkbox { + margin-right: 0.5rem; +} +.p-treetable .p-treetable-tbody > tr > td .p-treetable-toggler + .p-checkbox .p-indeterminate .p-checkbox-icon { + color: #212529; +} +.p-treetable .p-treetable-tbody > tr:focus { + outline: 0.15rem solid rgba(38, 143, 255, 0.5); + outline-offset: -0.15rem; +} +.p-treetable .p-treetable-tbody > tr.p-highlight { + background: #007bff; + color: #ffffff; +} +.p-treetable .p-treetable-tbody > tr.p-highlight .p-treetable-toggler { + color: #ffffff; +} +.p-treetable .p-treetable-tbody > tr.p-highlight .p-treetable-toggler:hover { + color: #ffffff; +} +.p-treetable.p-treetable-hoverable-rows .p-treetable-tbody > tr:not(.p-highlight):hover { + background: #e9ecef; + color: #212529; +} +.p-treetable.p-treetable-hoverable-rows .p-treetable-tbody > tr:not(.p-highlight):hover .p-treetable-toggler { + color: #212529; +} +.p-treetable .p-column-resizer-helper { + background: #007bff; +} +.p-treetable .p-treetable-scrollable-header, +.p-treetable .p-treetable-scrollable-footer { + background: #efefef; +} +.p-treetable .p-treetable-loading-icon { + font-size: 2rem; +} +.p-treetable.p-treetable-gridlines .p-datatable-header { + border-width: 1px 1px 0 1px; +} +.p-treetable.p-treetable-gridlines .p-treetable-footer { + border-width: 0 1px 1px 1px; +} +.p-treetable.p-treetable-gridlines .p-treetable-top { + border-width: 0 1px 0 1px; +} +.p-treetable.p-treetable-gridlines .p-treetable-bottom { + border-width: 0 1px 1px 1px; +} +.p-treetable.p-treetable-gridlines .p-treetable-thead > tr > th { + border-width: 1px; +} +.p-treetable.p-treetable-gridlines .p-treetable-tbody > tr > td { + border-width: 1px; +} +.p-treetable.p-treetable-gridlines .p-treetable-tfoot > tr > td { + border-width: 1px; +} +.p-treetable.p-treetable-sm .p-treetable-header { + padding: 0.875rem 0.875rem; +} +.p-treetable.p-treetable-sm .p-treetable-thead > tr > th { + padding: 0.5rem 0.5rem; +} +.p-treetable.p-treetable-sm .p-treetable-tbody > tr > td { + padding: 0.5rem 0.5rem; +} +.p-treetable.p-treetable-sm .p-treetable-tfoot > tr > td { + padding: 0.5rem 0.5rem; +} +.p-treetable.p-treetable-sm .p-treetable-footer { + padding: 0.5rem 0.5rem; +} +.p-treetable.p-treetable-lg .p-treetable-header { + padding: 1.25rem 1.25rem; +} +.p-treetable.p-treetable-lg .p-treetable-thead > tr > th { + padding: 1.25rem 1.25rem; +} +.p-treetable.p-treetable-lg .p-treetable-tbody > tr > td { + padding: 1.25rem 1.25rem; +} +.p-treetable.p-treetable-lg .p-treetable-tfoot > tr > td { + padding: 1.25rem 1.25rem; +} +.p-treetable.p-treetable-lg .p-treetable-footer { + padding: 1.25rem 1.25rem; +} + +.p-accordion .p-accordion-header .p-accordion-header-link { + padding: 1rem 1.25rem; + border: 1px solid #dee2e6; + color: #212529; + background: #efefef; + font-weight: 600; + border-radius: 4px; + transition: box-shadow 0.15s; +} +.p-accordion .p-accordion-header .p-accordion-header-link .p-accordion-toggle-icon { + margin-right: 0.5rem; +} +.p-accordion .p-accordion-header:not(.p-disabled) .p-accordion-header-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-accordion .p-accordion-header:not(.p-highlight):not(.p-disabled):hover .p-accordion-header-link { + background: #e9ecef; + border-color: #dee2e6; + color: #212529; +} +.p-accordion .p-accordion-header:not(.p-disabled).p-highlight .p-accordion-header-link { + background: #efefef; + border-color: #dee2e6; + color: #212529; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.p-accordion .p-accordion-header:not(.p-disabled).p-highlight:hover .p-accordion-header-link { + border-color: #dee2e6; + background: #e9ecef; + color: #212529; +} +.p-accordion .p-accordion-content { + padding: 1.25rem; + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + border-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-accordion .p-accordion-tab { + margin-bottom: 0; +} +.p-accordion .p-accordion-tab .p-accordion-header .p-accordion-header-link { + border-radius: 0; +} +.p-accordion .p-accordion-tab .p-accordion-content { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.p-accordion .p-accordion-tab:not(:first-child) .p-accordion-header .p-accordion-header-link { + border-top: 0 none; +} +.p-accordion .p-accordion-tab:not(:first-child) .p-accordion-header:not(.p-highlight):not(.p-disabled):hover .p-accordion-header-link, .p-accordion .p-accordion-tab:not(:first-child) .p-accordion-header:not(.p-disabled).p-highlight:hover .p-accordion-header-link { + border-top: 0 none; +} +.p-accordion .p-accordion-tab:first-child .p-accordion-header .p-accordion-header-link { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-accordion .p-accordion-tab:last-child .p-accordion-header:not(.p-highlight) .p-accordion-header-link { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-accordion .p-accordion-tab:last-child .p-accordion-content { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +.p-card { + background: #ffffff; + color: #212529; + box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12); + border-radius: 4px; +} +.p-card .p-card-body { + padding: 1.5rem; +} +.p-card .p-card-title { + font-size: 1.5rem; + font-weight: 700; + margin-bottom: 0.5rem; +} +.p-card .p-card-subtitle { + font-weight: 400; + margin-bottom: 0.5rem; + color: #6c757d; +} +.p-card .p-card-content { + padding: 1rem 0; +} +.p-card .p-card-footer { + padding: 1rem 0 0 0; +} + +.p-fieldset { + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + border-radius: 4px; +} +.p-fieldset .p-fieldset-legend { + padding: 1rem 1.25rem; + border: 1px solid #dee2e6; + color: #212529; + background: #efefef; + font-weight: 600; + border-radius: 4px; +} +.p-fieldset.p-fieldset-toggleable .p-fieldset-legend { + padding: 0; + transition: box-shadow 0.15s; +} +.p-fieldset.p-fieldset-toggleable .p-fieldset-legend a { + padding: 1rem 1.25rem; + color: #212529; + border-radius: 4px; + transition: box-shadow 0.15s; +} +.p-fieldset.p-fieldset-toggleable .p-fieldset-legend a .p-fieldset-toggler { + margin-right: 0.5rem; +} +.p-fieldset.p-fieldset-toggleable .p-fieldset-legend a:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-fieldset.p-fieldset-toggleable .p-fieldset-legend a:hover { + color: #212529; +} +.p-fieldset.p-fieldset-toggleable .p-fieldset-legend:hover { + background: #e9ecef; + border-color: #dee2e6; + color: #212529; +} +.p-fieldset .p-fieldset-content { + padding: 1.25rem; +} + +.p-divider .p-divider-content { + background-color: #ffffff; +} +.p-divider.p-divider-horizontal { + margin: 1rem 0; + padding: 0 1rem; +} +.p-divider.p-divider-horizontal:before { + border-top: 1px #dee2e6; +} +.p-divider.p-divider-horizontal .p-divider-content { + padding: 0 0.5rem; +} +.p-divider.p-divider-vertical { + margin: 0 1rem; + padding: 1rem 0; +} +.p-divider.p-divider-vertical:before { + border-left: 1px #dee2e6; +} +.p-divider.p-divider-vertical .p-divider-content { + padding: 0.5rem 0; +} + +.p-panel .p-panel-header { + border: 1px solid #dee2e6; + padding: 1rem 1.25rem; + background: #efefef; + color: #212529; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-panel .p-panel-header .p-panel-title { + font-weight: 600; +} +.p-panel .p-panel-header .p-panel-header-icon { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-panel .p-panel-header .p-panel-header-icon:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-panel .p-panel-header .p-panel-header-icon:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-panel.p-panel-toggleable .p-panel-header { + padding: 0.5rem 1.25rem; +} +.p-panel .p-panel-content { + padding: 1.25rem; + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + border-top: 0 none; +} +.p-panel .p-panel-footer { + padding: 0.5rem 1.25rem; + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + border-top: 0 none; +} + +.p-scrollpanel .p-scrollpanel-bar { + background: #efefef; + border: 0 none; +} + +.p-splitter { + border: 1px solid #dee2e6; + background: #ffffff; + border-radius: 4px; + color: #212529; +} +.p-splitter .p-splitter-gutter { + transition: box-shadow 0.15s; + background: #efefef; +} +.p-splitter .p-splitter-gutter .p-splitter-gutter-handle { + background: #dee2e6; +} +.p-splitter .p-splitter-gutter-resizing { + background: #dee2e6; +} + +.p-tabview .p-tabview-nav { + background: transparent; + border: 1px solid #dee2e6; + border-width: 0 0 1px 0; +} +.p-tabview .p-tabview-nav li { + margin-right: 0; +} +.p-tabview .p-tabview-nav li .p-tabview-nav-link { + border: solid; + border-width: 1px; + border-color: #ffffff #ffffff #dee2e6 #ffffff; + background: #ffffff; + color: #6c757d; + padding: 0.75rem 1rem; + font-weight: 600; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + transition: box-shadow 0.15s; + margin: 0 0 -1px 0; +} +.p-tabview .p-tabview-nav li .p-tabview-nav-link:not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-tabview .p-tabview-nav li:not(.p-highlight):not(.p-disabled):hover .p-tabview-nav-link { + background: #ffffff; + border-color: #dee2e6; + color: #6c757d; +} +.p-tabview .p-tabview-nav li.p-highlight .p-tabview-nav-link { + background: #ffffff; + border-color: #dee2e6 #dee2e6 #ffffff #dee2e6; + color: #495057; +} +.p-tabview .p-tabview-nav-btn.p-link { + background: #ffffff; + color: #495057; + width: 2.357rem; + box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + border-radius: 0; +} +.p-tabview .p-tabview-nav-btn.p-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-tabview .p-tabview-panels { + background: #ffffff; + padding: 1.25rem; + border: 0 none; + color: #212529; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +.p-toolbar { + background: #efefef; + border: 1px solid #dee2e6; + padding: 1rem 1.25rem; + border-radius: 4px; +} +.p-toolbar .p-toolbar-separator { + margin: 0 0.5rem; +} + +.p-confirm-popup { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + box-shadow: none; +} +.p-confirm-popup .p-confirm-popup-content { + padding: 1.25rem; +} +.p-confirm-popup .p-confirm-popup-footer { + text-align: right; + padding: 0 1.25rem 1.25rem 1.25rem; +} +.p-confirm-popup .p-confirm-popup-footer button { + margin: 0 0.5rem 0 0; + width: auto; +} +.p-confirm-popup .p-confirm-popup-footer button:last-child { + margin: 0; +} +.p-confirm-popup:after { + border: solid transparent; + border-color: rgba(255, 255, 255, 0); + border-bottom-color: #ffffff; +} +.p-confirm-popup:before { + border: solid transparent; + border-color: rgba(0, 0, 0, 0); + border-bottom-color: rgba(0, 0, 0, 0.2); +} +.p-confirm-popup.p-confirm-popup-flipped:after { + border-top-color: #ffffff; +} +.p-confirm-popup.p-confirm-popup-flipped:before { + border-top-color: rgba(0, 0, 0, 0.2); +} +.p-confirm-popup .p-confirm-popup-icon { + font-size: 1.5rem; +} +.p-confirm-popup .p-confirm-popup-message { + margin-left: 1rem; +} + +.p-dialog { + border-radius: 4px; + box-shadow: none; + border: 1px solid rgba(0, 0, 0, 0.2); +} +.p-dialog .p-dialog-header { + border-bottom: 1px solid #e9ecef; + background: #ffffff; + color: #212529; + padding: 1rem; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-dialog .p-dialog-header .p-dialog-title { + font-weight: 600; + font-size: 1.25rem; +} +.p-dialog .p-dialog-header .p-dialog-header-icon { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; + margin-right: 0.5rem; +} +.p-dialog .p-dialog-header .p-dialog-header-icon:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-dialog .p-dialog-header .p-dialog-header-icon:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-dialog .p-dialog-header .p-dialog-header-icon:last-child { + margin-right: 0; +} +.p-dialog .p-dialog-content { + background: #ffffff; + color: #212529; + padding: 1rem; +} +.p-dialog .p-dialog-content:last-of-type { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-dialog .p-dialog-footer { + border-top: 1px solid #e9ecef; + background: #ffffff; + color: #212529; + padding: 1rem; + text-align: right; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-dialog .p-dialog-footer button { + margin: 0 0.5rem 0 0; + width: auto; +} +.p-dialog.p-confirm-dialog .p-confirm-dialog-icon { + font-size: 2rem; +} +.p-dialog.p-confirm-dialog .p-confirm-dialog-message { + margin-left: 1rem; +} + +.p-overlaypanel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + box-shadow: none; +} +.p-overlaypanel .p-overlaypanel-content { + padding: 1.25rem; +} +.p-overlaypanel .p-overlaypanel-close { + background: #007bff; + color: #ffffff; + width: 2rem; + height: 2rem; + transition: box-shadow 0.15s; + border-radius: 50%; + position: absolute; + top: -1rem; + right: -1rem; +} +.p-overlaypanel .p-overlaypanel-close:enabled:hover { + background: #0069d9; + color: #ffffff; +} +.p-overlaypanel:after { + border: solid transparent; + border-color: rgba(255, 255, 255, 0); + border-bottom-color: #ffffff; +} +.p-overlaypanel:before { + border: solid transparent; + border-color: rgba(0, 0, 0, 0); + border-bottom-color: rgba(0, 0, 0, 0.2); +} +.p-overlaypanel.p-overlaypanel-flipped:after { + border-top-color: #ffffff; +} +.p-overlaypanel.p-overlaypanel-flipped:before { + border-top-color: rgba(0, 0, 0, 0.2); +} + +.p-sidebar { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: none; +} +.p-sidebar .p-sidebar-header { + padding: 1rem 1.25rem; +} +.p-sidebar .p-sidebar-header .p-sidebar-close, +.p-sidebar .p-sidebar-header .p-sidebar-icon { + width: 2rem; + height: 2rem; + color: #6c757d; + border: 0 none; + background: transparent; + border-radius: 50%; + transition: box-shadow 0.15s; +} +.p-sidebar .p-sidebar-header .p-sidebar-close:enabled:hover, +.p-sidebar .p-sidebar-header .p-sidebar-icon:enabled:hover { + color: #495057; + border-color: transparent; + background: transparent; +} +.p-sidebar .p-sidebar-header .p-sidebar-close:focus, +.p-sidebar .p-sidebar-header .p-sidebar-icon:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-sidebar .p-sidebar-header + .p-sidebar-content { + padding-top: 0; +} +.p-sidebar .p-sidebar-content { + padding: 1.25rem; +} + +.p-tooltip .p-tooltip-text { + background: #212529; + color: #ffffff; + padding: 0.5rem 0.75rem; + box-shadow: none; + border-radius: 4px; +} +.p-tooltip.p-tooltip-right .p-tooltip-arrow { + border-right-color: #212529; +} +.p-tooltip.p-tooltip-left .p-tooltip-arrow { + border-left-color: #212529; +} +.p-tooltip.p-tooltip-top .p-tooltip-arrow { + border-top-color: #212529; +} +.p-tooltip.p-tooltip-bottom .p-tooltip-arrow { + border-bottom-color: #212529; +} + +.p-fileupload .p-fileupload-buttonbar { + background: #efefef; + padding: 1rem 1.25rem; + border: 1px solid #dee2e6; + color: #212529; + border-bottom: 0 none; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-fileupload .p-fileupload-buttonbar .p-button { + margin-right: 0.5rem; +} +.p-fileupload .p-fileupload-buttonbar .p-button.p-fileupload-choose.p-focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-fileupload .p-fileupload-content { + background: #ffffff; + padding: 2rem 1rem; + border: 1px solid #dee2e6; + color: #212529; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-fileupload .p-progressbar { + height: 0.25rem; +} +.p-fileupload .p-fileupload-row > div { + padding: 1rem 1rem; +} +.p-fileupload.p-fileupload-advanced .p-message { + margin-top: 0; +} + +.p-fileupload-choose:not(.p-disabled):hover { + background: #0069d9; + color: #ffffff; + border-color: #0069d9; +} +.p-fileupload-choose:not(.p-disabled):active { + background: #0062cc; + color: #ffffff; + border-color: #0062cc; +} + +.p-breadcrumb { + background: #efefef; + border: 0 none; + border-radius: 4px; + padding: 1rem; +} +.p-breadcrumb ul li .p-menuitem-link { + transition: box-shadow 0.15s; + border-radius: 4px; +} +.p-breadcrumb ul li .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-breadcrumb ul li .p-menuitem-link .p-menuitem-text { + color: #007bff; +} +.p-breadcrumb ul li .p-menuitem-link .p-menuitem-icon { + color: #007bff; +} +.p-breadcrumb ul li.p-breadcrumb-chevron { + margin: 0 0.5rem 0 0.5rem; + color: #6c757d; +} +.p-breadcrumb ul li:last-child .p-menuitem-text { + color: #6c757d; +} +.p-breadcrumb ul li:last-child .p-menuitem-icon { + color: #6c757d; +} + +.p-contextmenu { + padding: 0.5rem 0; + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + border-radius: 4px; + width: 12.5rem; +} +.p-contextmenu .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; +} +.p-contextmenu .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-contextmenu .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; +} +.p-contextmenu .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-contextmenu .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; +} +.p-contextmenu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; +} +.p-contextmenu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; +} +.p-contextmenu .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; +} +.p-contextmenu .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-contextmenu .p-submenu-list { + padding: 0.5rem 0; + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + border-radius: 4px; +} +.p-contextmenu .p-menuitem.p-menuitem-active > .p-menuitem-link { + background: #e9ecef; +} +.p-contextmenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-contextmenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-icon, .p-contextmenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-contextmenu .p-menu-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; +} +.p-contextmenu .p-submenu-icon { + font-size: 0.875rem; +} + +.p-dock .p-dock-list-container { + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + padding: 0.5rem 0.5rem; + border-radius: 0.5rem; +} +.p-dock .p-dock-item { + padding: 0.5rem; +} +.p-dock .p-dock-action { + width: 4rem; + height: 4rem; +} +.p-dock.p-dock-top .p-dock-item-second-prev, +.p-dock.p-dock-top .p-dock-item-second-next, .p-dock.p-dock-bottom .p-dock-item-second-prev, +.p-dock.p-dock-bottom .p-dock-item-second-next { + margin: 0 0.9rem; +} +.p-dock.p-dock-top .p-dock-item-prev, +.p-dock.p-dock-top .p-dock-item-next, .p-dock.p-dock-bottom .p-dock-item-prev, +.p-dock.p-dock-bottom .p-dock-item-next { + margin: 0 1.3rem; +} +.p-dock.p-dock-top .p-dock-item-current, .p-dock.p-dock-bottom .p-dock-item-current { + margin: 0 1.5rem; +} +.p-dock.p-dock-left .p-dock-item-second-prev, +.p-dock.p-dock-left .p-dock-item-second-next, .p-dock.p-dock-right .p-dock-item-second-prev, +.p-dock.p-dock-right .p-dock-item-second-next { + margin: 0.9rem 0; +} +.p-dock.p-dock-left .p-dock-item-prev, +.p-dock.p-dock-left .p-dock-item-next, .p-dock.p-dock-right .p-dock-item-prev, +.p-dock.p-dock-right .p-dock-item-next { + margin: 1.3rem 0; +} +.p-dock.p-dock-left .p-dock-item-current, .p-dock.p-dock-right .p-dock-item-current { + margin: 1.5rem 0; +} + +@media screen and (max-width: 960px) { + .p-dock.p-dock-top .p-dock-list-container, .p-dock.p-dock-bottom .p-dock-list-container { + overflow-x: auto; + width: 100%; + } + .p-dock.p-dock-top .p-dock-list-container .p-dock-list, .p-dock.p-dock-bottom .p-dock-list-container .p-dock-list { + margin: 0 auto; + } + .p-dock.p-dock-left .p-dock-list-container, .p-dock.p-dock-right .p-dock-list-container { + overflow-y: auto; + height: 100%; + } + .p-dock.p-dock-left .p-dock-list-container .p-dock-list, .p-dock.p-dock-right .p-dock-list-container .p-dock-list { + margin: auto 0; + } + .p-dock .p-dock-list .p-dock-item { + transform: none; + margin: 0; + } +} +.p-megamenu { + padding: 0.5rem 1rem; + background: #efefef; + color: rgba(0, 0, 0, 0.9); + border: 0 none; + border-radius: 4px; +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link { + padding: 1rem; + color: rgba(0, 0, 0, 0.5); + border-radius: 4px; + transition: box-shadow 0.15s; + user-select: none; +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link .p-menuitem-text { + color: rgba(0, 0, 0, 0.5); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link .p-menuitem-icon { + color: rgba(0, 0, 0, 0.5); + margin-right: 0.5rem; +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link .p-submenu-icon { + color: rgba(0, 0, 0, 0.5); + margin-left: 0.5rem; +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover { + background: transparent; +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: rgba(0, 0, 0, 0.7); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: rgba(0, 0, 0, 0.7); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: rgba(0, 0, 0, 0.7); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem > .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link, +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover { + background: transparent; +} +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-text, +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: rgba(0, 0, 0, 0.9); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-icon, +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: rgba(0, 0, 0, 0.9); +} +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link .p-submenu-icon, +.p-megamenu .p-megamenu-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: rgba(0, 0, 0, 0.9); +} +.p-megamenu .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; +} +.p-megamenu .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-megamenu .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; +} +.p-megamenu .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-megamenu .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; +} +.p-megamenu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; +} +.p-megamenu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; +} +.p-megamenu .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; +} +.p-megamenu .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-megamenu .p-megamenu-panel { + background: #ffffff; + color: #212529; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; +} +.p-megamenu .p-megamenu-submenu-header { + margin: 0; + padding: 0.75rem 1rem; + color: #212529; + background: #ffffff; + font-weight: 600; + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-megamenu .p-megamenu-submenu { + padding: 0.5rem 0; + width: 12.5rem; +} +.p-megamenu .p-megamenu-submenu .p-menu-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; +} +.p-megamenu .p-menuitem.p-menuitem-active > .p-menuitem-link { + background: #e9ecef; +} +.p-megamenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-megamenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-icon, .p-megamenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-megamenu.p-megamenu-vertical { + width: 12.5rem; + padding: 0.5rem 0; +} + +.p-menu { + padding: 0.5rem 0; + background: #ffffff; + color: #212529; + border: 1px solid #dee2e6; + border-radius: 4px; + width: 12.5rem; +} +.p-menu .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; +} +.p-menu .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-menu .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; +} +.p-menu .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-menu .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; +} +.p-menu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; +} +.p-menu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; +} +.p-menu .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; +} +.p-menu .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-menu.p-menu-overlay { + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; +} +.p-menu .p-submenu-header { + margin: 0; + padding: 0.75rem 1rem; + color: #212529; + background: #ffffff; + font-weight: 600; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.p-menu .p-menu-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; +} + +.p-menubar { + padding: 0.5rem 1rem; + background: #efefef; + color: rgba(0, 0, 0, 0.9); + border: 0 none; + border-radius: 4px; +} +.p-menubar .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; +} +.p-menubar .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-menubar .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; +} +.p-menubar .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-menubar .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; +} +.p-menubar .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; +} +.p-menubar .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; +} +.p-menubar .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; +} +.p-menubar .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link { + padding: 1rem; + color: rgba(0, 0, 0, 0.5); + border-radius: 4px; + transition: box-shadow 0.15s; + user-select: none; +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-menuitem-text { + color: rgba(0, 0, 0, 0.5); +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-menuitem-icon { + color: rgba(0, 0, 0, 0.5); + margin-right: 0.5rem; +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-submenu-icon { + color: rgba(0, 0, 0, 0.5); + margin-left: 0.5rem; +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover { + background: transparent; +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: rgba(0, 0, 0, 0.7); +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: rgba(0, 0, 0, 0.7); +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: rgba(0, 0, 0, 0.7); +} +.p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link, +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover { + background: transparent; +} +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-text, +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: rgba(0, 0, 0, 0.9); +} +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-icon, +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: rgba(0, 0, 0, 0.9); +} +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link .p-submenu-icon, +.p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: rgba(0, 0, 0, 0.9); +} +.p-menubar .p-submenu-list { + padding: 0.5rem 0; + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + width: 12.5rem; +} +.p-menubar .p-submenu-list .p-menu-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; +} +.p-menubar .p-submenu-list .p-submenu-icon { + font-size: 0.875rem; +} +.p-menubar .p-menuitem.p-menuitem-active > .p-menuitem-link { + background: #e9ecef; +} +.p-menubar .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-menubar .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-icon, .p-menubar .p-menuitem.p-menuitem-active > .p-menuitem-link .p-submenu-icon { + color: #212529; +} + +@media screen and (max-width: 960px) { + .p-menubar { + position: relative; + } + .p-menubar .p-menubar-button { + display: flex; + width: 2rem; + height: 2rem; + color: rgba(0, 0, 0, 0.5); + border-radius: 50%; + transition: box-shadow 0.15s; + } + .p-menubar .p-menubar-button:hover { + color: rgba(0, 0, 0, 0.7); + background: transparent; + } + .p-menubar .p-menubar-button:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + } + .p-menubar .p-menubar-root-list { + position: absolute; + display: none; + padding: 0.5rem 0; + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; + width: 100%; + } + .p-menubar .p-menubar-root-list .p-menu-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; + } + .p-menubar .p-menubar-root-list .p-submenu-icon { + font-size: 0.875rem; + } + .p-menubar .p-menubar-root-list > .p-menuitem { + width: 100%; + position: static; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-menuitem-text { + color: #212529; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link .p-submenu-icon { + color: #212529; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); + } + .p-menubar .p-menubar-root-list > .p-menuitem > .p-menuitem-link > .p-submenu-icon { + margin-left: auto; + transition: transform 0.15s; + } + .p-menubar .p-menubar-root-list > .p-menuitem.p-menuitem-active > .p-menuitem-link > .p-submenu-icon { + transform: rotate(-180deg); + } + .p-menubar .p-menubar-root-list .p-submenu-list { + width: 100%; + position: static; + box-shadow: none; + border: 0 none; + } + .p-menubar .p-menubar-root-list .p-submenu-list .p-submenu-icon { + transition: transform 0.15s; + transform: rotate(90deg); + } + .p-menubar .p-menubar-root-list .p-submenu-list .p-menuitem-active > .p-menuitem-link > .p-submenu-icon { + transform: rotate(-90deg); + } + .p-menubar .p-menubar-root-list .p-menuitem { + width: 100%; + position: static; + } + .p-menubar .p-menubar-root-list ul li a { + padding-left: 2.25rem; + } + .p-menubar .p-menubar-root-list ul li ul li a { + padding-left: 3.75rem; + } + .p-menubar .p-menubar-root-list ul li ul li ul li a { + padding-left: 5.25rem; + } + .p-menubar .p-menubar-root-list ul li ul li ul li ul li a { + padding-left: 6.75rem; + } + .p-menubar .p-menubar-root-list ul li ul li ul li ul li ul li a { + padding-left: 8.25rem; + } + .p-menubar.p-menubar-mobile-active .p-menubar-root-list { + display: flex; + flex-direction: column; + top: 100%; + left: 0; + z-index: 1; + } +} +.p-panelmenu .p-panelmenu-header > a { + padding: 1rem 1.25rem; + border: 1px solid #dee2e6; + color: #212529; + background: #efefef; + font-weight: 600; + border-radius: 4px; + transition: box-shadow 0.15s; +} +.p-panelmenu .p-panelmenu-header > a .p-panelmenu-icon { + margin-right: 0.5rem; +} +.p-panelmenu .p-panelmenu-header > a .p-menuitem-icon { + margin-right: 0.5rem; +} +.p-panelmenu .p-panelmenu-header > a:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-panelmenu .p-panelmenu-header:not(.p-highlight):not(.p-disabled) > a:hover { + background: #e9ecef; + border-color: #dee2e6; + color: #212529; +} +.p-panelmenu .p-panelmenu-header.p-highlight { + margin-bottom: 0; +} +.p-panelmenu .p-panelmenu-header.p-highlight > a { + background: #efefef; + border-color: #dee2e6; + color: #212529; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.p-panelmenu .p-panelmenu-header.p-highlight:not(.p-disabled) > a:hover { + border-color: #dee2e6; + background: #e9ecef; + color: #212529; +} +.p-panelmenu .p-panelmenu-content { + padding: 0.5rem 0; + border: 1px solid #dee2e6; + background: #ffffff; + color: #212529; + border-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-panelmenu .p-panelmenu-content .p-menuitem .p-menuitem-link .p-panelmenu-icon { + margin-right: 0.5rem; +} +.p-panelmenu .p-panelmenu-content .p-submenu-list:not(.p-panelmenu-root-submenu) { + padding: 0 0 0 1rem; +} +.p-panelmenu .p-panelmenu-panel { + margin-bottom: 0; +} +.p-panelmenu .p-panelmenu-panel .p-panelmenu-header > a { + border-radius: 0; +} +.p-panelmenu .p-panelmenu-panel .p-panelmenu-content { + border-radius: 0; +} +.p-panelmenu .p-panelmenu-panel:not(:first-child) .p-panelmenu-header > a { + border-top: 0 none; +} +.p-panelmenu .p-panelmenu-panel:not(:first-child) .p-panelmenu-header:not(.p-highlight):not(.p-disabled):hover > a, .p-panelmenu .p-panelmenu-panel:not(:first-child) .p-panelmenu-header:not(.p-disabled).p-highlight:hover > a { + border-top: 0 none; +} +.p-panelmenu .p-panelmenu-panel:first-child .p-panelmenu-header > a { + border-top-right-radius: 4px; + border-top-left-radius: 4px; +} +.p-panelmenu .p-panelmenu-panel:last-child .p-panelmenu-header:not(.p-highlight) > a { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.p-panelmenu .p-panelmenu-panel:last-child .p-panelmenu-content { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +.p-steps .p-steps-item .p-menuitem-link { + background: transparent; + transition: box-shadow 0.15s; + border-radius: 4px; + background: transparent; +} +.p-steps .p-steps-item .p-menuitem-link .p-steps-number { + color: #212529; + border: 1px solid #dee2e6; + background: transparent; + min-width: 2rem; + height: 2rem; + line-height: 2rem; + font-size: 1.143rem; + z-index: 1; + border-radius: 4px; +} +.p-steps .p-steps-item .p-menuitem-link .p-steps-title { + margin-top: 0.5rem; + color: #6c757d; +} +.p-steps .p-steps-item .p-menuitem-link:not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-steps .p-steps-item.p-highlight .p-steps-number { + background: #007bff; + color: #ffffff; +} +.p-steps .p-steps-item.p-highlight .p-steps-title { + font-weight: 600; + color: #212529; +} +.p-steps .p-steps-item:before { + content: " "; + border-top: 1px solid #dee2e6; + width: 100%; + top: 50%; + left: 0; + display: block; + position: absolute; + margin-top: -1rem; +} + +.p-tabmenu .p-tabmenu-nav { + background: transparent; + border: 1px solid #dee2e6; + border-width: 0 0 1px 0; +} +.p-tabmenu .p-tabmenu-nav .p-tabmenuitem { + margin-right: 0; +} +.p-tabmenu .p-tabmenu-nav .p-tabmenuitem .p-menuitem-link { + border: solid; + border-width: 1px; + border-color: #ffffff #ffffff #dee2e6 #ffffff; + background: #ffffff; + color: #6c757d; + padding: 0.75rem 1rem; + font-weight: 600; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + transition: box-shadow 0.15s; + margin: 0 0 -1px 0; +} +.p-tabmenu .p-tabmenu-nav .p-tabmenuitem .p-menuitem-link .p-menuitem-icon { + margin-right: 0.5rem; +} +.p-tabmenu .p-tabmenu-nav .p-tabmenuitem .p-menuitem-link:not(.p-disabled):focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-tabmenu .p-tabmenu-nav .p-tabmenuitem:not(.p-highlight):not(.p-disabled):hover .p-menuitem-link { + background: #ffffff; + border-color: #dee2e6; + color: #6c757d; +} +.p-tabmenu .p-tabmenu-nav .p-tabmenuitem.p-highlight .p-menuitem-link { + background: #ffffff; + border-color: #dee2e6 #dee2e6 #ffffff #dee2e6; + color: #495057; +} + +.p-tieredmenu { + padding: 0.5rem 0; + background: #ffffff; + color: #212529; + border: 1px solid #dee2e6; + border-radius: 4px; + width: 12.5rem; +} +.p-tieredmenu .p-menuitem-link { + padding: 0.75rem 1rem; + color: #212529; + border-radius: 0; + transition: box-shadow 0.15s; + user-select: none; +} +.p-tieredmenu .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-tieredmenu .p-menuitem-link .p-menuitem-icon { + color: #212529; + margin-right: 0.5rem; +} +.p-tieredmenu .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-tieredmenu .p-menuitem-link:not(.p-disabled):hover { + background: #e9ecef; +} +.p-tieredmenu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-text { + color: #212529; +} +.p-tieredmenu .p-menuitem-link:not(.p-disabled):hover .p-menuitem-icon { + color: #212529; +} +.p-tieredmenu .p-menuitem-link:not(.p-disabled):hover .p-submenu-icon { + color: #212529; +} +.p-tieredmenu .p-menuitem-link:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: inset 0 0 0 0.15rem rgba(38, 143, 255, 0.5); +} +.p-tieredmenu.p-tieredmenu-overlay { + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; +} +.p-tieredmenu .p-submenu-list { + padding: 0.5rem 0; + background: #ffffff; + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: none; +} +.p-tieredmenu .p-menuitem.p-menuitem-active > .p-menuitem-link { + background: #e9ecef; +} +.p-tieredmenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-text { + color: #212529; +} +.p-tieredmenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-menuitem-icon, .p-tieredmenu .p-menuitem.p-menuitem-active > .p-menuitem-link .p-submenu-icon { + color: #212529; +} +.p-tieredmenu .p-menu-separator { + border-top: 1px solid #dee2e6; + margin: 0.5rem 0; +} +.p-tieredmenu .p-submenu-icon { + font-size: 0.875rem; +} + +.p-inline-message { + padding: 0.5rem 0.75rem; + margin: 0; + border-radius: 4px; +} +.p-inline-message.p-inline-message-info { + background: #cce5ff; + border: solid #b8daff; + border-width: 0px; + color: #004085; +} +.p-inline-message.p-inline-message-info .p-inline-message-icon { + color: #004085; +} +.p-inline-message.p-inline-message-success { + background: #d4edda; + border: solid #c3e6cb; + border-width: 0px; + color: #155724; +} +.p-inline-message.p-inline-message-success .p-inline-message-icon { + color: #155724; +} +.p-inline-message.p-inline-message-warn { + background: #fff3cd; + border: solid #ffeeba; + border-width: 0px; + color: #856404; +} +.p-inline-message.p-inline-message-warn .p-inline-message-icon { + color: #856404; +} +.p-inline-message.p-inline-message-error { + background: #f8d7da; + border: solid #f5c6cb; + border-width: 0px; + color: #721c24; +} +.p-inline-message.p-inline-message-error .p-inline-message-icon { + color: #721c24; +} +.p-inline-message .p-inline-message-icon { + font-size: 1rem; + margin-right: 0.5rem; +} +.p-inline-message .p-inline-message-text { + font-size: 1rem; +} +.p-inline-message.p-inline-message-icon-only .p-inline-message-icon { + margin-right: 0; +} + +.p-message { + margin: 1rem 0; + border-radius: 4px; +} +.p-message .p-message-wrapper { + padding: 1rem 1.25rem; +} +.p-message .p-message-close { + width: 2rem; + height: 2rem; + border-radius: 50%; + background: transparent; + transition: box-shadow 0.15s; +} +.p-message .p-message-close:hover { + background: rgba(255, 255, 255, 0.3); +} +.p-message .p-message-close:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-message.p-message-info { + background: #cce5ff; + border: solid #b8daff; + border-width: 1px; + color: #004085; +} +.p-message.p-message-info .p-message-icon { + color: #004085; +} +.p-message.p-message-info .p-message-close { + color: #004085; +} +.p-message.p-message-success { + background: #d4edda; + border: solid #c3e6cb; + border-width: 1px; + color: #155724; +} +.p-message.p-message-success .p-message-icon { + color: #155724; +} +.p-message.p-message-success .p-message-close { + color: #155724; +} +.p-message.p-message-warn { + background: #fff3cd; + border: solid #ffeeba; + border-width: 1px; + color: #856404; +} +.p-message.p-message-warn .p-message-icon { + color: #856404; +} +.p-message.p-message-warn .p-message-close { + color: #856404; +} +.p-message.p-message-error { + background: #f8d7da; + border: solid #f5c6cb; + border-width: 1px; + color: #721c24; +} +.p-message.p-message-error .p-message-icon { + color: #721c24; +} +.p-message.p-message-error .p-message-close { + color: #721c24; +} +.p-message .p-message-text { + font-size: 1rem; + font-weight: 500; +} +.p-message .p-message-icon { + font-size: 1.5rem; + margin-right: 0.5rem; +} + +.p-toast { + opacity: 1; +} +.p-toast .p-toast-message { + margin: 0 0 1rem 0; + box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + border-radius: 4px; +} +.p-toast .p-toast-message .p-toast-message-content { + padding: 1rem; + border-width: 0; +} +.p-toast .p-toast-message .p-toast-message-content .p-toast-message-text { + margin: 0 0 0 1rem; +} +.p-toast .p-toast-message .p-toast-message-content .p-toast-message-icon { + font-size: 2rem; +} +.p-toast .p-toast-message .p-toast-message-content .p-toast-summary { + font-weight: 700; +} +.p-toast .p-toast-message .p-toast-message-content .p-toast-detail { + margin: 0.5rem 0 0 0; +} +.p-toast .p-toast-message .p-toast-icon-close { + width: 2rem; + height: 2rem; + border-radius: 50%; + background: transparent; + transition: box-shadow 0.15s; +} +.p-toast .p-toast-message .p-toast-icon-close:hover { + background: rgba(255, 255, 255, 0.3); +} +.p-toast .p-toast-message .p-toast-icon-close:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} +.p-toast .p-toast-message.p-toast-message-info { + background: #cce5ff; + border: solid #b8daff; + border-width: 1px; + color: #004085; +} +.p-toast .p-toast-message.p-toast-message-info .p-toast-message-icon, +.p-toast .p-toast-message.p-toast-message-info .p-toast-icon-close { + color: #004085; +} +.p-toast .p-toast-message.p-toast-message-success { + background: #d4edda; + border: solid #c3e6cb; + border-width: 1px; + color: #155724; +} +.p-toast .p-toast-message.p-toast-message-success .p-toast-message-icon, +.p-toast .p-toast-message.p-toast-message-success .p-toast-icon-close { + color: #155724; +} +.p-toast .p-toast-message.p-toast-message-warn { + background: #fff3cd; + border: solid #ffeeba; + border-width: 1px; + color: #856404; +} +.p-toast .p-toast-message.p-toast-message-warn .p-toast-message-icon, +.p-toast .p-toast-message.p-toast-message-warn .p-toast-icon-close { + color: #856404; +} +.p-toast .p-toast-message.p-toast-message-error { + background: #f8d7da; + border: solid #f5c6cb; + border-width: 1px; + color: #721c24; +} +.p-toast .p-toast-message.p-toast-message-error .p-toast-message-icon, +.p-toast .p-toast-message.p-toast-message-error .p-toast-icon-close { + color: #721c24; +} + +.p-galleria .p-galleria-close { + margin: 0.5rem; + background: transparent; + color: #efefef; + width: 4rem; + height: 4rem; + transition: box-shadow 0.15s; + border-radius: 4px; +} +.p-galleria .p-galleria-close .p-galleria-close-icon { + font-size: 2rem; +} +.p-galleria .p-galleria-close:hover { + background: rgba(255, 255, 255, 0.1); + color: #efefef; +} +.p-galleria .p-galleria-item-nav { + background: transparent; + color: #efefef; + width: 4rem; + height: 4rem; + transition: box-shadow 0.15s; + border-radius: 4px; + margin: 0 0.5rem; +} +.p-galleria .p-galleria-item-nav .p-galleria-item-prev-icon, +.p-galleria .p-galleria-item-nav .p-galleria-item-next-icon { + font-size: 2rem; +} +.p-galleria .p-galleria-item-nav:not(.p-disabled):hover { + background: rgba(255, 255, 255, 0.1); + color: #efefef; +} +.p-galleria .p-galleria-caption { + background: rgba(0, 0, 0, 0.5); + color: #efefef; + padding: 1rem; +} +.p-galleria .p-galleria-indicators { + padding: 1rem; +} +.p-galleria .p-galleria-indicators .p-galleria-indicator button { + background-color: #e9ecef; + width: 1rem; + height: 1rem; + transition: box-shadow 0.15s; + border-radius: 4px; +} +.p-galleria .p-galleria-indicators .p-galleria-indicator button:hover { + background: #dee2e6; +} +.p-galleria .p-galleria-indicators .p-galleria-indicator.p-highlight button { + background: #007bff; + color: #ffffff; +} +.p-galleria.p-galleria-indicators-bottom .p-galleria-indicator, .p-galleria.p-galleria-indicators-top .p-galleria-indicator { + margin-right: 0.5rem; +} +.p-galleria.p-galleria-indicators-left .p-galleria-indicator, .p-galleria.p-galleria-indicators-right .p-galleria-indicator { + margin-bottom: 0.5rem; +} +.p-galleria.p-galleria-indicator-onitem .p-galleria-indicators { + background: rgba(0, 0, 0, 0.5); +} +.p-galleria.p-galleria-indicator-onitem .p-galleria-indicators .p-galleria-indicator button { + background: rgba(255, 255, 255, 0.4); +} +.p-galleria.p-galleria-indicator-onitem .p-galleria-indicators .p-galleria-indicator button:hover { + background: rgba(255, 255, 255, 0.6); +} +.p-galleria.p-galleria-indicator-onitem .p-galleria-indicators .p-galleria-indicator.p-highlight button { + background: #007bff; + color: #ffffff; +} +.p-galleria .p-galleria-thumbnail-container { + background: rgba(0, 0, 0, 0.9); + padding: 1rem 0.25rem; +} +.p-galleria .p-galleria-thumbnail-container .p-galleria-thumbnail-prev, +.p-galleria .p-galleria-thumbnail-container .p-galleria-thumbnail-next { + margin: 0.5rem; + background-color: transparent; + color: #efefef; + width: 2rem; + height: 2rem; + transition: box-shadow 0.15s; + border-radius: 4px; +} +.p-galleria .p-galleria-thumbnail-container .p-galleria-thumbnail-prev:hover, +.p-galleria .p-galleria-thumbnail-container .p-galleria-thumbnail-next:hover { + background: rgba(255, 255, 255, 0.1); + color: #efefef; +} +.p-galleria .p-galleria-thumbnail-container .p-galleria-thumbnail-item-content:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-galleria-mask { + --maskbg: rgba(0, 0, 0, 0.9); +} + +.p-image-mask { + --maskbg: rgba(0, 0, 0, 0.9); +} + +.p-image-preview-indicator { + background-color: transparent; + color: #f8f9fa; + transition: box-shadow 0.15s; +} + +.p-image-preview-container:hover > .p-image-preview-indicator { + background-color: rgba(0, 0, 0, 0.5); +} + +.p-image-toolbar { + padding: 1rem; +} + +.p-image-action.p-link { + color: #f8f9fa; + background-color: transparent; + width: 3rem; + height: 3rem; + border-radius: 50%; + transition: box-shadow 0.15s; + margin-right: 0.5rem; +} +.p-image-action.p-link:last-child { + margin-right: 0; +} +.p-image-action.p-link:hover { + color: #f8f9fa; + background-color: rgba(255, 255, 255, 0.1); +} +.p-image-action.p-link i { + font-size: 1.5rem; +} + +.p-avatar { + background-color: #dee2e6; + border-radius: 4px; +} +.p-avatar.p-avatar-lg { + width: 3rem; + height: 3rem; + font-size: 1.5rem; +} +.p-avatar.p-avatar-lg .p-avatar-icon { + font-size: 1.5rem; +} +.p-avatar.p-avatar-xl { + width: 4rem; + height: 4rem; + font-size: 2rem; +} +.p-avatar.p-avatar-xl .p-avatar-icon { + font-size: 2rem; +} + +.p-avatar-group .p-avatar { + border: 2px solid #ffffff; +} + +.p-badge { + background: #007bff; + color: #ffffff; + font-size: 0.75rem; + font-weight: 700; + min-width: 1.5rem; + height: 1.5rem; + line-height: 1.5rem; +} +.p-badge.p-badge-secondary { + background-color: #6c757d; + color: #ffffff; +} +.p-badge.p-badge-success { + background-color: #28a745; + color: #ffffff; +} +.p-badge.p-badge-info { + background-color: #17a2b8; + color: #ffffff; +} +.p-badge.p-badge-warning { + background-color: #ffc107; + color: #212529; +} +.p-badge.p-badge-danger { + background-color: #dc3545; + color: #ffffff; +} +.p-badge.p-badge-lg { + font-size: 1.125rem; + min-width: 2.25rem; + height: 2.25rem; + line-height: 2.25rem; +} +.p-badge.p-badge-xl { + font-size: 1.5rem; + min-width: 3rem; + height: 3rem; + line-height: 3rem; +} + +.p-chip { + background-color: #dee2e6; + color: #212529; + border-radius: 16px; + padding: 0 0.75rem; +} +.p-chip .p-chip-text { + line-height: 1.5; + margin-top: 0.25rem; + margin-bottom: 0.25rem; +} +.p-chip .p-chip-icon { + margin-right: 0.5rem; +} +.p-chip img { + width: 2rem; + height: 2rem; + margin-left: -0.75rem; + margin-right: 0.5rem; +} +.p-chip .p-chip-remove-icon { + margin-left: 0.5rem; + border-radius: 4px; + transition: box-shadow 0.15s; +} +.p-chip .p-chip-remove-icon:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-inplace .p-inplace-display { + padding: 0.5rem 0.75rem; + border-radius: 4px; + transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; +} +.p-inplace .p-inplace-display:not(.p-disabled):hover { + background: #e9ecef; + color: #212529; +} +.p-inplace .p-inplace-display:focus { + outline: 0 none; + outline-offset: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.p-progressbar { + border: 0 none; + height: 1.5rem; + background: #e9ecef; + border-radius: 4px; +} +.p-progressbar .p-progressbar-value { + border: 0 none; + margin: 0; + background: #007bff; +} +.p-progressbar .p-progressbar-label { + color: #ffffff; + line-height: 1.5rem; +} + +.p-scrolltop { + width: 3rem; + height: 3rem; + border-radius: 4px; + box-shadow: none; + transition: box-shadow 0.15s; +} +.p-scrolltop.p-link { + background: rgba(0, 0, 0, 0.7); +} +.p-scrolltop.p-link:hover { + background: rgba(0, 0, 0, 0.8); +} +.p-scrolltop .p-scrolltop-icon { + font-size: 1.5rem; + color: #efefef; +} + +.p-skeleton { + background-color: #e9ecef; + border-radius: 4px; +} +.p-skeleton:after { + background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0)); +} + +.p-tag { + background: #007bff; + color: #ffffff; + font-size: 0.75rem; + font-weight: 700; + padding: 0.25rem 0.4rem; + border-radius: 4px; +} +.p-tag.p-tag-success { + background-color: #28a745; + color: #ffffff; +} +.p-tag.p-tag-info { + background-color: #17a2b8; + color: #ffffff; +} +.p-tag.p-tag-warning { + background-color: #ffc107; + color: #212529; +} +.p-tag.p-tag-danger { + background-color: #dc3545; + color: #ffffff; +} +.p-tag .p-tag-icon { + margin-right: 0.25rem; + font-size: 0.75rem; +} + +.p-terminal { + background: #ffffff; + color: #212529; + border: 1px solid #dee2e6; + padding: 1.25rem; +} +.p-terminal .p-terminal-input { + font-size: 1rem; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; +} + +/* Vendor extensions to the designer enhanced bootstrap compatibility */ +.p-breadcrumb .p-breadcrumb-chevron { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; +} +.p-breadcrumb .p-breadcrumb-chevron:before { + content: "/"; +} + +.fc.fc-theme-standard .fc-highlight { + background: #cce5ff; +} + +/* Customizations to the designer theme should be defined here */ +.p-inputtext { + padding: 0.375rem 0.75rem; +} + +.p-inputtext:enabled:focus { + color: #212529; + background-color: #fff; + border-color: #86b7fe; + outline: 0; + box-shadow: 0 0 0 .25rem rgba(13,110,253,.25); +} diff --git a/public/css/components/vue-datepicker.css b/public/css/components/vue-datepicker.css new file mode 100644 index 000000000..c2551b9d8 --- /dev/null +++ b/public/css/components/vue-datepicker.css @@ -0,0 +1,46 @@ +@import '../../../vendor/vuejs/vuedatepicker_css/main.css'; +:root { + /*General*/ + --dp-font-family: var(--bs-body-font-family); + --dp-border-radius: .25rem; + --dp-cell-border-radius: .25rem; /*Specific border radius for the calendar cell*/ + + /*Sizing*/ + --dp-button-height: 2.1875rem; /*Size for buttons in overlays*/ + --dp-month-year-row-height: 2.1875rem; /*Height of the month-year select row*/ + --dp-month-year-row-button-size: 2.1875rem; /*Specific height for the next/previous buttons*/ + --dp-button-icon-height: 1.25rem; /*Icon sizing in buttons*/ + --dp-cell-size: 2.1875rem; /*Width and height of calendar cell*/ + --dp-cell-padding: .3125rem; /*Padding in the cell*/ + --dp-common-padding: .625rem; /*Common padding used*/ + --dp-input-icon-padding: 2.1875rem; + --dp-input-padding: .375rem .75rem; + --dp-action-buttons-padding: .125rem .3125rem; /*Adjust padding for the action buttons in action row*/ + --dp-row-margin: .3125rem 0; /*Adjust the spacing between rows in the calendar*/ + --dp-calendar-header-cell-padding: 0.5rem; /*Adjust padding in calendar header cells*/ + --dp-two-calendars-spacing: .625rem; /*Space between multiple calendars*/ + --dp-overlay-col-padding: .1875rem; /*Padding in the overlay column*/ + --dp-time-inc-dec-button-size: 2rem; /*Sizing for arrow buttons in the time picker*/ + --dp-menu-padding: .375rem .5rem; /*Menu padding*/ +} +.dp__theme_light { + --dp-text-color: var(--text-color); + --dp-primary-color: var(--bs-primary); + --dp-primary-disabled-color: rgba(var(--bs-primary-rgb), .65); + --dp-primary-text-color: var(--primary-color-text); + --dp-secondary-color: var(--bs-secondary); + --dp-border-color: var(--bs-gray-400); + --dp-menu-border-color: var(--bs-gray-400); + --dp-border-color-hover: var(--bs-gray-400); + --dp-icon-color: rgba(var(--bs-black-rgb), .5); + --dp-hover-icon-color: rgba(var(--bs-black-rgb), .75); + --dp-range-between-dates-text-color: var(--dp-hover-text-color, var(--text-color)); +} +.dp__theme_light .form-control.is-invalid { + --dp-border-color-hover: var(--bs-danger); +} +.dp__theme_light .form-control.is-valid { + --dp-border-color-hover: var(--bs-success); +} +.form-control.is-invalid ~ .dp__clear_icon, +.was-validated .form-control:invalid ~ .dp__clear_icon { margin-right: calc(1.5em + .75rem - 12px) } diff --git a/public/js/api/fhcapifactory.js b/public/js/api/fhcapifactory.js new file mode 100644 index 000000000..41c89ef50 --- /dev/null +++ b/public/js/api/fhcapifactory.js @@ -0,0 +1,30 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import search from "./search.js"; +import phrasen from "./phrasen.js"; +import navigation from "./navigation.js"; +import filter from "./filter.js"; +import studstatus from "./studstatus.js"; + +export default { + search, + phrasen, + navigation, + filter, + studstatus +}; diff --git a/public/js/api/filter.js b/public/js/api/filter.js new file mode 100644 index 000000000..a5920c758 --- /dev/null +++ b/public/js/api/filter.js @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + saveCustomFilter(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/saveCustomFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + customFilterName: wsParams.customFilterName + } + ); + }, + removeCustomFilter(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/removeCustomFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterId: wsParams.filterId + } + ); + }, + applyFilterFields(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/applyFilterFields', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterFields: wsParams.filterFields + } + ); + }, + addFilterField(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/addFilterField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterField: wsParams.filterField + } + ); + }, + removeFilterField(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/removeFilterField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterField: wsParams.filterField + } + ); + }, + getFilterById(wsParams) { + return this.$fhcApi.get( + '/api/frontend/v1/filter/getFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterId: wsParams.filterId + } + ); + }, + getFilter(wsParams) { + return this.$fhcApi.get( + '/api/frontend/v1/filter/getFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType + } + ); + } +}; + diff --git a/public/js/api/navigation.js b/public/js/api/navigation.js new file mode 100644 index 000000000..05006331f --- /dev/null +++ b/public/js/api/navigation.js @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + getHeader(navigation_page) { + return this.$fhcApi.get( + '/api/frontend/v1/navigation/header', + { navigation_page } + ); + }, + getMenu: function(navigation_page) { + return this.$fhcApi.get( + '/api/frontend/v1/navigation/menu', + { navigation_page } + ); + } +}; + diff --git a/public/js/api/phrasen.js b/public/js/api/phrasen.js new file mode 100644 index 000000000..896641bcf --- /dev/null +++ b/public/js/api/phrasen.js @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + loadCategory(category) { + return this.$fhcApi.get('/api/frontend/v1/phrasen/loadModule/' + category); + } +}; diff --git a/public/js/api/search.js b/public/js/api/search.js new file mode 100644 index 000000000..4655d8fa8 --- /dev/null +++ b/public/js/api/search.js @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + search(searchsettings) { + const url = '/api/frontend/v1/searchbar/search'; + return this.$fhcApi.post(url, searchsettings); + }, + searchdummy(searchsettings) { + const url = 'public/js/apps/api/dummyapi.php/Search'; + return this.$fhcApi.post(url, searchsettings); + } +}; \ No newline at end of file diff --git a/public/js/api/studstatus.js b/public/js/api/studstatus.js new file mode 100644 index 000000000..87d6840b1 --- /dev/null +++ b/public/js/api/studstatus.js @@ -0,0 +1,223 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + abmeldung: { + getDetails(antrag_id, prestudent_id) { + const url = '/api/frontend/v1/studstatus/abmeldung/' + + (antrag_id !== undefined ? 'getDetailsForAntrag/' + antrag_id : 'getDetailsForNewAntrag/' + prestudent_id); + return this.$fhcApi.get(url); + }, + create(stdsem, prestudent_id, grund) { + return this.$fhcApi.post('/api/frontend/v1/studstatus/abmeldung/createAntrag', { + studiensemester: stdsem, + prestudent_id, + grund + }, { + errorHandling: 'strict' + }); + }, + cancel(antrag_id) { + if (!Array.isArray(antrag_id)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/abmeldung/cancelAntrag', + { antrag_id } + ); + return Promise.allSettled(antrag_id.map(antrag => this.$fhcApi.post( + '/api/frontend/v1/studstatus/abmeldung/cancelAntrag', + { antrag_id: antrag.studierendenantrag_id }, + { errorHeader: '#' + antrag.studierendenantrag_id } + ))); + } + }, + unterbrechung: { + getDetails(antrag_id, prestudent_id) { + const url = '/api/frontend/v1/studstatus/unterbrechung/' + + (antrag_id !== undefined ? 'getDetailsForAntrag/' + antrag_id : 'getDetailsForNewAntrag/' + prestudent_id); + return this.$fhcApi.get(url); + }, + create(studiensemester, prestudent_id, grund, datum_wiedereinstieg, attachment) { + return this.$fhcApi.post('/api/frontend/v1/studstatus/unterbrechung/createAntrag', { + studiensemester, + prestudent_id, + grund, + datum_wiedereinstieg, + attachment + }, { + errorHandling: 'strict' + }); + }, + cancel(antrag_id) { + return this.$fhcApi.post('/api/frontend/v1/studstatus/unterbrechung/cancelAntrag', { + antrag_id + }, { + errorHandling: 'strict' + }); + } + }, + wiederholung: { + getDetails(prestudent_id) { + const url = '/api/frontend/v1/studstatus/wiederholung/getDetailsForNewAntrag/' + prestudent_id; + return this.$fhcApi.get(url) + }, + getLvs(antrag_id) { + const url = '/api/frontend/v1/studstatus/wiederholung/getLvs/' + antrag_id; + return this.$fhcApi.get(url) + }, + create(prestudent_id, studiensemester) { + return this.$fhcApi.post('/api/frontend/v1/studstatus/wiederholung/createAntrag', { + prestudent_id, + studiensemester + }, { + errorHandling: 'strict' + }); + }, + cancel(prestudent_id, studiensemester) { + return this.$fhcApi.post('/api/frontend/v1/studstatus/wiederholung/cancelAntrag', { + prestudent_id, + studiensemester + }, { + errorHandling: 'strict' + }); + }, + saveLvs(forbiddenLvs, mandatoryLvs) { + return this.$fhcApi.post('/api/frontend/v1/studstatus/wiederholung/saveLvs', { + forbiddenLvs, + mandatoryLvs + }); + } + }, + leitung: { + getStgs() { + return this.$fhcApi.get('/api/frontend/v1/studstatus/leitung/getActiveStgs'); + }, + getAntraege(url, config, params) { + return this.$fhcApi + .get('/api/frontend/v1/studstatus/leitung/getAntraege/' + url) + .then(res => res.data); // Return data for tabulator + }, + getHistory(antrag_id) { + return this.$fhcApi.get('/api/frontend/v1/studstatus/leitung/getHistory/' + antrag_id) + }, + getPrestudents(query, signal) { + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/getPrestudents', + { query }, + { + signal: signal, + timeout: 30000 + } + ); + }, + approve(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/approveAntrag', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/approveAntrag', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + reject(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/rejectAntrag', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/rejectAntrag', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + reopen(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/reopenAntrag', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/reopenAntrag', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + pause(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/pauseAntrag', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/pauseAntrag', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + unpause(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/unpauseAntrag', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/unpauseAntrag', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + object(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/objectAntrag', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/objectAntrag', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + approveObjection(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/approveObjection', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/approveObjection', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + }, + denyObjection(antrag) { + if (!Array.isArray(antrag)) + return this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/denyObjection', + antrag + ); + return Promise.allSettled(antrag.map(a => this.$fhcApi.post( + '/api/frontend/v1/studstatus/leitung/denyObjection', + a, + { errorHeader: '#' + a.studierendenantrag_id } + ))); + } + } +}; \ No newline at end of file diff --git a/public/js/apps/Bismeldestichtag/Bismeldestichtag.js b/public/js/apps/Bismeldestichtag/Bismeldestichtag.js index 79b73a2e5..04275d76a 100644 --- a/public/js/apps/Bismeldestichtag/Bismeldestichtag.js +++ b/public/js/apps/Bismeldestichtag/Bismeldestichtag.js @@ -24,6 +24,8 @@ import {CoreRESTClient} from '../../RESTClient.js'; import {CoreFetchCmpt} from '../../components/Fetch.js'; import {BismeldestichtagAPIs} from './API.js'; +import Phrasen from '../../plugin/Phrasen.js'; + const bismeldestichtagApp = Vue.createApp({ data: function() { return { @@ -187,4 +189,4 @@ const bismeldestichtagApp = Vue.createApp({ } }); -bismeldestichtagApp.mount('#main'); +bismeldestichtagApp.use(Phrasen).mount('#main'); diff --git a/public/js/apps/LogsViewer/LogsViewer.js b/public/js/apps/LogsViewer/LogsViewer.js index 84798c3e1..8aefa1d67 100755 --- a/public/js/apps/LogsViewer/LogsViewer.js +++ b/public/js/apps/LogsViewer/LogsViewer.js @@ -21,6 +21,8 @@ import {LogsViewerTabulatorEventHandlers} from './TabulatorSetup.js'; import {CoreFilterCmpt} from '../../components/filter/Filter.js'; import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js'; +import Phrasen from '../../plugin/Phrasen.js'; + const logsViewerApp = Vue.createApp({ data: function() { return { @@ -40,5 +42,5 @@ const logsViewerApp = Vue.createApp({ } }); -logsViewerApp.mount('#main'); +logsViewerApp.use(Phrasen).mount('#main'); diff --git a/public/js/apps/TestSearch.js b/public/js/apps/TestSearch.js index d4aa35312..469aaeb09 100755 --- a/public/js/apps/TestSearch.js +++ b/public/js/apps/TestSearch.js @@ -1,195 +1,195 @@ -import {CoreFilterCmpt} from '../components/Filter.js'; -import {CoreNavigationCmpt} from '../components/Navigation.js'; -import verticalsplit from "../components/verticalsplit/verticalsplit.js"; -import searchbar from "../components/searchbar/searchbar.js"; -import fhcapifactory from "./api/fhcapifactory.js"; +import {CoreFilterCmpt} from '../components/filter/Filter.js'; +import {CoreNavigationCmpt} from '../components/navigation/Navigation.js'; +import CoreVerticalsplit from "../components/verticalsplit/verticalsplit.js"; +import CoreSearchbar from "../components/searchbar/searchbar.js"; +import FhcApi from "../plugin/FhcApi.js"; -Vue.$fhcapi = fhcapifactory; - -Vue.createApp({ - "data": function() { - return { - "title": "Test Search", - "appSideMenuEntries": {}, - "searchbaroptions": { - "types": [ - "person", - "raum", - "mitarbeiter", - "student", - "prestudent", - "document", - "cms", - "organisationunit" - ], - "actions": { - "person": { - "defaultaction": { - "type": "link", - "action": function(data) { - //alert('person defaultaction ' + JSON.stringify(data)); - //window.location.href = data.profil; - return data.profil; - } - }, - "childactions": [ - { - "label": "testchildaction1", - "icon": "fas fa-check-circle", - "type": "function", - "action": function(data) { - alert('person testchildaction 01 ' + JSON.stringify(data)); - } - }, - { - "label": "testchildaction2", - "icon": "fas fa-file-csv", - "type": "function", - "action": function(data) { - alert('person testchildaction 02 ' + JSON.stringify(data)); - } - } - ] - }, - "raum": { - "defaultaction": { - "type": "function", - "action": function(data) { - alert('raum defaultaction ' + JSON.stringify(data)); - } - }, - "childactions": [ - { - "label": "Rauminformation", - "icon": "fas fa-info-circle", - "type": "link", - "action": function(data) { - return data.infolink; - } - }, - { - "label": "Raumreservierung", - "icon": "fas fa-bookmark", - "type": "link", - "action": function(data) { - return data.booklink; - } - } - ] - }, - "employee": { - "defaultaction": { - "type": "function", - "action": function(data) { - alert('employee defaultaction ' + JSON.stringify(data)); - } - }, - "childactions": [ - { - "label": "testchildaction1", - "icon": "fas fa-address-book", - "type": "function", - "action": function(data) { - alert('employee testchildaction 01 ' + JSON.stringify(data)); - } - }, - { - "label": "testchildaction2", - "icon": "fas fa-user-slash", - "type": "function", - "action": function(data) { - alert('employee testchildaction 02 ' + JSON.stringify(data)); - } - }, - { - "label": "testchildaction3", - "icon": "fas fa-bell", - "type": "function", - "action": function(data) { - alert('employee testchildaction 03 ' + JSON.stringify(data)); - } - }, - { - "label": "testchildaction4", - "icon": "fas fa-calculator", - "type": "function", - "action": function(data) { - alert('employee testchildaction 04 ' + JSON.stringify(data)); - } - } - ] - }, - "organisationunit": { - "defaultaction": { - "type": "function", - "action": function(data) { - alert('organisationunit defaultaction ' + JSON.stringify(data)); - } - }, - "childactions": [] - } - } - }, - "searchbaroptions2": { - "types": [ - "raum", - "organisationunit" - ], - "actions": { - "raum": { - "defaultaction": { - "type": "function", - "action": function(data) { - alert('raum defaultaction ' + JSON.stringify(data)); - } - }, - "childactions": [ - { - "label": "Rauminformation", - "icon": "fas fa-info-circle", - "type": "link", - "action": function(data) { - return data.infolink; - } - }, - { - "label": "Raumreservierung", - "icon": "fas fa-bookmark", - "type": "link", - "action": function(data) { - return data.booklink; - } - } - ] - }, - "organisationunit": { - "defaultaction": { - "type": "function", - "action": function(data) { - alert('organisationunit defaultaction ' + JSON.stringify(data)); - } - }, - "childactions": [] - } - } - } - }; - }, - "components": { - "CoreNavigationCmpt": CoreNavigationCmpt, - "CoreFilterCmpt": CoreFilterCmpt, - "verticalsplit": verticalsplit, - "searchbar": searchbar - }, - "methods": { - "newSideMenuEntryHandler": function(payload) { - this.appSideMenuEntries = payload; - }, - "searchfunction": function(searchsettings) { - return Vue.$fhcapi.Search.search(searchsettings); - }, - "searchfunctiondummy": function(searchsettings) { - return Vue.$fhcapi.Search.searchdummy(searchsettings); - } - } -}).mount('#main'); +const app = Vue.createApp({ + components: { + CoreNavigationCmpt, + CoreFilterCmpt, + CoreVerticalsplit, + CoreSearchbar + }, + data() { + return { + title: "Test Search", + appSideMenuEntries: {}, + searchbaroptions: { + types: [ + "person", + "raum", + "mitarbeiter", + "student", + "prestudent", + "document", + "cms", + "organisationunit" + ], + actions: { + person: { + defaultaction: { + type: "link", + action(data) { + //alert('person defaultaction ' + JSON.stringify(data)); + //window.location.href = data.profil; + return data.profil; + } + }, + childactions: [ + { + label: "testchildaction1", + icon: "fas fa-check-circle", + type: "function", + action(data) { + alert('person testchildaction 01 ' + JSON.stringify(data)); + } + }, + { + label: "testchildaction2", + icon: "fas fa-file-csv", + type: "function", + action(data) { + alert('person testchildaction 02 ' + JSON.stringify(data)); + } + } + ] + }, + raum: { + defaultaction: { + type: "function", + action(data) { + alert('raum defaultaction ' + JSON.stringify(data)); + } + }, + childactions: [ + { + label: "Rauminformation", + icon: "fas fa-info-circle", + type: "link", + action(data) { + return data.infolink; + } + }, + { + label: "Raumreservierung", + icon: "fas fa-bookmark", + type: "link", + action(data) { + return data.booklink; + } + } + ] + }, + employee: { + defaultaction: { + type: "function", + action(data) { + alert('employee defaultaction ' + JSON.stringify(data)); + } + }, + childactions: [ + { + label: "testchildaction1", + icon: "fas fa-address-book", + type: "function", + action(data) { + alert('employee testchildaction 01 ' + JSON.stringify(data)); + } + }, + { + label: "testchildaction2", + icon: "fas fa-user-slash", + type: "function", + action(data) { + alert('employee testchildaction 02 ' + JSON.stringify(data)); + } + }, + { + label: "testchildaction3", + icon: "fas fa-bell", + type: "function", + action(data) { + alert('employee testchildaction 03 ' + JSON.stringify(data)); + } + }, + { + label: "testchildaction4", + icon: "fas fa-calculator", + type: "function", + action(data) { + alert('employee testchildaction 04 ' + JSON.stringify(data)); + } + } + ] + }, + organisationunit: { + defaultaction: { + type: "function", + action(data) { + alert('organisationunit defaultaction ' + JSON.stringify(data)); + } + }, + childactions: [] + } + } + }, + searchbaroptions2: { + types: [ + "raum", + "organisationunit" + ], + actions: { + raum: { + defaultaction: { + type: "function", + action(data) { + alert('raum defaultaction ' + JSON.stringify(data)); + } + }, + childactions: [ + { + label: "Rauminformation", + icon: "fas fa-info-circle", + type: "link", + action(data) { + return data.infolink; + } + }, + { + label: "Raumreservierung", + icon: "fas fa-bookmark", + type: "link", + action(data) { + return data.booklink; + } + } + ] + }, + organisationunit: { + defaultaction: { + type: "function", + action(data) { + alert('organisationunit defaultaction ' + JSON.stringify(data)); + } + }, + childactions: [] + } + } + } + }; + }, + methods: { + newSideMenuEntryHandler(payload) { + this.appSideMenuEntries = payload; + }, + searchfunction(searchsettings) { + return this.$fhcApi.factory.search.search(searchsettings); + }, + searchfunctiondummy(searchsettings) { + return this.$fhcApi.factory.search.searchdummy(searchsettings); + } + } +}); +app.use(FhcApi) +app.mount('#main'); diff --git a/public/js/apps/lehre/Antrag.js b/public/js/apps/lehre/Antrag.js index 00a5877b3..d0c8ab89a 100755 --- a/public/js/apps/lehre/Antrag.js +++ b/public/js/apps/lehre/Antrag.js @@ -1,12 +1,10 @@ import StudierendenantragAntrag from "../../components/Studierendenantrag/Antrag.js"; import StudierendenantragStatus from "../../components/Studierendenantrag/Status.js"; import StudierendenantragInfoblock from "../../components/Studierendenantrag/Infoblock.js"; -import VueDatePicker from "../../components/vueDatepicker.js.php"; import Phrasen from '../../plugin/Phrasen.js'; const app = Vue.createApp({ components: { - VueDatePicker, StudierendenantragAntrag, StudierendenantragStatus, StudierendenantragInfoblock diff --git a/public/js/components/Fetch.js b/public/js/components/Fetch.js index 9a34e1a3f..2f7985649 100755 --- a/public/js/components/Fetch.js +++ b/public/js/components/Fetch.js @@ -99,8 +99,10 @@ export const CoreFetchCmpt = { * */ errorHandler: function(error) { - if (error.response.data.retval) + if (error.response?.data?.retval) this.setError(error.response.data.retval); + else if (error.data?.message) + this.setError(error.data.message); else this.setError(error.message); }, diff --git a/public/js/components/Form/Form.js b/public/js/components/Form/Form.js index 2257eb289..1d75f6af7 100755 --- a/public/js/components/Form/Form.js +++ b/public/js/components/Form/Form.js @@ -9,7 +9,8 @@ export default { $registerToForm: component => { if (this.inputs.indexOf(component) < 0) this.inputs.push(component); - } + }, + $clearValidationForName: this.clearValidationForName }; }, props: { @@ -40,9 +41,34 @@ export default { return a; }, {}); + }, + factory() { + const factory = Object.create(Object.getPrototypeOf(this.$fhcApi.factory), Object.getOwnPropertyDescriptors(this.$fhcApi.factory)); + factory.$fhcApi = { + get: this.get, + post: this.post, + _defaultErrorHandlers: this.$fhcApi._defaultErrorHandlers + }; + return factory; } }, methods: { + get(...args) { + if (typeof args[0] == 'object' && args[0].clearValidation && args[0].setFeedback) + args[0] = this; + else + args.unshift(this); + + return this.$fhcApi.get(...args); + }, + post(...args) { + if (typeof args[0] == 'object' && args[0].clearValidation && args[0].setFeedback) + args[0] = this; + else + args.unshift(this); + + return this.$fhcApi.post(...args); + }, _sendFeedbackToInput(inputs, feedback, valid) { if (inputs.length) { inputs.forEach(input => input.setFeedback(valid, feedback)); @@ -128,6 +154,10 @@ export default { } }); }); + }, + clearValidationForName(name) { + (this.sortedInputs[name.split('.')[0] + name.split('.').slice(1).map(p => `[${p}]`).join("")] || this.sortedInputs['_default'] || []) + .forEach(input => input.clearValidation()); } }, template: ` diff --git a/public/js/components/Form/Input.js b/public/js/components/Form/Input.js index 1fc318523..7395e9480 100755 --- a/public/js/components/Form/Input.js +++ b/public/js/components/Form/Input.js @@ -7,9 +7,16 @@ export default { components: { FhcFragment }, - inject: [ - '$registerToForm' - ], + inject: { + registerToForm: { + from: '$registerToForm', + default: null + }, + clearValidationForName: { + from: '$clearValidationForName', + default: null + } + }, props: { bsFeedback: Boolean, noAutoClass: Boolean, @@ -22,7 +29,8 @@ export default { data() { return { valid: undefined, - feedback: [] + feedback: [], + modelValueDummy: undefined } }, computed: { @@ -31,8 +39,9 @@ export default { return true; if (this.containerClass) return true; - if (this.autoContainerClass) - return true; + for (const prop in this.autoContainerClass) + if (Object.hasOwn(this.autoContainerClass, prop)) + return true; return false; }, acc() { @@ -111,13 +120,19 @@ export default { if (!c.includes('form-check-input') && !c.includes('btn-check')) classes.push('form-check-input'); break; + case 'color': + if (!c.includes('form-control-color')) + classes.push('form-control-color'); + if (!c.includes('form-control')) + classes.push('form-control'); + break; case 'autocomplete': case 'datepicker': classes.push('p-0'); classes.push('border-0'); - case 'color': - if (!c.includes('form-control-color')) - classes.push('form-control-color'); + if (!c.includes('form-control')) + classes.push('form-control'); + break; case 'text': case 'number': case 'password': @@ -145,9 +160,13 @@ export default { }, modelValueCmp: { get() { + if (this.$attrs.modelValue === undefined) + return this.modelValueDummy; return this.$attrs.modelValue; }, set(v) { + if (this.$attrs.modelValue === undefined) + this.modelValueDummy = v; this.$emit('update:modelValue', v); } }, @@ -171,12 +190,26 @@ export default { this.valid = undefined; this.feedback = []; }, + clearValidationForThisName() { + if (this.valid === undefined) + return; + if (this.clearValidationForName && this.name) + this.clearValidationForName(this.name); + else + this.clearValidation(); + }, setFeedback(valid, feedback) { if (!feedback) feedback = []; if (!Array.isArray(feedback)) feedback = [feedback]; this.valid = valid; + // NOTE(chris): On a list of radios/checkboxes only add the feedback message to the last item + if (this.name && (this.lcType == 'radio' || this.lcType == 'checkbox')) { + const selector = 'input[type="' + this.lcType + '"][name="' + this.name + '"]'; + if ([...this.$el.parentNode.querySelectorAll(selector)].pop() != this.$refs.input) + return; + } this.feedback = feedback; }, _loadComponents() { @@ -198,19 +231,20 @@ export default { this._loadComponents(); }, mounted() { - if (this.$registerToForm) - this.$registerToForm(this); + if (this.registerToForm) + this.registerToForm(this); }, template: ` - - - + + diff --git a/public/js/components/Form/Upload/Dms.js b/public/js/components/Form/Upload/Dms.js index 3184a7e0b..fe58fa74b 100755 --- a/public/js/components/Form/Upload/Dms.js +++ b/public/js/components/Form/Upload/Dms.js @@ -53,29 +53,29 @@ export default { }, watch: { modelValue(n) { - if (n instanceof FileList) - return this.$refs.upload.files = n; - - const dt = new DataTransfer(); - const dms = []; - for (var file of n) { - if (file instanceof File) { - dt.items.add(file); - } else { - const dmsFile = new File([JSON.stringify(file)], file.name, { - type: 'application/x.fhc-dms+json' - }); - dt.items.add(dmsFile); - } + if (n instanceof FileList) + return this.$refs.upload.files = n; + + const dt = new DataTransfer(); + const dms = []; + for (var file of n) { + if (file instanceof File) { + dt.items.add(file); + } else { + const dmsFile = new File([JSON.stringify(file)], file.name, { + type: 'application/x.fhc-dms+json' + }); + dt.items.add(dmsFile); } - this.$emit('update:modelValue', dt.files); - }, + } + this.$emit('update:modelValue', dt.files); + } }, template: `
    -
  • +
  • {{ file.name }}