Compare commits

...

148 Commits

Author SHA1 Message Date
chfhtw 011e93720e phrasen dashboard 2026-04-23 15:08:42 +02:00
chfhtw dd13c73415 code quality 2026-04-23 14:04:49 +02:00
chfhtw fbc5f95340 hide drag and drop features from screenreaders because they is no implementation for using them with screenreaders yet 2026-04-23 10:54:57 +02:00
chfhtw ff3a25a5ec missing CSS styles for dashboard item header 2026-04-23 10:43:12 +02:00
chfhtw 2a21bbf062 add list semantics to drop grid for accessibility 2026-04-23 10:41:08 +02:00
chfhtw d9a80e5ef7 use footer like header inside article 2026-04-23 09:50:08 +02:00
chfhtw 2cadee1599 remove id => bad practice 2026-04-23 09:48:04 +02:00
chfhtw 6c26fde210 comments & code quality 2026-04-23 09:47:49 +02:00
chfhtw bc908b7fe9 use computed classes for the resize button instead of if-else in template 2026-04-23 09:41:18 +02:00
chfhtw 26d468aa6f more semantics and code quality 2026-04-23 09:28:23 +02:00
chfhtw 0f7188a347 remove id (it's bad practice) and we don't need it anymore 2026-04-22 15:35:05 +02:00
chfhtw 3e832f9526 remove unused resizeOverlay 2026-04-22 15:32:59 +02:00
chfhtw 392cfbdc4e remove margin from h5 2026-04-22 15:32:10 +02:00
chfhtw 0751aa5a0f basic semantics 2026-04-22 14:36:55 +02:00
chfhtw 6c818e5c30 add dragClick to the additionalRow button 2026-04-22 11:01:58 +02:00
chfhtw 20f043abc6 ignore additionRow when overwriteRows is active & only activate overwriteRows if it is bigger 2026-04-22 11:01:35 +02:00
chfhtw 416451eb0b remove marginForExtraRow and add the required space via CSS 2026-04-22 10:23:51 +02:00
chfhtw 3ab7a61a47 handle additionalRow disabling outside Grid.js and recreate behavior before commit a8f680810f 2026-04-22 10:03:13 +02:00
chfhtw 5571353464 let items jump over pinned items (this may be especially relevant if pinned items trap one or more items involved in a drag and drop operation) 2026-04-22 09:37:50 +02:00
chfhtw 95d85c7f5b temporary change height on dragging if the reordering process needs it (otherwise we get some ugly overflow issue on the preview item or if another item is moved to the bottom it vanishes during the drag and drop operation and reappears after the drop) 2026-04-22 09:36:11 +02:00
chfhtw d7e509979a account for additionalRow prop when computing placeholders (missed this in 262b170244) 2026-04-22 09:32:53 +02:00
chfhtw 91a5b2d4fc fire correct method for draganddrop cancel 2026-04-22 09:28:20 +02:00
chfhtw 38ea481177 add safety measures to add function of GridLogic 2026-04-21 13:49:39 +02:00
chfhtw dbf945dfe5 Comments for GridLogic 2026-04-21 13:48:49 +02:00
chfhtw 21065a3c95 because of 4ab9056700 pinned property can't be deleted (will get overwritten by the original value) so work around that with boolean values that are later removed if false 2026-04-21 13:47:08 +02:00
chfhtw 79b5defb63 accidentially deleted a colon in 89e0326435 2026-04-21 13:37:53 +02:00
chfhtw 9a113e2993 remove prop that is not needed anymore 2026-04-21 09:35:17 +02:00
chfhtw 89e0326435 code quality 2026-04-21 09:34:15 +02:00
chfhtw 28d65ac114 replace tmpLoading with an abortcontroller 2026-04-20 15:22:52 +02:00
chfhtw 239577e9cf safeguard watchers; move indexedItems watcher code into a method and reuse it in cols watcher 2026-04-20 15:17:07 +02:00
chfhtw a8fb45adc6 undo parts of 187b4a6e4b to prevent having no gridlogic available 2026-04-20 15:03:06 +02:00
chfhtw 9316016d24 get selected funktionen the vuejs way 2026-04-20 14:59:35 +02:00
chfhtw 33b5c370b1 code quality 2026-04-20 14:41:28 +02:00
chfhtw cdf63840b0 simplify widgetAdd, widgetUpdate and widgetRemove 2026-04-20 14:30:31 +02:00
chfhtw cf14501311 handle initial position and size in Grid.js no Section.js & add weights & add size properties of updated items to return value in GridLogic.add() 2026-04-20 14:16:21 +02:00
chfhtw 187b4a6e4b guard indexedItems watcher so it won't run unnecessarily 2026-04-20 14:05:16 +02:00
chfhtw 4ab9056700 deep clone instead of shallow to prevent watchers from executing prematurely 2026-04-20 12:53:03 +02:00
chfhtw 9a281dfa71 don't save source property for widgets since it get generated when fetched 2026-04-20 12:51:19 +02:00
chfhtw 0b3f7d1fe3 code quality 2026-04-20 12:50:20 +02:00
chfhtw a9d82de25c handle custom prop like other internal props 2026-04-20 12:47:48 +02:00
Harald Bamberger 6cc09969dd Merge branch 'master' into feature-68530/Dashboard_Cleanup_Admin 2026-04-20 10:45:20 +02:00
Harald Bamberger 98a10a2f55 Merge branch 'feature-69389/AbmeldungSTGL_Anzeige_mit_Studiengangskuerzel' 2026-04-17 12:37:49 +02:00
Harald Bamberger e48b94b858 studiengangskuerzel statt kurzbzlang 2026-04-17 12:35:55 +02:00
chfhtw ff08ca140c remove unused variable 2026-04-17 12:22:12 +02:00
chfhtw 61a9feb8fd rearrange code and call preventDefault only if moving/resizing would be successful 2026-04-17 12:19:56 +02:00
chfhtw 21fdf31518 move checkPinnedWidgetAnimation into dragOver function 2026-04-17 12:10:37 +02:00
chfhtw 3af9397689 move removeWidgetClones and this.mode=MODE_IDLE into _cleanupDragging function 2026-04-17 12:08:03 +02:00
chfhtw fef756f508 change denied-dragging-animation to drop-grid-item-blocker and call it the vuejs way instead of vanilla js 2026-04-17 11:44:25 +02:00
chfhtw 131edf1293 move check into updateCursor block, it only needs to check if the hovered tile changes 2026-04-17 11:41:31 +02:00
chfhtw 6787b9b553 correct pinned widget detection => before it only accounted for 1x1 widgets 2026-04-17 10:46:00 +02:00
chfhtw 97baaf6797 rename items_placeholders => placeholders 2026-04-17 10:21:54 +02:00
chfhtw 4e88765a83 remove unused function 2026-04-17 10:19:27 +02:00
chfhtw aac26f6720 replace with faster logic 2026-04-17 10:02:34 +02:00
chfhtw 3b3e75003f indentation 2026-04-17 10:01:13 +02:00
chfhtw ab699aafdc using variables for better readability 2026-04-17 09:59:38 +02:00
chfhtw 98bdb8c526 bugfix: if the moving object is bigger than 1x1 and its target location touches the original location swapping could locate the occupier inside the overlapping slot making the occupier overlap the moving object 2026-04-17 09:57:24 +02:00
chfhtw 992cb6b310 !some(!true) is the same as every(true) and we don't need to check for the originalFrame since only the moving item was in there and no other item hence no occupier 2026-04-16 16:00:01 +02:00
chfhtw 5b5f6ac0b9 missed one line in 8ab83eaf41 2026-04-16 15:49:35 +02:00
Andreas Österreicher 0ff29ba6af Merge branch 'epic-56039/LV-Evaluierung' 2026-04-16 13:22:40 +02:00
chfhtw 4b064f566a get information if resize would be successful from tempPositionUpdates instead of gridlogic 2026-04-16 11:53:23 +02:00
Harald Bamberger ba543448ae Merge branch 'bug-76260/StudVW_Messages_TinyMCE_Cursor_Jumping_TextInput' 2026-04-16 11:26:32 +02:00
chfhtw 2a86a70386 gridlogic: save width & height on move action similar to resize action (see: 88c4a04aea) 2026-04-16 11:24:03 +02:00
Harald Bamberger f121f9b5a2 deactivate pagination - since potentially msg threads are not shown completely 2026-04-16 11:23:57 +02:00
chfhtw 8ab83eaf41 don't use place in gridlogic -> the current values should be in the root of the object 2026-04-16 11:16:22 +02:00
chfhtw 262b170244 utilize getFreeSlots from gridlogic to create placeholders 2026-04-16 11:00:34 +02:00
chfhtw e21f35b880 easier more straightforward way to computed free slots in gridlogic 2026-04-16 10:58:47 +02:00
chfhtw 24c8a1c501 Decode JSON in backend not frontend and make component path a full path (also in backend) 2026-04-16 09:51:32 +02:00
chfhtw cfe6e3c805 rearrange and comment dropgrid for better understanding 2026-04-16 09:27:12 +02:00
Harald Bamberger 88b22f5490 revert to v-if to not render element when not necessary and use Vue.nextTick for dependent code 2026-04-15 17:02:58 +02:00
Harald Bamberger 4b7ee9abe1 Merge branch 'feature-70376/Lohnguide' 2026-04-15 15:52:12 +02:00
chfhtw d3b62daea0 not needed anymore since "resizeOverlay" is now handled by css classes (see previous commit) 2026-04-15 15:50:10 +02:00
chfhtw 35355b28c0 use css classes instead of temporary items 2026-04-15 13:56:35 +02:00
chfhtw 88c82a41ba gridlogic: return null for impossible updates instead of throwing an error 2026-04-15 13:55:07 +02:00
chfhtw 910e960e4f code quality 2026-04-15 13:54:30 +02:00
Cristina d499619cf3 Added phrase endedatumMussInZukunftLiegen 2026-04-15 13:39:06 +02:00
Cristina f489153ff3 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-04-15 12:25:05 +02:00
Cristina 9b79a07fa2 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-04-15 12:24:44 +02:00
Harald Bamberger 6ce14a25d7 Merge branch 'master' into feature-70376/Lohnguide 2026-04-15 11:49:26 +02:00
Werner Masik c701d92779 fix db_update 2026-04-15 11:15:10 +02:00
Werner Masik 73e03ba901 Gehaltstyp Überstundenpauschale und Sachbezug PKW 2026-04-15 10:44:03 +02:00
chfhtw d1911f0f96 add shared cleanup function to prevent duplicate code 2026-04-15 10:14:56 +02:00
ma0068 95a7797ae9 delete unused apicall for mode modal
add editor.setContent to function getReplyData
readd loadReplyData and use v-show for visibleDiv for mode inSamePage
2026-04-14 15:46:34 +02:00
chfhtw 09a5515121 replace checkWidgetSizeLimitAnimation function with simple condition statement and remove now unused helper function 2026-04-14 14:35:50 +02:00
chfhtw 328fe4256e safeguard component loading from widgetTemplate (=widgetSetup) 2026-04-14 11:44:30 +02:00
chfhtw c240eb4a4e move loading animation inside component root element 2026-04-14 11:30:13 +02:00
chfhtw 38d9d91945 get rid of cachedWidgetLoader & slightly rename some prop for better understanding 2026-04-14 11:12:26 +02:00
chfhtw 4669598dd9 remove resizeLimit function and replace it with internal function using the widgetsSetup prop 2026-04-14 10:42:17 +02:00
chfhtw d68fa8ce95 code quality dashboard.css 2026-04-13 14:03:28 +02:00
chfhtw d61ee51d79 rename css class to dash-case 2026-04-13 14:00:14 +02:00
chfhtw a6f81006be hide content of dashboard item on drag not via event but via css class 2026-04-13 13:28:15 +02:00
chfhtw 5fa374259e replace draggedItem css class 2026-04-13 13:22:16 +02:00
chfhtw 9fd033b30e get rid of toggleDraggedItemOverlay and replace it with css classes that are computed inside the template 2026-04-13 13:21:35 +02:00
chfhtw e98ed3c74f rename function to clarify what it does 2026-04-13 11:47:26 +02:00
chfhtw ebe76821e4 remove unused mode 2026-04-13 11:45:50 +02:00
chfhtw 3858e38a02 remove unused code 2026-04-13 11:13:38 +02:00
chfhtw 510c35e077 simplify drop grid events 2026-04-13 10:50:11 +02:00
chfhtw a8f680810f remove unnecessary touch and mouse events from dashboard 2026-04-13 10:45:54 +02:00
Andreas Österreicher 3a91b12f31 Merge branch 'epic-56039/LV-Evaluierung' 2026-04-13 10:39:00 +02:00
chfhtw 6c90ccfbaa add drag-drop-touch-js/dragdroptouch to composer and use it to add drag and drop functionality for touch devices 2026-04-13 10:38:58 +02:00
ma0068 843894405e changes for NewDiv
remove Watcher for formData Fields
add predefault settings for tabulator fields
add setContent for Editor
change text for closing window/tab
show alertSuccess for sending Message just in case of inSamePage
2026-04-10 13:03:24 +02:00
chfhtw 653a320e6c Display section name only in admin mode & display source information for widgets (from which section it is) in non-admin mode 2026-04-10 12:57:58 +02:00
ma0068 8fddbc3a32 delete watcher for formData fields, add setContent for loading Vorlage 2026-04-10 09:15:34 +02:00
chfhtw 57e7ad6903 don't render hidden widgets in default (non edit) mode 2026-04-08 15:55:56 +02:00
chfhtw 290564fd2f bigger padding for dashboard items in mobile view 2026-04-08 15:55:07 +02:00
chfhtw b9207b5efb make drop grid padding configurable via css 2026-04-08 15:54:36 +02:00
chfhtw c58715d95b dashboard css remove doubles 2026-04-08 15:52:57 +02:00
chfhtw 5c6a8b9966 code quality dashboard css 2026-04-08 15:52:33 +02:00
chfhtw dd713a26db replace inline styles with bootstrap class 2026-04-08 15:07:42 +02:00
chfhtw fad293fbbf code quality dashboard section 2026-04-08 15:00:36 +02:00
chfhtw 5a1b94f45b Remove redundant Dashboardpage and load correct one in Users Landingpage 2026-04-08 14:27:56 +02:00
chfhtw 344c68bf08 remove deprecated file 2026-04-08 14:20:12 +02:00
chfhtw 8ca5849b14 get timezone inside the components that require it 2026-04-08 09:45:08 +02:00
chfhtw 01e6a1061c get renderers inside the components that require it 2026-04-07 13:43:50 +02:00
Werner Masik 5c463c0866 add vordienstzeit to lohnguide 2026-04-06 22:25:13 +02:00
Werner Masik 423bbd95a6 add vordienstzeit to lohnguide 2026-04-06 22:25:12 +02:00
chfhtw 7eb888d2e3 rename apps/Dashboard/Fhc to apps/Cis 2026-04-03 11:19:38 +02:00
chfhtw 26f67b6798 rename apps/Cis to apps/Cis/Menu 2026-04-03 11:16:19 +02:00
chfhtw e6eed4be4e move hidden widgets to the bottom of the dashboard 2026-04-02 16:36:04 +02:00
Cristina 386cc779bf Merge branch 'master' into epic-56039/LV-Evaluierung 2026-04-02 15:49:20 +02:00
chfhtw 4d9ff395e9 remove reorder which is not used anymore 2026-04-02 14:52:17 +02:00
Cristina 08c6d58a50 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-04-02 14:24:39 +02:00
Cristina 3f53c5feba Added: method getKFLByUID to get Kompetenzfeldleitung by UID 2026-04-02 14:23:59 +02:00
chfhtw 114a50ad4e Comments & variable renames for better understanding 2026-04-02 11:43:29 +02:00
chfhtw 218f434e01 stabilize workaround for chrome by adding more code into the workaround and get rid of updateCursorOnMouseMove in the process 2026-03-30 16:49:21 +02:00
chfhtw 8a53c438e3 more cleanup of dropgrid code 2026-03-30 15:01:03 +02:00
chfhtw 3fe15a302c correct calculation of allowed resize directions 2026-03-30 14:14:06 +02:00
chfhtw dec83bbd21 move draggable from item to the buttons 2026-03-30 13:37:26 +02:00
chfhtw 2354746d4f more cleanup of dropgrid code & cleanup of dashboard item code 2026-03-30 13:34:34 +02:00
chfhtw d421b1ccb8 cleanup dropgrid code 2026-03-30 09:29:05 +02:00
chfhtw 00e019d6fe cleanup db data 2026-03-26 15:21:41 +01:00
Harald Bamberger 5b0c115b10 Merge branch 'master' into feature-68530/Dashboard_Cleanup_Admin 2026-03-25 17:06:52 +01:00
chfhtw d37fac0ff7 cleanup api calls to dashboard board 2026-03-25 16:12:17 +01:00
chfhtw 91d656ff60 remove unnecessary function 2026-03-25 16:11:52 +01:00
Harald Bamberger 627a52e3d1 Merge branch 'master' into feature-70376/Lohnguide 2026-03-25 09:36:27 +01:00
Harald Bamberger 059b13938e Merge branch 'master' into feature-70376/Lohnguide 2026-03-18 11:46:27 +01:00
Werner Masik 6b816def31 add lohnguide to vertragsbestandteil SQL 2026-03-05 15:34:51 +01:00
Werner Masik 5fbcf588ed fix vertragsbestandteil lohnguide 2026-03-05 14:29:56 +01:00
Werner Masik 41b2a6d1d4 added db migration for lohnguide 2026-03-04 10:53:30 +01:00
Werner Masik e054f1222b basic model and factory for lohnguide 2026-03-03 11:50:45 +01:00
Cristina c57eb1b8de Adapted method getLvLeitung: filter Dummy and allow only active Benutzer/Person 2026-03-02 11:00:05 +01:00
Cristina d1015956d1 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-02-25 10:54:29 +01:00
Cristina 726fce9fac Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-02-25 10:52:50 +01:00
Cristina 27a91de5f6 Added app lvevaluierung to system.tbl_app 2026-01-22 15:51:05 +01:00
Cristina 7ccc26c878 Added lvevaluierung phrasen for STGL Übersichtsseite 2026-01-22 15:41:25 +01:00
Cristina 9ebc847e8e Added lvevaluierung phrasen for Lektoren Übersichtsseite 2026-01-22 14:59:00 +01:00
Cristina 511b04c1f8 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-01-21 18:01:55 +01:00
Cristina ec90d35e02 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-01-21 18:01:24 +01:00
ma0068 38e8f91fdf add kurzbzlang to studentDropdown suggestion 2025-11-25 10:48:57 +01:00
62 changed files with 3934 additions and 1938 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ class Auth extends FHC_Controller
if ($this->form_validation->run())
{
redirect($this->authlib->getLandingPage('/CisVue/Dashboard'));
redirect($this->authlib->getLandingPage('/Cis4'));
}
else
{
@@ -1,43 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*
*/
class Dashboard extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/benutzer:r'
)
);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @return void
*/
public function index()
{
$this->load->model('person/Person_model','PersonModel');
$personData = getData($this->PersonModel->getByUid(getAuthUID()))[0];
$viewData = array(
'uid' => getAuthUID(),
'name' => $personData->vorname,
'person_id' => $personData->person_id
);
$this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData]);
}
}
@@ -40,11 +40,32 @@ class Board extends FHCAPI_Controller
public function list()
{
$this->DashboardModel->addSelect('dashboard_id');
$this->DashboardModel->addSelect('dashboard_kurzbz');
$this->DashboardModel->addSelect('tbl_dashboard.beschreibung');
$this->DashboardModel->addSelect("(
SELECT json_agg(w.*)
FROM dashboard.tbl_widget w
JOIN dashboard.tbl_dashboard_widget dw
USING(widget_id)
WHERE dw.dashboard_id=tbl_dashboard.dashboard_id
) AS \"widgetSetup\"");
$result = $this->DashboardModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
$data = array_map(function ($dashboard) {
$tmpSetups = json_decode($dashboard->widgetSetup);
$tmpSetups = array_map(function ($widget) {
$widget->setup->file = absoluteJsImportUrl($widget->setup->file);
return $widget;
}, $tmpSetups);
$dashboard->widgetSetup = $tmpSetups;
return $dashboard;
}, $data);
$this->terminateWithSuccess($data);
}
public function create()
@@ -82,7 +103,7 @@ class Board extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
$this->terminateWithSuccess($data);
}
public function delete()
@@ -116,6 +137,6 @@ class Board extends FHCAPI_Controller
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
$this->terminateWithSuccess($data);
}
}
@@ -120,10 +120,7 @@ class Preset extends FHCAPI_Controller
$conf = $this->dashboardlib->getPreset($db, $funktion);
if ($conf) {
$preset = json_decode($conf->preset, true);
if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets']))
$result[$funktion] = [];
else
$result[$funktion] = $preset[$funktion]['widgets'];
$result[$funktion] = $preset;
} else {
$result[$funktion] = [];
}
@@ -154,7 +151,7 @@ class Preset extends FHCAPI_Controller
$preset_decoded = json_decode($preset->preset, true);
$this->dashboardlib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, [$widget]);
$preset_decoded[$widget['widgetid']] = $widget;
$preset->preset = json_encode($preset_decoded);
@@ -186,8 +183,10 @@ class Preset extends FHCAPI_Controller
$preset_decoded = json_decode($preset->preset, true);
if (!$this->dashboardlib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid))
if (!isset($preset_decoded[$widgetid]))
show_404();
unset($preset_decoded[$widgetid]);
$preset->preset = json_encode($preset_decoded);
@@ -48,25 +48,9 @@ class User extends FHCAPI_Controller
$uid = $this->authlib->getAuthObj()->username;
/*$mergedconfig = $this->dashboardlib->getMergedConfig($dashboard->dashboard_id, $uid);
$mergedconfig = $this->dashboardlib->getMergedUserConfig($dashboard->dashboard_id, $uid);
$this->terminateWithSuccess([
'general' => call_user_func_array(
'array_merge_recursive',
$mergedconfig
)
]);*/
$defaultconfig = $this->dashboardlib->getDefaultConfig($dashboard->dashboard_id);
$userconfig = $this->dashboardlib->getUserConfig($dashboard->dashboard_id, $uid);
$defaultconfig_squashed = $defaultconfig ? call_user_func_array('array_replace_recursive', $defaultconfig) : [];
$userconfig_squashed = $userconfig ? call_user_func_array('array_replace_recursive', $userconfig) : [];
$mergedconfig = array_replace_recursive($defaultconfig_squashed, $userconfig_squashed);
$this->terminateWithSuccess([
DashboardLib::SECTION_IF_FUNKTION_KURZBZ_IS_NULL => $mergedconfig
]);
$this->terminateWithSuccess($mergedconfig);
}
public function addWidget()
@@ -86,26 +70,15 @@ class User extends FHCAPI_Controller
if (!isset($widget['widgetid']))
$widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz);
if (isset($widget['source']))
unset($widget['source']);
$override = $this->dashboardlib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid);
$override_decoded = json_decode($override->override, true);
if (!isset($override_decoded['general']) || !is_array($override_decoded['general']))
$override_decoded['general'] = [];
$override_decoded[$widget['widgetid']] = $widget;
if (!isset($override_decoded['general']['widgets']))
$override_decoded['general']['widgets'] = [];
$override_decoded['general']['widgets'][$widget['widgetid']] = $widget;
// NOTE(chris): remove doubles in other funktionen
foreach ($override_decoded as $funktion => $array) {
if ($funktion == 'general')
continue;
if (isset($array['widgets']) && isset($array['widgets'][$widget['widgetid']]))
unset($override_decoded[$funktion]['widgets'][$widget['widgetid']]);
}
$override->override = json_encode($override_decoded);
$result = $this->dashboardlib->insertOrUpdateOverride($override);
@@ -135,18 +108,10 @@ class User extends FHCAPI_Controller
$override_decoded = json_decode($override->override, true);
foreach (array_keys($override_decoded) as $k) {
if (!isset($override_decoded[$k]["widgets"])) {
unset($override_decoded[$k]);
continue;
}
if (isset($override_decoded[$k]["widgets"][$widget_id])) {
unset($override_decoded[$k]["widgets"][$widget_id]);
}
if (!$override_decoded[$k]["widgets"]) {
unset($override_decoded[$k]);
}
}
if (!isset($override_decoded[$widget_id]))
show_404();
unset($override_decoded[$widget_id]);
$override->override = json_encode($override_decoded);
@@ -42,14 +42,22 @@ class Messages extends FHCAPI_Controller
]);
}
public function getMessages($id, $type_id, $size, $page)
public function getMessages($id, $type_id, $size=null, $page=null)
{
if($type_id != 'person_id'){
$id = $this->_getPersonId($id, $type_id);
}
$offset = $size * ($page - 1);
$limit = $size;
if(!(is_null($size) && is_null($page)))
{
$offset = $size * ($page - 1);
$limit = $size;
}
else
{
$offset = null;
$limit = null;
}
$result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
@@ -37,7 +37,9 @@ class DashboardLib
public function getDashboardByKurzbz($dashboard_kurzbz)
{
$result = $this->_ci->DashboardModel->getDashboardByKurzbz($dashboard_kurzbz);
$result = $this->_ci->DashboardModel->loadWhere([
'dashboard_kurzbz' => $dashboard_kurzbz
]);
if (hasData($result))
{
@@ -47,17 +49,21 @@ class DashboardLib
return null;
}
public function getMergedConfig($dashboard_id, $uid)
public function getMergedUserConfig($dashboard_id, $uid)
{
$defaultconfig = $this->getDefaultConfig($dashboard_id);
$userconfig = $this->getUserConfig($dashboard_id, $uid);
$defaultconfig = $this->getUserBaseConfig($dashboard_id);
$userconfig = $this->getUserOverrideConfig($dashboard_id, $uid);
$mergedconfig = array_replace_recursive($defaultconfig, $userconfig);
$sourceconfig = array_map(function ($value) {
return ['source' => $value['source']];
}, $defaultconfig);
$mergedconfig = array_replace_recursive($defaultconfig, $userconfig, $sourceconfig);
return $mergedconfig;
}
public function getDefaultConfig($dashboard_id)
protected function getUserBaseConfig($dashboard_id)
{
$funktion_kurzbzs = [];
$rights = $this->_ci->permissionlib->getAccessRights();
@@ -87,7 +93,11 @@ class DashboardLib
$preset = json_decode($presetobj->preset, true);
if (null !== $preset)
{
$defaultconfig = array_replace_recursive($defaultconfig, $preset);
$preset = array_map(function ($value) use ($presetobj) {
$value['source'] = $presetobj->funktion_kurzbz ?: self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
return $value;
}, $preset);
$defaultconfig = array_merge_recursive($defaultconfig, $preset);
}
}
}
@@ -95,7 +105,7 @@ class DashboardLib
return $defaultconfig;
}
public function getUserConfig($dashboard_id, $uid)
protected function getUserOverrideConfig($dashboard_id, $uid)
{
$res_userconfig = $this->_ci->DashboardOverrideModel->getOverride($dashboard_id, $uid);
@@ -124,7 +134,7 @@ class DashboardLib
$emptyoverride = new stdClass();
$emptyoverride->dashboard_id = $dashboard->dashboard_id;
$emptyoverride->uid = $uid;
$emptyoverride->override = '{"' . self::USEROVERRIDE_SECTION . '": {"widgets":{}}, "custom": { "widgets" : {}}}';
$emptyoverride->override = '[]';
return $emptyoverride;
}
@@ -143,8 +153,7 @@ class DashboardLib
$emptypreset = new stdClass();
$emptypreset->dashboard_id = $dashboard->dashboard_id;
$emptypreset->funktion_kurzbz = $funktion_kurzbz;
$section = ($funktion_kurzbz !== null) ? $funktion_kurzbz : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
$emptypreset->preset = '{"' . $section . '": { "widgets" : {}},"custom": { "widgets" : {}}}';
$emptypreset->preset = '[]';
return $emptypreset;
}
@@ -209,44 +218,4 @@ class DashboardLib
return $result;
}
public function addWidgetsToWidgets(&$widgets, $dashboard_kurzbz, $section, $addwigets)
{
foreach ($addwigets as $widget)
{
if(!isset($widget['widgetid']))
{
$widget['widgetid'] = $this->generateWidgetId($dashboard_kurzbz);
}
$this->addWidgetToWidgets($widgets, $section, $widget, $widget['widgetid']);
}
}
public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid)
{
$section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
if (!isset($widgets[$section]) || !isset($widgets[$section]["widgets"]) || !is_array($widgets[$section]))
{
$widgets[$section] = array();
$widgets[$section]["widgets"] = array();
}
$widgets[$section]["widgets"][$widgetid] = $widget;
}
public function removeWidgetFromWidgets(&$widgets, $section, $widgetid)
{
$section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL;
if (isset($widgets[$section]) && isset($widgets[$section]["widgets"][$widgetid]))
{
unset($widgets[$section]["widgets"][$widgetid]);
if(empty($widgets[$section]["widgets"]) && $section !== self::USEROVERRIDE_SECTION) {
unset($widgets[$section]);
}
return true;
}
else {
return false;
}
}
}
@@ -3,6 +3,7 @@ namespace vertragsbestandteil;
use Exception;
use vertragsbestandteil\VertragsbestandteilStunden;
use vertragsbestandteil\VertragsbestandteilLohnguide;
/**
* Description of VertragsbestandteilFactory
@@ -22,6 +23,7 @@ class VertragsbestandteilFactory
const VERTRAGSBESTANDTEIL_URLAUBSANSPRUCH = 'urlaubsanspruch';
const VERTRAGSBESTANDTEIL_ZEITAUFZEICHNUNG = 'zeitaufzeichnung';
const VERTRAGSBESTANDTEIL_LEHRE = 'lehre';
const VERTRAGSBESTANDTEIL_LOHNGUIDE = 'lohnguide';
public static function getVertragsbestandteil($data, $fromdb=false)
{
@@ -69,6 +71,11 @@ class VertragsbestandteilFactory
$vertragsbestandteil = new VertragsbestandteilZeitaufzeichnung();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$vertragsbestandteil = new VertragsbestandteilLohnguide();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
default:
throw new Exception('Unknown vertragsbestandteiltyp_kurzbz '
@@ -127,6 +134,12 @@ class VertragsbestandteilFactory
$vertragsbestandteildbmodel = $CI->VertragsbestandteilZeitaufzeichnung_model;
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$CI->load->model('vertragsbestandteil/VertragsbestandteilLohnguide_model',
'VertragsbestandteilLohnguide_model');
$vertragsbestandteildbmodel = $CI->VertragsbestandteilLohnguide_model;
break;
default:
throw new Exception('Unknown vertragsbestandteil_kurzbz '
. $vertragsbestandteil_kurzbz);
@@ -10,6 +10,7 @@ require_once __DIR__ . '/VertragsbestandteilKuendigungsfrist.php';
require_once __DIR__ . '/VertragsbestandteilUrlaubsanspruch.php';
require_once __DIR__ . '/VertragsbestandteilFreitext.php';
require_once __DIR__ . '/VertragsbestandteilKarenz.php';
require_once __DIR__ . '/VertragsbestandteilLohnguide.php';
require_once __DIR__ . '/VertragsbestandteilFactory.php';
require_once __DIR__ . '/OverlapChecker.php';
@@ -0,0 +1,155 @@
<?php
namespace vertragsbestandteil;
use vertragsbestandteil\Vertragsbestandteil;
use vertragsbestandteil\VertragsbestandteilFactory;
class VertragsbestandteilLohnguide extends Vertragsbestandteil
{
protected $stellenbezeichnung;
protected $vordienstzeit;
protected $fachrichtung_kurzbz;
protected $modellstelle_kurzbz;
protected $kommentar_person;
protected $kommentar_modellstelle;
public function __construct()
{
parent::__construct();
$this->setVertragsbestandteiltyp_kurzbz(
VertragsbestandteilFactory::VERTRAGSBESTANDTEIL_LOHNGUIDE);
}
public function getStellenbezeichnung()
{
return $this->stellenbezeichnung;
}
public function setStellenbezeichnung($stellenbezeichnung): self
{
$this->markDirty('stellenbezeichnung', $this->stellenbezeichnung, $stellenbezeichnung);
$this->stellenbezeichnung = $stellenbezeichnung;
return $this;
}
public function getVordienstzeit()
{
return $this->vordienstzeit;
}
public function setVordienstzeit($vordienstzeit): self
{
$this->markDirty('vordienstzeit', $this->vordienstzeit, $vordienstzeit);
$this->vordienstzeit = $vordienstzeit;
return $this;
}
public function getFachrichtung_kurzbz()
{
return $this->fachrichtung_kurzbz;
}
public function setFachrichtung_kurzbz($fachrichtung_kurzbz): self
{
$this->markDirty('fachrichtung_kurzbz', $this->fachrichtung_kurzbz, $fachrichtung_kurzbz);
$this->fachrichtung_kurzbz = $fachrichtung_kurzbz;
return $this;
}
public function getModellstelle_kurzbz()
{
return $this->modellstelle_kurzbz;
}
public function setModellstelle_kurzbz($modellstelle_kurzbz): self
{
$this->markDirty('modellstelle_kurzbz', $this->modellstelle_kurzbz, $modellstelle_kurzbz);
$this->modellstelle_kurzbz = $modellstelle_kurzbz;
return $this;
}
public function getKommentar_person()
{
return $this->kommentar_person;
}
public function setKommentar_person($kommentar_person): self
{
$this->markDirty('kommentar_person', $this->kommentar_person, $kommentar_person);
$this->kommentar_person = $kommentar_person;
return $this;
}
public function getKommentar_modellstelle()
{
return $this->kommentar_modellstelle;
}
public function setKommentar_modellstelle($kommentar_modellstelle): self
{
$this->markDirty('kommentar_modellstelle', $this->kommentar_modellstelle, $kommentar_modellstelle);
$this->kommentar_modellstelle = $kommentar_modellstelle;
return $this;
}
public function hydrateByStdClass($data, $fromdb=false)
{
parent::hydrateByStdClass($data, $fromdb);
$this->fromdb = $fromdb;
isset($data->fachrichtung_kurzbz) && $this->setFachrichtung_kurzbz($data->fachrichtung_kurzbz);
isset($data->stellenbezeichnung) && $this->setStellenbezeichnung($data->stellenbezeichnung);
isset($data->vordienstzeit) && $this->setVordienstzeit($data->vordienstzeit);
isset($data->modellstelle_kurzbz) && $this->setModellstelle_kurzbz($data->modellstelle_kurzbz);
isset($data->kommentar_person) && $this->setKommentar_person($data->kommentar_person);
isset($data->kommentar_modellstelle) && $this->setKommentar_modellstelle($data->kommentar_modellstelle);
$this->fromdb = false;
}
public function toStdClass(): \stdClass
{
$tmp = array(
'vertragsbestandteil_id' => $this->getVertragsbestandteil_id(),
'stellenbezeichnung' => $this->getStellenbezeichnung(),
'vordienstzeit' => $this->getVordienstzeit(),
'fachrichtung_kurzbz' => $this->getFachrichtung_kurzbz(),
'modellstelle_kurzbz' => $this->getModellstelle_kurzbz(),
'kommentar_person' => $this->getKommentar_person(),
'kommentar_modellstelle' => $this->getKommentar_modellstelle(),
);
$tmp = array_filter($tmp, function($k) {
return in_array($k, $this->modifiedcolumns);
}, ARRAY_FILTER_USE_KEY);
return (object) $tmp;
}
public function __toString()
{
$txt = <<<EOTXT
modellstelle_kurzbz: {$this->getModellstelle_kurzbz()}
EOTXT;
return parent::__toString() . $txt;
}
/* public function validate()
{
if( !(filter_var($this->tage, FILTER_VALIDATE_INT,
array(
'options' => array(
'min_range' => 1,
'max_range' => 50
)
)
)) ) {
$this->validationerrors[] = 'Urlaubsanspruch muss eine Tagesanzahl im Bereich 1 bis 50 sein.';
}
return parent::validate();
} */
}
@@ -11,15 +11,4 @@ class Dashboard_model extends DB_Model
$this->dbTable = 'dashboard.tbl_dashboard';
$this->pk = 'dashboard_id';
}
/**
* Get Dashboard by kurzbz.
* @param string dashboard_kurzbz
* @return array
*/
public function getDashboardByKurzbz($dashboard_kurzbz)
{
return $this->loadWhere(array('dashboard_kurzbz' => $dashboard_kurzbz));
}
}
@@ -402,14 +402,17 @@ class Lehrveranstaltung_model extends DB_Model
SELECT
vorname, nachname, mitarbeiter_uid, lehrfunktion_kurzbz
FROM
lehre.tbl_lehreinheit
lehre.tbl_lehreinheit le
JOIN lehre.tbl_lehreinheitmitarbeiter lema USING (lehreinheit_id)
JOIN public.tbl_benutzer b ON b.uid = lema.mitarbeiter_uid
JOIN public.tbl_person p using (person_id)
WHERE
tbl_lehreinheit.lehrveranstaltung_id= ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
le.lehrveranstaltung_id= ?
AND le.studiensemester_kurzbz = ?
AND lehrfunktion_kurzbz = 'LV-Leitung'
AND lema.mitarbeiter_uid NOT like '_Dummy%'
AND b.aktiv = TRUE
AND p.aktiv = TRUE
ORDER BY
lema.insertamum DESC
LIMIT 1
@@ -594,7 +594,10 @@ class Studiengang_model extends DB_Model
$this->addSelect('p.prestudent_id');
$this->addSelect('pers.vorname');
$this->addSelect('pers.nachname');
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', " . $this->dbTable . ".bezeichnung, ')') AS name");
$this->addSelect("CONCAT(UPPER(pers.nachname), ' ', pers.vorname, ' (', "
. $this->dbTable . ".bezeichnung, ', ', "
. "UPPER(" . $this->dbTable . ".typ), "
. "UPPER(" . $this->dbTable . ".kurzbz),')') AS name");
$this->addJoin('public.tbl_prestudent p', 'studiengang_kz');
$this->addJoin(
@@ -261,6 +261,42 @@ class Benutzerfunktion_model extends DB_Model
}
/**
* Get active Kompetenzfeldleitung bei UID.
*
* @param $uid
* @return array|stdClass|null
*/
public function getKFLByUID($uid)
{
$query = '
SELECT
bf.uid,
bf.oe_kurzbz,
oe.organisationseinheittyp_kurzbz
FROM
public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN public.tbl_benutzer b USING (uid)
WHERE
b.uid = ?
AND b.aktiv = TRUE
AND funktion_kurzbz = \'Leitung\'
AND organisationseinheittyp_kurzbz = \'Kompetenzfeld\'
AND (datum_von IS NULL OR datum_von <= now())
AND (datum_bis IS NULL OR datum_bis >= now())
';
$parameters_array = array();
if (is_string($uid))
{
$parameters_array[] = $uid;
}
return $this->execQuery($query, $parameters_array);
}
public function insertBenutzerfunktion($Json)
{
unset($Json['benutzerfunktion_id']);
+6 -4
View File
@@ -242,6 +242,7 @@ class Message_model extends DB_Model
*/
public function getMessagesForTable($person_id, $offset, $limit)
{
$limitoffset = (!is_null($offset) && !is_null($limit)) ? 'limit ? offset ?' : '';
$sql = <<<EOSQL
with filtered_messages as (
select
@@ -310,11 +311,12 @@ class Message_model extends DB_Model
public.tbl_person pr on pr.person_id = fm.recipient_id
order by
m.insertamum DESC
limit ?
offset ?;
{$limitoffset}
EOSQL;
$parametersArray = array($person_id, $person_id, $limit, $offset);
$parametersArray = $limitoffset
? array($person_id, $person_id, $limit, $offset)
: array($person_id, $person_id);
$count = 0;
$data = $this->execQuery($sql, $parametersArray);
@@ -325,7 +327,7 @@ EOSQL;
$data = getData($data);
if($data)
{
$count = ceil($data[0]->total_msgs / $limit);
$count = is_null($limit) ? 1 : ceil($data[0]->total_msgs / $limit);
}
return success(['data' => $data, 'count' => $count]);
@@ -0,0 +1,11 @@
<?php
class VertragsbestandteilLohnguide_model extends DB_Model
{
public function __construct()
{
parent::__construct();
$this->dbTable = 'hr.tbl_vertragsbestandteil_lohnguide';
$this->pk = 'vertragsbestandteil_id';
}
}
@@ -37,7 +37,8 @@ class Vertragsbestandteil_model extends DB_Model
kf.arbeitgeber_frist, kf.arbeitnehmer_frist,
s.wochenstunden, s.teilzeittyp_kurzbz,
u.tage,
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice,
lg.stellenbezeichnung, lg.vordienstzeit, lg.fachrichtung_kurzbz, lg.modellstelle_kurzbz, lg.kommentar_person, lg.kommentar_modellstelle
FROM
hr.tbl_vertragsbestandteil v
LEFT JOIN
@@ -63,6 +64,8 @@ class Vertragsbestandteil_model extends DB_Model
hr.tbl_vertragsbestandteil_urlaubsanspruch u USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_zeitaufzeichnung z USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_lohnguide lg USING(vertragsbestandteil_id)
EOSQL;
return $sql;
}
@@ -39,7 +39,7 @@ $includesArray = array(
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/Dashboard/Fhc.js',
'public/js/apps/Cis.js',
),
);
@@ -6,7 +6,7 @@ $includesArray = array(
'fontawesome6' => true,
'axios027' => true,
'customJSModules' => array_merge([
'public/js/apps/Cis.js'
'public/js/apps/Cis/Menu.js'
], $customJSModules ?? []),
'customCSSs' => array_merge([
'public/css/Cis4/Cis.css'
@@ -8,7 +8,7 @@ $includesArray = array(
'axios027' => true,
'primevue3' => true,
'customJSModules' => array_merge([
'public/js/apps/Cis.js'
'public/js/apps/Cis/Menu.js'
], $customJSModules ?? []),
'customCSSs' => array_merge([
'public/css/Cis4/Cis.css',
+14
View File
@@ -70,6 +70,18 @@
}
}
},
{
"type": "package",
"package": {
"name": "drag-drop-touch-js/dragdroptouch",
"version": "2.0.3",
"source": {
"url": "https://github.com/drag-drop-touch-js/dragdroptouch.git",
"type": "git",
"reference": "master"
}
}
},
{
"type": "package",
"package": {
@@ -452,6 +464,8 @@
"easyrdf/easyrdf": "0.9.*",
"drag-drop-touch-js/dragdroptouch": "*",
"fgelinas/timepicker": "0.3.3",
"fortawesome/font-awesome4": "4.7.*",
"fortawesome/font-awesome6": "6.1.*",
Generated
+11 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f4f0af4586f46f97d8b6092c1ac0fb3a",
"content-hash": "869cbc35bd1ba90ab90934fcb41b0f51",
"packages": [
{
"name": "afarkas/html5shiv",
@@ -804,6 +804,16 @@
"abandoned": true,
"time": "2018-03-09T06:07:41+00:00"
},
{
"name": "drag-drop-touch-js/dragdroptouch",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/drag-drop-touch-js/dragdroptouch.git",
"reference": "master"
},
"type": "library"
},
{
"name": "easyrdf/easyrdf",
"version": "0.9.1",
+60 -52
View File
@@ -2,7 +2,7 @@
@import './dashboard/news.css';
@import './dashboard/LvPlan.css';
:root{
:root {
--fhc-dashboard-danger: var(--fhc-danger, #842029);
--fhc-dashboard-grid-size: 4;
--fhc-dashboard-link: var(--fhc-link, #0a57ca);
@@ -17,22 +17,16 @@
--fhc-dashboard-section-info-color-hover: var(--fhc-primary-highlight, #005585);
}
@media(max-width: 577px) {
:root {
--fhc-dashboard-grid-size: 1;
}
}
.core-dashboard a{
.core-dashboard a {
color: var(--fhc-dashboard-link);
}
@media (max-width: 576px){
@media (max-width: 576px) {
.widget-icon {
max-height: 250px;
object-fit: cover;
}
.widget-icon-container{
.widget-icon-container {
max-width: 250px;
margin-left: auto;
margin-right: auto;
@@ -46,27 +40,36 @@
background-repeat: no-repeat;
background-position: center;
background-size: cover;
cursor:pointer;
cursor: pointer;
}
.dashboard-section > .newGridRow{
position:absolute;
width:20px;
height:20px;
padding:0;
bottom:0;
left:50%;
transform:translate(-50%, 50%);
.dashboard-section.edit-active {
/**
* replaces margin for extra row
* 10% equals 0.1 of 100%
* 1rem equals the padding of pb-3 that is overwritten here
*/
padding-bottom: calc(10% / var(--fhc-dashboard-grid-size) + 1rem) !important;
}
.dashboard-section > .newGridRow {
position: absolute;
width: 20px;
height: 20px;
padding: 0;
bottom: 0;
left: 50%;
transform: translate(-50%, 50%);
background-color: var(--fhc-dashboard-gridrow-background);
}
.newGridRow:hover {
color:white;
color: white;
background-color: var(--fhc-dashboard-gridrow-background-highlight);
}
.empty-tile-hover:hover {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-500 -500 1448 1512"><path fill="rgb(211, 211, 211)" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 344V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h64V168c0-13.3 10.7-24 24-24s24 10.7 24 24v64h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H248v64c0 13.3-10.7 24-24 24s-24-10.7-24-24z"/></svg>');
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="-500 -500 1448 1512"><path fill="rgb(211, 211, 211)" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM200 344V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h64V168c0-13.3 10.7-24 24-24s24 10.7 24 24v64h64c13.3 0 24 10.7 24 24s-10.7 24-24 24H248v64c0 13.3-10.7 24-24 24s-24-10.7-24-24z"/></svg>');
}
.alert-danger .form-check-input:checked {
@@ -74,16 +77,6 @@
background-color: var(--fhc-dashboard-danger);
}
:root {
--fhc-dashboard-grid-size: 4;
}
@media(max-width: 1400px) {
:root {
--fhc-dashboard-grid-size: 4;
}
}
@media(max-width: 1200px) {
:root {
--fhc-dashboard-grid-size: 3;
@@ -105,6 +98,7 @@
@media(max-width: 577px) {
:root {
--fhc-dashboard-grid-size: 1;
--fhc-dg-item-py: .75rem;
}
}
@@ -132,50 +126,64 @@
cursor: move !important;
}
.draggedItem {
.drop-grid-item-resize > .dashboard-item,
.drop-grid-item-move > .dashboard-item {
height: 100%;
width: 100%;
background-color: var(--fhc-dashboard-draggeditem-background);
position: relative;
}
.dashboard-item-overlay{
.drop-grid-item-resize > .dashboard-item > *,
.drop-grid-item-move > .dashboard-item > * {
display: none;
}
.drop-grid-item-sizechanged > .dashboard-item,
.drop-grid-item-move > .dashboard-item {
background-color: var(--fhc-dashboard-item-overlay-background);
}
.dashboard-item-overlay::before{
position:absolute;
content:"";
top:0.25rem;
left:0.25rem;
right:0.25rem;
bottom:0.25rem;
border:4px dashed var(--fhc-dashboard-item-overly-border-color);
opacity: 0.5;
.drop-grid-item-sizechanged > .dashboard-item::before,
.drop-grid-item-move > .dashboard-item::before {
position: absolute;
content: "";
top: .25rem;
left: .25rem;
right: .25rem;
bottom: .25rem;
border: 4px dashed var(--fhc-dashboard-item-overly-border-color);
opacity: .5;
}
#deleteBookmark i{
.drop-grid-item-oversized > .dashboard-item {
/* Bootstrap: border-danger */
--bs-border-opacity: 1;
border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;
}
#deleteBookmark i {
color: var(--fhc-dashboard-danger);
}
.pin:hover{
.pin:hover {
cursor: pointer;
}
.pin[pinned]:hover{
.pin[pinned]:hover {
color: var(--fhc-dashboard-pin-pinned-hover-color);
}
.section-info{
.section-info {
color: var(--fhc-dashboard-section-info-color);
cursor:pointer;
cursor: pointer;
}
.section-info:hover {
color: var(--fhc-dashboard-section-info-color-hover);
}
.denied-dragging-animation {
.drop-grid-item-blocker [pinned='true'] {
animation: wiggle 0.5s linear;
color: var(--fhc-dashboard-denied-dragging-animation-color) !important;
}
@@ -204,13 +212,13 @@
}
.hiddenWidget{
.hidden-widget {
background: var(--fhc-disabled-background);
opacity: 40%;
}
.hiddenWidget .card,
.hiddenWidget .card-body,
.hiddenWidget .card-body *{
.hidden-widget .card,
.hidden-widget .card-body,
.hidden-widget .card-body * {
background: inherit !important;
}
+8 -5
View File
@@ -17,13 +17,16 @@
export default {
getMessages(params) {
let url = 'api/frontend/v1/messages/messages/getMessages'
+ '/' + params.id
+ '/' + params.type;
if(params.size && params.page) {
url += '/' + params.size
+ '/' + params.page;
}
return {
method: 'get',
url: 'api/frontend/v1/messages/messages/getMessages/'
+ params.id + '/'
+ params.type + '/'
+ params.size + '/'
+ params.page
url: url
};
},
getVorlagen(){
+297 -140
View File
@@ -1,156 +1,313 @@
import FhcSearchbar from "../components/searchbar/searchbar.js";
import CisMenu from "../components/Cis/Menu.js";
import FhcDashboard from '../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../plugins/Phrasen.js';
import ApiSearchbar from '../api/factory/searchbar.js';
import Theme from "../plugins/Theme.js";
import Theme from '../plugins/Theme.js';
import contrast from '../directives/contrast.js';
import {setScrollbarWidth} from "../helpers/CssVarCalcHelpers.js";
import LvPlan from "../components/Cis/LvPlan/Lehrveranstaltung.js";
import MyLvPlan from "../components/Cis/LvPlan/Personal.js";
import MylvStudent from "../components/Cis/Mylv/Student.js";
import Profil from "../components/Cis/Profil/Profil.js";
import Raumsuche from "../components/Cis/Raumsuche/Raumsuche.js";
import CmsNews from "../components/Cis/Cms/News.js";
import CmsContent from "../components/Cis/Cms/Content.js";
import Info from "../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
import RoomInformation, {DEFAULT_MODE_RAUMINFO} from "../components/Cis/Mylv/RoomInformation.js";
import AbgabetoolStudent from "../components/Cis/Abgabetool/AbgabetoolStudent.js";
import AbgabetoolMitarbeiter from "../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
import AbgabetoolAssistenz from "../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
import DeadlineOverview from "../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../components/Cis/Studium/Studium.js";
import ApiRouteInfo from '../api/factory/routeinfo.js';
import {capitalize} from "../helpers/StringHelpers.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(`/${ciPath}`),
routes: [
{
path: `/Cis/Studium`,
name: 'Studium',
component: Studium,
props: true
},
{
path: `/Cis/Profil/View/:uid`,
name: 'ProfilView',
component: Profil,
props: true
},
{
path: `/Cis/Profil`,
name: 'Profil',
component: Profil,
props: true
},
{
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
name: 'AbgabetoolStudent',
component: AbgabetoolStudent,
props: true
},
{
path: `/Cis/Abgabetool/Mitarbeiter`,
name: 'AbgabetoolMitarbeiter',
component: AbgabetoolMitarbeiter,
props: true
},
{
path: `/Cis/Abgabetool/Assistenz/:stg_kz_prop?`,
name: 'AbgabetoolAssistenz',
component: AbgabetoolAssistenz,
props: true
},
{
path: `/Cis/Abgabetool/Deadlines/:person_uid_prop?`,
name: 'DeadlineOverview',
component: DeadlineOverview,
props: true
},
{
path: `/Cis/Raumsuche`,
name: 'Raumsuche',
component: Raumsuche,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
name: "RoomInformationOld",
component: RoomInformation,
redirect: (to) => {
return { // redirect to longer Rauminfo url and map params
name: "RoomInformation",
params: { // in this case always populate other params since they are not optional
ort_kurzbz: to.params.ort_kurzbz,
mode: DEFAULT_MODE_RAUMINFO,
focus_date: new Date().toISOString().split("T")[0]
},
};
},
},
{
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
name: 'RoomInformation',
component: RoomInformation,
props: (route) => { // validate and set mode/focus date if for some reason missing
const validModes = ["Month", "Week", "Day"];
// check mode string
const mode = route.params.mode &&
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
: DEFAULT_MODE_RAUMINFO;
// default to today date if not provided
const d = new Date(route.params.focus_date)
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
// existing viewData declaration written from codeigniter 3 into routerview tag
return {
propsViewData: {
mode,
focus_date,
ort_kurzbz: route.params.ort_kurzbz
}
};
},
beforeEnter: (to, from, next) => {
// missing mode or focus_date -> set defaults
if (!to.params.mode || !to.params.focus_date) {
next({
name: "RoomInformation",
params: {
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
ort_kurzbz: route.params.ort_kurzbz
}
});
} else {
next();
}
}
},
{
path: `/CisVue/Cms/Content/:content_id`,
name: 'Content',
component: CmsContent,
props: true
},
{
path: `/CisVue/Cms/News`,
name: 'News',
component: CmsNews,
props: true
},
{
path: `/Cis/MyLv/:studiensemester?`,
name: 'MyLv',
component: MylvStudent,
props: true,
},
{
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
name: 'LvInfo',
component: Info,
props: true
},
// Redirect old links to new format
{
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
path: "/Cis/LvPlan/:lv_id(\\d+)",
name: "LvPlanOld",
component: LvPlan,
redirect(to) {
const route = Vue.unref(router.currentRoute);
const { mode, focus_date } = route.params; // keep mode and focus_date if available
return { // redirect to longer LvPlan url and map params
name: "LvPlan",
params: {
mode,
focus_date,
lv_id: to.params.lv_id
},
};
},
},
{
path: `/Cis/LvPlan/:mode?/:focus_date?/:lv_id?`,
name: 'LvPlan',
component: LvPlan,
props(route) {
return {
propsViewData: route.params
};
}
},
{
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
name: 'MyLvPlan',
component: MyLvPlan,
props(route) {
return {
propsViewData: route.params
};
}
},
{
path: `/Cis4`,
name: 'Cis4',
component: FhcDashboard,
props: {dashboard: 'CIS'},
},
{
path: `/`,
name: 'FhcDashboard',
component: FhcDashboard,
props: {dashboard: 'CIS'},
},
{
path: '/:pathMatch(.*)*',
name: 'Fallback',
component: FhcDashboard,
props: {dashboard: 'CIS'},
redirect: () => {
return {
name: "Cis4",
params: {
dashboard: 'CIS'
},
};
},
},
]
})
const app = Vue.createApp({
name: 'CisApp',
components: {
FhcSearchbar,
CisMenu
},
data: function() {
return {
searchbaroptions: {
origin: "cis",
cssclass: "",
calcheightonly: true,
types: {
employee: Vue.computed(() => this.$p.t("search/type_employee")),
student: Vue.computed(() => this.$p.t("search/type_student")),
room: Vue.computed(() => this.$p.t("search/type_room")),
organisationunit: Vue.computed(() => this.$p.t("search/type_organisationunit")),
cms: Vue.computed(() => this.$p.t("search/type_cms")),
dms: Vue.computed(() => this.$p.t("search/type_dms"))
},
actions: {
employee: {
defaultaction: {
type: "link",
action: function(data) {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router+
"/Cis/Profil/View/"+data.uid;
}
},
childactions: []
},
student: {
defaultaction: {
type: "link",
action: function (data) {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router +
"/Cis/Profil/View/" + data.uid;
name: 'CisApp',
data: () => ({
appSideMenuEntries: {}
}),
components: {},
computed: {
isMobile() {
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
return smallScreen;// && touchCapable;
}
},
provide() {
return { // provide injectable & watchable language property
language: Vue.computed(() => this.$p.user_language),
isMobile: this.isMobile
}
},
methods: {
isInternalRoute(href) {
const internalBase = window.location.origin
return href.startsWith(internalBase);
},
handleClick(event) {
const target = event.target.closest('a');
}
},
childactions: []
},
room: {
defaultaction: {
type: "link",
renderif: function(data) {
if(data.content_id === null){
return false;
}
return true;
},
action: function(data) {
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/content/' + data.content_id;
return link;
}
},
childactions: [
{
label: "LV-Plan",
icon: "fas fa-bookmark",
type: "link",
action: function(data) {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/getRoomInformation/' + data.ort_kurzbz;
return link;
}
},
{
label: "Rauminformation",
icon: "fas fa-info-circle",
type: "link",
renderif: function(data) {
if(data.content_id === null){
return false;
}
return true;
},
action: function(data) {
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/content/' + data.content_id;
return link;
}
},
]
},
organisationunit: {
defaultaction: {
type: "link",
renderif: function(data) {
if(data.mailgroup) {
return true;
}
return false;
},
action: function(data) {
const link = 'mailto:' + data.mailgroup;
return link;
}
},
childactions: []
},
cms: {
defaultaction: {
type: "link",
action: function (data) {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/content/' + data.content_id;
return link;
}
},
childactions: []
},
dms: {
defaultaction: {
type: "link",
action: function (data) {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
'cms/dms.php?id=' + data.dms_id;
return link;
}
},
childactions: []
}
}
}
};
},
methods: {
searchfunction: function(searchsettings) {
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
}
}
if(target?.id == 'skiplink') return
if (target && this.isInternalRoute(target.href)) {
const url = new URL(target.href)
const path = url.pathname
const base = this.$router.options.history.base
const route = path.replace(base, '') || '/'
// let click event propagate normally if we dont route internally
const res = this.$router.resolve(route)
if(!res?.matched?.length || res.name === 'Fallback') return
event.preventDefault(); // Prevent browser navigation
if(this.isMobile) { // toggle the menu
const navMain = document.getElementById('nav-main');
// fix unwanted toggle from off to on for some links on mobile
if(navMain.classList.contains('show')){
document.getElementById('nav-main-btn').click();
}
}
this.$router.push(route);
}
}
},
mounted() {
document.addEventListener('click', this.handleClick);
},
beforeUnmount() {
document.removeEventListener('click', this.handleClick);
},
});
// kind of a bandaid for bad css on some pages to avoid horizontal scroll
setScrollbarWidth();
app.config.globalProperties.$capitalize = capitalize;
FhcApps.router.makeExtendable(router);
FhcApps.makeExtendable(app);
app.use(router);
app.use(primevue.config.default, {
zIndex: {
overlay: 9000,
tooltip: 8000
}
})
app.directive('tooltip', primevue.tooltip);
app.use(PluginsPhrasen);
app.use(Theme);
app.mount('#cis-header');
app.directive('contrast', contrast);
app.mount('#fhccontent');
router.afterEach((to, from, failure) => {
app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath));
});
+156
View File
@@ -0,0 +1,156 @@
import FhcSearchbar from "../../components/searchbar/searchbar.js";
import CisMenu from "../../components/Cis/Menu.js";
import PluginsPhrasen from '../../plugins/Phrasen.js';
import ApiSearchbar from '../../api/factory/searchbar.js';
import Theme from "../../plugins/Theme.js";
const app = Vue.createApp({
name: 'CisMenuApp',
components: {
FhcSearchbar,
CisMenu
},
data: function() {
return {
searchbaroptions: {
origin: "cis",
cssclass: "",
calcheightonly: true,
types: {
employee: Vue.computed(() => this.$p.t("search/type_employee")),
student: Vue.computed(() => this.$p.t("search/type_student")),
room: Vue.computed(() => this.$p.t("search/type_room")),
organisationunit: Vue.computed(() => this.$p.t("search/type_organisationunit")),
cms: Vue.computed(() => this.$p.t("search/type_cms")),
dms: Vue.computed(() => this.$p.t("search/type_dms"))
},
actions: {
employee: {
defaultaction: {
type: "link",
action: function(data) {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router+
"/Cis/Profil/View/"+data.uid;
}
},
childactions: []
},
student: {
defaultaction: {
type: "link",
action: function (data) {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router +
"/Cis/Profil/View/" + data.uid;
}
},
childactions: []
},
room: {
defaultaction: {
type: "link",
renderif: function(data) {
if(data.content_id === null){
return false;
}
return true;
},
action: function(data) {
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/content/' + data.content_id;
return link;
}
},
childactions: [
{
label: "LV-Plan",
icon: "fas fa-bookmark",
type: "link",
action: function(data) {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/getRoomInformation/' + data.ort_kurzbz;
return link;
}
},
{
label: "Rauminformation",
icon: "fas fa-info-circle",
type: "link",
renderif: function(data) {
if(data.content_id === null){
return false;
}
return true;
},
action: function(data) {
const link= FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/content/' + data.content_id;
return link;
}
},
]
},
organisationunit: {
defaultaction: {
type: "link",
renderif: function(data) {
if(data.mailgroup) {
return true;
}
return false;
},
action: function(data) {
const link = 'mailto:' + data.mailgroup;
return link;
}
},
childactions: []
},
cms: {
defaultaction: {
type: "link",
action: function (data) {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
'/CisVue/Cms/content/' + data.content_id;
return link;
}
},
childactions: []
},
dms: {
defaultaction: {
type: "link",
action: function (data) {
const link = FHC_JS_DATA_STORAGE_OBJECT.app_root +
'cms/dms.php?id=' + data.dms_id;
return link;
}
},
childactions: []
}
}
}
};
},
methods: {
searchfunction: function(searchsettings) {
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
}
}
});
FhcApps.makeExtendable(app);
app.use(primevue.config.default, {
zIndex: {
overlay: 9000,
tooltip: 8000
}
})
app.use(PluginsPhrasen);
app.use(Theme);
app.mount('#cis-header');
+1 -45
View File
@@ -3,13 +3,10 @@ import DashboardAdmin from '../../components/Dashboard/Admin.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import ApiRenderers from '../../api/factory/renderers.js';
const app = Vue.createApp({
name: 'DashboardAdminApp',
data: () => ({
appSideMenuEntries: {},
renderers: null
appSideMenuEntries: {}
}),
components: {
CoreNavigationCmpt,
@@ -17,49 +14,8 @@ const app = Vue.createApp({
},
provide() {
return {
// TODO(chris): move those two into the components that need it
renderers: Vue.computed(() => this.renderers),
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone
};
},
created() {
this.$api
.call(ApiRenderers.loadRenderers())
.then(res => {
for (let rendertype of Object.keys(res.data)) {
let modalTitle = null;
let modalContent = null;
let calendarEvent = null;
if (res.data[rendertype].modalTitle)
modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalTitle)));
if (res.data[rendertype].modalContent)
modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalContent)));
if (res.data[rendertype].calendarEvent)
calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].calendarEvent)));
if (res.data[rendertype].calendarEventStyles) {
var head = document.head;
if (!head.querySelector(`link[href="${res.data[rendertype].calendarEventStyles}"]`)) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = res.data[rendertype].calendarEventStyles;
head.appendChild(link);
}
}
if (this.renderers === null) {
this.renderers = {};
}
if (!this.renderers[rendertype]) {
this.renderers[rendertype] = {}
}
this.renderers[rendertype].modalTitle = modalTitle;
this.renderers[rendertype].modalContent = modalContent;
this.renderers[rendertype].calendarEvent = calendarEvent;
}
})
.catch(this.$fhcAlert.handleSystemErrors);
}
});
app.use(PluginsPhrasen);
-356
View File
@@ -1,356 +0,0 @@
import FhcDashboard from '../../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import Theme from '../../plugins/Theme.js';
import contrast from '../../directives/contrast.js';
import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js";
import LvPlan from "../../components/Cis/LvPlan/Lehrveranstaltung.js";
import MyLvPlan from "../../components/Cis/LvPlan/Personal.js";
import MylvStudent from "../../components/Cis/Mylv/Student.js";
import Profil from "../../components/Cis/Profil/Profil.js";
import Raumsuche from "../../components/Cis/Raumsuche/Raumsuche.js";
import CmsNews from "../../components/Cis/Cms/News.js";
import CmsContent from "../../components/Cis/Cms/Content.js";
import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js";
import RoomInformation, {DEFAULT_MODE_RAUMINFO} from "../../components/Cis/Mylv/RoomInformation.js";
import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent.js";
import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js";
import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssistenz.js";
import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js";
import Studium from "../../components/Cis/Studium/Studium.js";
import ApiRenderers from '../../api/factory/renderers.js';
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(`/${ciPath}`),
routes: [
{
path: `/Cis/Studium`,
name: 'Studium',
component: Studium,
props: true
},
{
path: `/Cis/Profil/View/:uid`,
name: 'ProfilView',
component: Profil,
props: true
},
{
path: `/Cis/Profil`,
name: 'Profil',
component: Profil,
props: true
},
{
path: `/Cis/Abgabetool/Student/:student_uid_prop?`,
name: 'AbgabetoolStudent',
component: AbgabetoolStudent,
props: true
},
{
path: `/Cis/Abgabetool/Mitarbeiter`,
name: 'AbgabetoolMitarbeiter',
component: AbgabetoolMitarbeiter,
props: true
},
{
path: `/Cis/Abgabetool/Assistenz/:stg_kz_prop?`,
name: 'AbgabetoolAssistenz',
component: AbgabetoolAssistenz,
props: true
},
{
path: `/Cis/Abgabetool/Deadlines/:person_uid_prop?`,
name: 'DeadlineOverview',
component: DeadlineOverview,
props: true
},
{
path: `/Cis/Raumsuche`,
name: 'Raumsuche',
component: Raumsuche,
props: true
},
// Redirect old links to new format
{
path: "/CisVue/Cms/getRoomInformation/:ort_kurzbz",
name: "RoomInformationOld",
component: RoomInformation,
redirect: (to) => {
return { // redirect to longer Rauminfo url and map params
name: "RoomInformation",
params: { // in this case always populate other params since they are not optional
ort_kurzbz: to.params.ort_kurzbz,
mode: DEFAULT_MODE_RAUMINFO,
focus_date: new Date().toISOString().split("T")[0]
},
};
},
},
{
path: `/CisVue/Cms/getRoomInformation/:mode/:focus_date/:ort_kurzbz`,
name: 'RoomInformation',
component: RoomInformation,
props: (route) => { // validate and set mode/focus date if for some reason missing
const validModes = ["Month", "Week", "Day"];
// check mode string
const mode = route.params.mode &&
validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase())
? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()
: DEFAULT_MODE_RAUMINFO;
// default to today date if not provided
const d = new Date(route.params.focus_date)
const focus_date = !isNaN(d) ? route.params.focus_date : new Date().toISOString().split("T")[0];
// for consistency reasons format the props into one object but actually use a new name to we dont collide with
// existing viewData declaration written from codeigniter 3 into routerview tag
return {
propsViewData: {
mode,
focus_date,
ort_kurzbz: route.params.ort_kurzbz
}
};
},
beforeEnter: (to, from, next) => {
// missing mode or focus_date -> set defaults
if (!to.params.mode || !to.params.focus_date) {
next({
name: "RoomInformation",
params: {
mode: to.params.mode || DEFAULT_MODE_RAUMINFO,
focus_date: to.params.focus_date || new Date().toISOString().split("T")[0],
ort_kurzbz: route.params.ort_kurzbz
}
});
} else {
next();
}
}
},
{
path: `/CisVue/Cms/Content/:content_id`,
name: 'Content',
component: CmsContent,
props: true
},
{
path: `/CisVue/Cms/News`,
name: 'News',
component: CmsNews,
props: true
},
{
path: `/Cis/MyLv/:studiensemester?`,
name: 'MyLv',
component: MylvStudent,
props: true,
},
{
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
name: 'LvInfo',
component: Info,
props: true
},
// Redirect old links to new format
{
// only trigger on first param being numeric to avoid paths like "LvPlan/Month" entering here
path: "/Cis/LvPlan/:lv_id(\\d+)",
name: "LvPlanOld",
component: LvPlan,
redirect(to) {
const route = Vue.unref(router.currentRoute);
const { mode, focus_date } = route.params; // keep mode and focus_date if available
return { // redirect to longer LvPlan url and map params
name: "LvPlan",
params: {
mode,
focus_date,
lv_id: to.params.lv_id
},
};
},
},
{
path: `/Cis/LvPlan/:mode?/:focus_date?/:lv_id?`,
name: 'LvPlan',
component: LvPlan,
props(route) {
return {
propsViewData: route.params
};
}
},
{
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
name: 'MyLvPlan',
component: MyLvPlan,
props(route) {
return {
propsViewData: route.params
};
}
},
{
path: `/Cis4`,
name: 'Cis4',
component: FhcDashboard,
props: {dashboard: 'CIS'},
},
{
path: `/`,
name: 'FhcDashboard',
component: FhcDashboard,
props: {dashboard: 'CIS'},
},
{
path: '/:pathMatch(.*)*',
name: 'Fallback',
component: FhcDashboard,
props: {dashboard: 'CIS'},
redirect: () => {
return {
name: "Cis4",
params: {
dashboard: 'CIS'
},
};
},
},
]
})
const app = Vue.createApp({
name: 'FhcApp',
data: () => ({
appSideMenuEntries: {},
renderers: null,
}),
components: {},
computed: {
isMobile() {
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
return smallScreen;// && touchCapable;
}
},
provide() {
return { // provide injectable & watchable language property
language: Vue.computed(() => this.$p.user_language),
renderers: Vue.computed(() => this.renderers),
isMobile: this.isMobile
}
},
methods: {
isInternalRoute(href) {
const internalBase = window.location.origin
return href.startsWith(internalBase);
},
handleClick(event) {
const target = event.target.closest('a');
if(target?.id == 'skiplink') return
if (target && this.isInternalRoute(target.href)) {
const url = new URL(target.href)
const path = url.pathname
const base = this.$router.options.history.base
const route = path.replace(base, '') || '/'
// let click event propagate normally if we dont route internally
const res = this.$router.resolve(route)
if(!res?.matched?.length || res.name === 'Fallback') return
event.preventDefault(); // Prevent browser navigation
if(this.isMobile) { // toggle the menu
const navMain = document.getElementById('nav-main');
// fix unwanted toggle from off to on for some links on mobile
if(navMain.classList.contains('show')){
document.getElementById('nav-main-btn').click();
}
}
this.$router.push(route);
}
}
},
async created(){
await this.$api
.call(ApiRenderers.loadRenderers())
.then(res => res.data)
.then(data => {
for (let rendertype of Object.keys(data)) {
let modalTitle = null;
let modalContent = null;
let calendarEvent = null;
if (data[rendertype].modalTitle)
modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalTitle)));
if (data[rendertype].modalContent)
modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalContent)));
if (data[rendertype].calendarEvent)
calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].calendarEvent)));
if (data[rendertype].calendarEventStyles){
var head = document.head;
if(!head.querySelector(`link[href="${data[rendertype].calendarEventStyles}"]`)){
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = data[rendertype].calendarEventStyles;
head.appendChild(link);
}
}
if(this.renderers === null) {
this.renderers = {};
}
if (!this.renderers[rendertype]) {
this.renderers[rendertype] = {}
}
this.renderers[rendertype].modalTitle = modalTitle;
this.renderers[rendertype].modalContent = modalContent;
this.renderers[rendertype].calendarEvent = calendarEvent;
}
});
},
mounted() {
document.addEventListener('click', this.handleClick);
},
beforeUnmount() {
document.removeEventListener('click', this.handleClick);
},
});
// kind of a bandaid for bad css on some pages to avoid horizontal scroll
setScrollbarWidth();
app.config.globalProperties.$capitalize = capitalize;
FhcApps.router.makeExtendable(router);
FhcApps.makeExtendable(app);
app.use(router);
app.use(primevue.config.default, {
zIndex: {
overlay: 9000,
tooltip: 8000
}
})
app.directive('tooltip', primevue.tooltip);
app.use(PluginsPhrasen);
app.use(Theme);
app.directive('contrast', contrast);
app.mount('#fhccontent');
router.afterEach((to, from, failure) => {
app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath));
});
-16
View File
@@ -1,16 +0,0 @@
import {CoreNavigationCmpt} from '../components/navigation/Navigation.js';
import DashboardAdmin from '../components/Dashboard/Admin.js';
import Phrases from "../plugin/Phrasen.js"
Vue.createApp({
name: 'DashboardAdminApp',
data: () => ({
appSideMenuEntries: {}
}),
components: {
CoreNavigationCmpt,
DashboardAdmin
},
mounted() {
}
}).use(Phrases).mount('#main');
@@ -20,7 +20,8 @@ export default {
},
inject: {
mode: "mode",
dropableEvents: "dropableEvents"
dropableEvents: "dropableEvents",
timezone: "timezone"
},
props: {
events: Array,
+6 -8
View File
@@ -3,6 +3,7 @@ import FhcCalendar from "./Base.js";
import ApiLvPlan from '../../api/factory/lvPlan.js';
import { useEventLoader } from '../../composables/EventLoader.js';
import { useRenderers } from '../../composables/Renderers.js';
import ModeDay from './Mode/Day.js';
import ModeWeek from './Mode/Week.js';
@@ -13,14 +14,7 @@ export default {
components: {
FhcCalendar
},
inject: [
"renderers"
],
props: {
timezone: {
type: String,
required: true
},
date: {
type: [Date, String, Number, luxon.DateTime],
default: luxon.DateTime.local()
@@ -41,6 +35,7 @@ export default {
],
data() {
return {
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
modes: {
day: Vue.markRaw(ModeDay),
week: Vue.markRaw(ModeWeek),
@@ -99,10 +94,13 @@ export default {
context.emit('update:lv', newValue);
});
const { renderers } = useRenderers();
return {
rangeInterval,
events,
lv
lv,
renderers
};
},
created() {
+7 -9
View File
@@ -1,6 +1,7 @@
import FhcCalendar from "./Base.js";
import { useEventLoader } from '../../composables/EventLoader.js';
import { useRenderers } from '../../composables/Renderers.js';
import ModeList from '../Calendar/Mode/List.js';
@@ -9,22 +10,17 @@ export default {
components: {
FhcCalendar
},
inject: [
"renderers"
],
props: {
timezone: {
type: String,
required: true
},
getPromiseFunc: {
type: Function,
required: true
}
},
data() {
const timezone = FHC_JS_DATA_STORAGE_OBJECT.timezone;
return {
now: luxon.DateTime.now().setZone(this.timezone),
timezone,
now: luxon.DateTime.now().setZone(timezone),
modes: {
list: Vue.markRaw(ModeList)
},
@@ -59,10 +55,12 @@ export default {
const rangeInterval = Vue.ref(null);
const { events } = useEventLoader(rangeInterval, props.getPromiseFunc);
const { renderers } = useRenderers();
return {
rangeInterval,
events
events,
renderers
};
},
template: /* html */`
@@ -22,7 +22,7 @@ export default {
computed:{
currentDay() {
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
return luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
return this.propsViewData?.focus_date;
},
currentMode() {
@@ -95,7 +95,6 @@ export default {
<fhc-calendar
v-else-if="lv"
ref="calendar"
:timezone="viewData.timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
+2 -4
View File
@@ -15,7 +15,6 @@ export default {
propsViewData: Object
},
data() {
const now = luxon.DateTime.now().setZone(this.viewData.timezone);
return {
studiensemester_kurzbz: null,
studiensemester_start: null,
@@ -26,7 +25,7 @@ export default {
},
computed:{
currentDay() {
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
},
currentMode() {
return this.propsViewData?.mode || DEFAULT_MODE_LVPLAN;
@@ -35,7 +34,7 @@ export default {
if (!this.studiensemester_start || !this.studiensemester_ende || !this.uid)
return false;
const opts = { zone: this.viewData.timezone };
const opts = { zone: FHC_JS_DATA_STORAGE_OBJECT.timezone };
const start = luxon.DateTime
.fromISO(this.studiensemester_start, opts)
.toUnixInteger();
@@ -115,7 +114,6 @@ export default {
<fhc-calendar
ref="calendar"
v-model:lv="lv"
:timezone="viewData.timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
+2 -3
View File
@@ -27,7 +27,7 @@ export default {
computed:{
currentDay() {
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
return luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
return this.propsViewData?.focus_date;
},
currentMode() {
@@ -47,7 +47,7 @@ export default {
return;
}
const opts = { zone: this.viewData.timezone };
const opts = { zone: FHC_JS_DATA_STORAGE_OBJECT.timezone };
const start = luxon.DateTime
.fromISO(this.studiensemester_start, opts)
.toUnixInteger();
@@ -124,7 +124,6 @@ export default {
<hr>
<fhc-calendar
ref="calendar"
:timezone="viewData.timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
@@ -15,7 +15,7 @@ export default {
},
computed: {
currentDay() {
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.viewData.timezone).toISODate();
return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
},
currentMode() {
return this.propsViewData?.mode || DEFAULT_MODE_RAUMINFO;
@@ -51,7 +51,6 @@ export default {
<hr>
<fhc-calendar
ref="calendar"
:timezone="viewData.timezone"
:get-promise-func="getPromiseFunc"
:date="currentDay"
:mode="currentMode"
+92 -45
View File
@@ -4,7 +4,6 @@ import DashboardAdminWidgets from "./Admin/Widgets.js";
import DashboardAdminPresets from "./Admin/Presets.js";
import ApiDashboardBoard from "../../api/factory/dashboard/board.js";
import ApiDashboardWidget from "../../api/factory/dashboard/widget.js";
export default {
name: 'DashboardAdmin',
@@ -16,7 +15,7 @@ export default {
provide() {
return {
adminMode: true,
widgetsSetup: Vue.computed(() => this.dashboards[this.current] ? this.dashboards[this.current].widgetSetup : null)
widgetsSetup: Vue.computed(() => this.dashboard ? this.dashboard.widgetSetup : null)
};
},
data() {
@@ -34,33 +33,32 @@ export default {
methods: {
dashboardAdd() {
let _name = '';
BsPrompt.popup('New Dashboard name').then(
name => {
_name = name;
BsPrompt
.popup('New Dashboard name')
.then(dashboard_kurzbz => {
const params = {
dashboard_kurzbz: name
dashboard_kurzbz
};
return this.$api
.call(ApiDashboardBoard.add(params))
.then(response =>{
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
let newDashboard = {
dashboard_id: response.data,
dashboard_kurzbz: _name,
dashboard_kurzbz,
beschreibung: ''
};
this.dashboards.push(newDashboard);
this.current = newDashboard.dashboard_id;
})
.catch(this.$fhcAlert.handleSystemError);
});
});
},
dashboardUpdate(dashboard) {
return this.$api
this.$api
.call(ApiDashboardBoard.update(dashboard))
.then(response =>{
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id);
@@ -70,73 +68,122 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
dashboardDelete(dashboard_id) {
return this.$api
this.$api
.call(ApiDashboardBoard.delete(dashboard_id))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.current = -1;
this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id);
});
})
.catch(this.$fhcAlert.handleSystemError);
},
assignWidgets(widgets) {
this.widgets = widgets;
/*while (this.widgets.length)
this.widgets.pop();
for (var i in widgets)
this.widgets.push(widgets[i]);*/
}
},
created() {
this.$api
.call(ApiDashboardBoard.list())
.then(result => {
this.dashboards = result.data.retval;
for (const dashboard of this.dashboards) {
this.$api
.call(ApiDashboardWidget.list(dashboard.dashboard_id))
.then(res => {
dashboard.widgetSetup = res.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
this.dashboards = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `<div class="dashboard-admin">
template: /* html */`
<div class="dashboard-admin">
<div class="input-group">
<label for="dashboard-select" class="input-group-text">Dashboard:</label>
<select id="dashboard-select" class="form-select" v-model="current">
<option v-for="dashboard in dashboards" :key="dashboard.dashboard_id" :value="dashboard.dashboard_id">{{dashboard.dashboard_kurzbz}}</option>
<label for="dashboard-select" class="input-group-text">
Dashboard:
</label>
<select id="dashboard-select" v-model="current" class="form-select">
<option
v-for="dashboard in dashboards"
:key="dashboard.dashboard_id"
:value="dashboard.dashboard_id"
>{{ dashboard.dashboard_kurzbz }}</option>
</select>
<button class="btn btn-outline-secondary" type="button" @click="dashboardAdd"><i class="fa-solid fa-plus"></i></button>
<button
class="btn btn-outline-secondary"
type="button"
@click="dashboardAdd"
><i class="fa-solid fa-plus"></i></button>
</div>
<div v-if="dashboard">
<ul class="nav nav-tabs mt-3" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" id="edit-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">{{this.$p.t('ui', 'bearbeiten')}}</button>
<button
id="edit-tab"
class="nav-link"
data-bs-toggle="tab"
data-bs-target="#edit"
type="button"
role="tab"
aria-controls="edit"
aria-selected="false"
>{{ this.$p.t('ui', 'bearbeiten') }}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="widgets-tab" data-bs-toggle="tab" data-bs-target="#widgets" type="button" role="tab" aria-controls="widgets" aria-selected="true">Widgets</button>
<button
id="widgets-tab"
class="nav-link active"
data-bs-toggle="tab"
data-bs-target="#widgets"
type="button"
role="tab"
aria-controls="widgets"
aria-selected="true"
>Widgets</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="presets-tab" data-bs-toggle="tab" data-bs-target="#presets" type="button" role="tab" aria-controls="presets" aria-selected="false">Presets</button>
<button
class="nav-link"
id="presets-tab"
data-bs-toggle="tab"
data-bs-target="#presets"
type="button"
role="tab"
aria-controls="presets"
aria-selected="false"
>Presets</button>
</li>
</ul>
<div class="tab-content pt-3">
<div class="tab-pane fade" id="edit" role="tabpanel" aria-labelledby="edit-tab">
<dashboard-admin-edit v-bind="dashboard" :key="dashboard.dashboard_id" @change="dashboardUpdate($event)" @delete="dashboardDelete($event)"></dashboard-admin-edit>
<div
id="edit"
class="tab-pane fade"
role="tabpanel"
aria-labelledby="edit-tab"
>
<dashboard-admin-edit
v-bind="dashboard"
@change="dashboardUpdate($event)"
@delete="dashboardDelete($event)"
></dashboard-admin-edit>
</div>
<div class="tab-pane fade show active" id="widgets" role="tabpanel" aria-labelledby="widgets-tab">
<dashboard-admin-widgets :key="dashboard.dashboard_id" :dashboard_id="dashboard.dashboard_id" :widgets="widgets" @assign-widgets="assignWidgets"></dashboard-admin-widgets>
<div
id="widgets"
class="tab-pane fade show active"
role="tabpanel"
aria-labelledby="widgets-tab"
>
<dashboard-admin-widgets
:dashboard_id="dashboard.dashboard_id"
:widgets="widgets"
@assign-widgets="assignWidgets"
></dashboard-admin-widgets>
</div>
<div class="tab-pane fade" id="presets" role="tabpanel" aria-labelledby="presets-tab">
<dashboard-admin-presets :dashboard="dashboard.dashboard_kurzbz" :widgets="widgets"></dashboard-admin-presets>
<div
id="presets"
class="tab-pane fade"
role="tabpanel"
aria-labelledby="presets-tab"
>
<dashboard-admin-presets
:dashboard="dashboard.dashboard_kurzbz"
:widgets="widgets"
></dashboard-admin-presets>
</div>
</div>
</div>
+34 -13
View File
@@ -1,15 +1,15 @@
import BsConfirm from '../../Bootstrap/Confirm.js';
export default {
emits: [
"change",
"delete"
],
props: {
dashboard_id: Number,
dashboard_kurzbz: String,
beschreibung: String
},
emits: [
"change",
"delete"
],
data() {
return {
kurzbz: this.dashboard_kurzbz,
@@ -18,22 +18,43 @@ export default {
},
methods: {
sendDelete() {
BsConfirm.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo'))
.then(() => this.$emit('delete', this.dashboard_id)).catch();
BsConfirm
.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo'))
.then(() => this.$emit('delete', this.dashboard_id))
.catch();
}
},
template: `<div class="dashboard-admin-edit px-3">
template: /* html */`
<div class="dashboard-admin-edit px-3">
<div class="mb-3">
<label for="dashboard-admin-edit-kurzbz">Kurz Bezeichnung</label>
<input id="dashboard-admin-edit-kurzbz" type="text" class="form-control" v-model="kurzbz">
<label for="dashboard-admin-edit-kurzbz">{{ $p.t('dashboard/kurzbz') }}</label>
<input
id="dashboard-admin-edit-kurzbz"
v-model="kurzbz"
type="text"
class="form-control"
>
</div>
<div class="mb-3">
<label for="dashboard-admin-edit-beschreibung">Beschreibung</label>
<textarea id="dashboard-admin-edit-beschreibung" class="form-control" v-model="desc"></textarea>
<label for="dashboard-admin-edit-beschreibung">{{ $p.t('global/beschreibung') }}</label>
<textarea
id="dashboard-admin-edit-beschreibung"
v-model="desc"
class="form-control"
></textarea>
</div>
<div>
<button class="btn btn-danger" @click="sendDelete">{{this.$p.t('ui', 'loeschen')}}</button>
<button class="btn btn-primary" @click="$emit('change', {dashboard_id,dashboard_kurzbz:kurzbz,beschreibung:desc})">{{this.$p.t('ui', 'btnAktualisieren')}}</button>
<button class="btn btn-danger" @click="sendDelete">
{{ this.$p.t('ui', 'loeschen') }}
</button>
<button
class="btn btn-primary"
@click="$emit('change', {
dashboard_id,
dashboard_kurzbz: kurzbz,
beschreibung: desc
})"
>{{ this.$p.t('ui', 'btnAktualisieren') }}</button>
</div>
</div>`
}
+56 -32
View File
@@ -12,18 +12,27 @@ export default {
dashboard: String,
widgets: Array
},
data: () => ({
funktionen: {},
sections: [],
tmpLoading: ''
}),
data() {
return {
funktionen: {},
sections: [],
selectedFunktionen: [],
abortController: null
};
},
computed: {
pickerWidgets() {
return this.widgets.filter(widget => widget.allowed);
}
},
watch: {
dashboard() {
this.loadSections();
this.loadFunktionen();
}
},
methods: {
widgetAdd(section_name, widget) {
widgetAdd(widget, section_name) {
this.$refs.widgetpicker.getWidget().then(widget_id => {
widget.widget = widget_id;
widget.id = 'loading_' + String((new Date()).valueOf());
@@ -64,22 +73,26 @@ export default {
})
.catch(() => {});
},
widgetUpdate(section_name, payload) {
payload = payload[section_name];
widgetUpdate(payload, section_name) {
for (var k in payload) {
const section = this.sections.find(section => section.name == section_name);
for (var wid in section.widgets) {
if (section.widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]);
// NOTE(chris): remove internal props
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id'])
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id', 'custom'])
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
if (payload[k].place) {
Object.values(payload[k].place).forEach(place => {
if (place.pinned === false)
delete place.pinned;
});
}
payload[k].widgetid = k;
delete payload[k].custom;
}
this.$api
.call(Object.entries(payload).map(([key, widget]) => [
@@ -106,7 +119,7 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
widgetRemove(section_name, id) {
widgetRemove(id, section_name) {
const params = {
db: this.dashboard,
funktion_kurzbz: section_name,
@@ -122,21 +135,22 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
loadSections(evt) {
let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value);
this.sections = [];
this.tmpLoading = funktionen.join('###');
loadSections() {
const params = {
db: this.dashboard,
funktionen
funktionen: this.selectedFunktionen
};
if (this.abortController)
this.abortController.abort();
this.abortController = new AbortController();
const signal = this.abortController.signal;
this.sections = [];
return this.$api
.call(ApiDashboardPreset.getBatch(params))
.call(ApiDashboardPreset.getBatch(params), { signal })
.then(result => {
if (this.tmpLoading !== funktionen.join('###'))
return; // NOTE(chris): prevent race condition
for (var section in result.data) {
let widgets = [];
for (var wid in result.data[section]) {
@@ -151,7 +165,6 @@ export default {
}
})
.catch(this.$fhcAlert.handleSystemError);
},
loadFunktionen() {
this.$api
@@ -165,17 +178,17 @@ export default {
created() {
this.loadFunktionen();
},
watch: {
dashboard() {
// TODO(chris): this should be done without a watcher
this.loadSections({target:this.$refs.funktionenList});
this.loadFunktionen();
}
},
template: `<div class="dashboard-admin-presets">
template: /* html */`
<div class="dashboard-admin-presets">
<div class="row">
<div class="col-3">
<select ref="funktionenList" style="height:30em" class="form-control" multiple @input="loadSections">
<select
v-model="selectedFunktionen"
class="form-control"
style="height:30em"
multiple
@change="loadSections"
>
<option
v-for="funktion in funktionen"
:key="funktion.funktion_kurzbz"
@@ -185,9 +198,20 @@ export default {
</select>
</div>
<div class="col-9">
<dashboard-section v-for="section in sections" :key="section.name" :name="section.name" :widgets="section.widgets" @widget-add="widgetAdd" @widget-update="widgetUpdate" @widget-remove="widgetRemove"></dashboard-section>
<dashboard-section
v-for="section in sections"
:key="section.name"
:name="section.name"
:widgets="section.widgets"
@widget-add="widgetAdd"
@widget-update="widgetUpdate"
@widget-remove="widgetRemove"
></dashboard-section>
</div>
</div>
<dashboard-widget-picker ref="widgetpicker" :widgets="pickerWidgets"></dashboard-widget-picker>
<dashboard-widget-picker
ref="widgetpicker"
:widgets="pickerWidgets"
></dashboard-widget-picker>
</div>`
}
@@ -1,14 +1,14 @@
import ApiDashboardWidget from "../../../api/factory/dashboard/widget.js";
export default {
emits: [
"change",
"assignWidgets"
],
props: {
dashboard_id: Number,
widgets: Array
},
emits: [
"change",
"assignWidgets"
],
methods: {
sendChange(widget_id) {
let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed;
@@ -29,11 +29,27 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `
template: /* html */`
<div class="dashboard-admin-widgets">
<div v-for="widget in widgets" :key="widget.widget_id" class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" :id="'dashboard-admin-widgets-' + widget.widget_id" v-model="widget.allowed" @input.prevent="sendChange(widget.widget_id)">
<label class="form-check-label" :for="'dashboard-admin-widgets-' + widget.widget_id">{{(widget.setup && widget.setup.name) || widget.widget_kurzbz}}</label>
<div
v-for="widget in widgets"
:key="widget.widget_id"
class="form-check form-switch"
>
<input
:id="'dashboard-admin-widgets-' + widget.widget_id"
v-model="widget.allowed"
class="form-check-input"
type="checkbox"
role="switch"
@input.prevent="sendChange(widget.widget_id)"
>
<label
class="form-check-label"
:for="'dashboard-admin-widgets-' + widget.widget_id"
>
{{ (widget.setup && widget.setup.name) || widget.widget_kurzbz }}
</label>
</div>
</div>`
}
+34 -17
View File
@@ -21,7 +21,7 @@ export default {
type: Object,
required: true,
validator(value) {
return value && value.name && value.timezone
return value && value.name
}
}
},
@@ -35,14 +35,12 @@ export default {
},
provide() {
return {
editMode: Vue.computed(()=>this.editMode),
widgetsSetup: Vue.computed(() => this.widgetsSetup),
timezone: Vue.computed(() => this.viewData.timezone)
editMode: Vue.computed(() => this.editMode),
widgetsSetup: Vue.computed(() => this.widgetsSetup)
}
},
methods: {
widgetAdd(section_name, widget) {
// TODO(chris): remove section_name? (change order of params => get rid of it)
widgetAdd(widget) {
this.$refs.widgetpicker
.getWidget()
.then(widget_id => {
@@ -64,19 +62,24 @@ export default {
})
.catch(() => {});
},
widgetUpdate(section_name, payload) {
payload = payload[section_name];
widgetUpdate(payload) {
for (var k in payload) {
for (var wid in this.widgets) {
if (this.widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]);
// NOTE(chris): remove internal props
for (var prop of ['_x','_y','_w','_h','index','id','preset'])
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id', 'preset'])
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
if (payload[k].place) {
Object.values(payload[k].place).forEach(place => {
if (place.pinned === false)
delete place.pinned;
});
}
payload[k].widgetid = k;
}
this.$api
@@ -113,7 +116,7 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
widgetRemove(section_name, id) {
widgetRemove(id) {
this.$api
.call(ApiDashboardUser.removeWidget(this.dashboard, id))
.then(() => {
@@ -138,8 +141,8 @@ export default {
const widgets = [];
const remove = [];
for (var wid in res.data.general.widgets) {
let widget = res.data.general.widgets[wid];
for (var wid in res.data) {
let widget = res.data[wid];
widget.id = wid;
if (widget.custom || widget.preset) {
widgets.push(widget);
@@ -149,19 +152,33 @@ export default {
}
}
remove.forEach(wid => this.widgetRemove('general', wid));
remove.forEach(wid => this.widgetRemove(wid));
this.widgets = widgets;
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `
template: /* html */`
<div class="core-dashboard">
<h3>
{{ $p.t('global/personalGreeting', [ viewData?.name ]) }}
<button style="margin-left: 8px;" class="btn" @click="editMode = !editMode" aria-label="edit dashboard" v-tooltip="{showDelay:1000,value:'edit dashboard'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
<button
class="btn ms-2"
aria-label="edit dashboard"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/edit') }"
@click="editMode = !editMode"
><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
</h3>
<dashboard-section :seperator="0" name="general" :widgets="widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section>
<dashboard-widget-picker ref="widgetpicker" :widgets="widgetsSetup"></dashboard-widget-picker>
<dashboard-section
name="general"
:widgets="widgets"
@widget-add="widgetAdd"
@widget-update="widgetUpdate"
@widget-remove="widgetRemove"
></dashboard-section>
<dashboard-widget-picker
ref="widgetpicker"
:widgets="widgetsSetup"
></dashboard-widget-picker>
</div>`
}
+293 -133
View File
@@ -1,7 +1,13 @@
import BsModal from "../Bootstrap/Modal.js";
import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
import HeightTransition from "../Tranistion/HeightTransition.js";
import { enableDragDropTouch } from "../../../../vendor/drag-drop-touch-js/dragdroptouch/dist/drag-drop-touch.esm.min.js";
if (!document.dragDropTouchActive) {
enableDragDropTouch();
document.dragDropTouchActive = true;
}
export default {
name: 'Item',
components: {
@@ -11,18 +17,14 @@ export default {
data: () => ({
component: "",
arguments: null,
target: false,
widget: null,
tmpConfig: {},
isLoading: false,
hasConfig: false,
sharedData: null,
sharedData: null
}),
emits: [
"change",
"remove",
"dragstart",
"resizestart",
"configOpened",
"configClosed",
"pinItem",
@@ -30,41 +32,85 @@ export default {
],
props: [
"id",
"widgetID",
"config",
"width",
"height",
"custom",
"hidden",
"editMode",
"loading",
"loading", // widget got added and is waiting for backend to save in db
"item_data",
"place",
"setup",
"dragstate",
"resizeOverlay",
"additionalRow"
"widgetTemplate",
"source"
],
computed: {
maxHeight(){
return this.setup?.height?.max;
},
maxWidth(){
if (Object.prototype.toString.call(this.setup?.width) == "[object Number]"){
return this.setup?.width;
sourceInfoTooltip() {
switch (this.source) {
case null:
return '';
case 'general':
return this.$p.t('dashboard', 'widgetFromGeneralSection');
case 'custom':
return this.$p.t('dashboard', 'widgetFromCustomSection');
default:
return this.$p.t('dashboard', 'widgetFromFunktionSection', [this.source]);
}
return this.setup?.width?.max;
},
minHeight() {
return this.setup?.height?.min;
isResizeableHorizontal() {
if (this.widgetTemplate.setup.width === undefined)
return true;
if (Object.prototype.toString.call(this.widgetTemplate.setup.width) == "[object Number]")
return false;
if (this.widgetTemplate.setup.width.min === undefined) {
if (this.widgetTemplate.setup.width.max === undefined)
return true;
return this.widgetTemplate.setup.width.max > 1;
}
if (this.widgetTemplate.setup.width.max === undefined)
return true;
return this.widgetTemplate.setup.width.max > this.widgetTemplate.setup.width.min;
},
minWidth() {
return this.setup?.width?.min;
isResizeableVertical() {
if (this.widgetTemplate.setup.height === undefined)
return true;
if (Object.prototype.toString.call(this.widgetTemplate.setup.height) == "[object Number]")
return false;
if (this.widgetTemplate.setup.height.min === undefined) {
if (this.widgetTemplate.setup.height.max === undefined)
return true;
return this.widgetTemplate.setup.height.max > 1;
}
if (this.widgetTemplate.setup.height.max === undefined)
return true;
return this.widgetTemplate.setup.height.max > this.widgetTemplate.setup.height.min;
},
isResizeable(){
return this.maxWidth >1 || this.maxHeight >1;
isResizeable() {
return this.isResizeableVertical || this.isResizeableHorizontal;
},
isPinned(){
resizeClasses() {
const classes = {
icon: 'fa-up-right-and-down-left-from-center mirror-x',
button: 'cursor-nw-resize'
};
if (!this.isResizeableHorizontal) {
classes.icon = 'fa-up-down pe-2';
classes.button = 'cursor-ns-resize';
} else if (!this.isResizeableVertical) {
classes.icon = 'fa-left-right pe-2';
classes.button = 'cursor-ew-resize';
}
return classes;
},
isPinned() {
return this.place?.pinned ? true : false;
},
ready() {
@@ -80,16 +126,16 @@ export default {
}
},
methods: {
unpin(){
unpin() {
// Unpinning is only possible in edit mode
if(!this.editMode)
if (!this.editMode)
return;
let result = { item: this.item_data, x: this.item_data.x, y: this.item_data.y };
let result = { item: this.item_data, pinned: false };
this.$emit('unPinItem', [result]);
},
pinItem(){
let result = { item: this.item_data, x: this.item_data.x, y: this.item_data.y};
this.$emit('pinItem',[result]);
pinItem() {
let result = { item: this.item_data, pinned: true };
this.$emit('pinItem', [result]);
},
getWidgetC4Link(widget) {
return (FHC_JS_DATA_STORAGE_OBJECT.app_root +
@@ -101,22 +147,6 @@ export default {
handleHideBsModal() {
this.$emit('configClosed')
},
mouseDown(e) {
this.target = e.target;
},
startDrag(e) {
if (this.$refs.dragHandle.contains(this.target)) {
this.$emit("dragstart", e);
} else if (
this.isResizeable &&
this.$refs.resizeHandle.contains(this.target)
) {
if (this.isResizeable) this.$emit("resizestart", e);
else e.preventDefault();
} else {
e.preventDefault();
}
},
openConfig() {
this.tmpConfig = { ...this.arguments };
this.$refs.config.show();
@@ -135,110 +165,240 @@ export default {
},
sendChangeConfig(config) {
for (var k in config) {
if (this.widget.arguments[k] == config[k]) {
delete config[k];
if (this.widgetTemplate.arguments[k] == config[k]) {
delete config[k];
}
}
this.$emit("change", config);
},
async initializeComponent() {
if (
this.widgetTemplate
&& this.widgetTemplate.setup
&& this.widgetTemplate.widget_id
&& this.widgetTemplate.arguments
) {
let component = (await import(this.widgetTemplate.setup.file)).default;
this.$options.components["widget" + this.widgetTemplate.widget_id] = component;
this.component = "widget" + this.widgetTemplate.widget_id;
this.arguments = { ...this.widgetTemplate.arguments, ...this.config };
this.tmpConfig = { ...this.arguments };
}
}
},
watch: {
config() {
this.arguments = { ...this.widget?.arguments, ...this.config };
this.arguments = { ...this.widgetTemplate?.arguments, ...this.config };
this.tmpConfig = { ...this.arguments };
this.$refs.config && this.$refs.config.hide();
this.isLoading = false;
},
widgetTemplate() {
this.initializeComponent();
}
},
setup() {
const { actions } = useCachedWidgetLoader();
return {
loadWidget: actions.load
};
},
async created() {
this.widget = await this.loadWidget(this.id);
let component = (await import(this.widget.setup.file)).default;
this.$options.components["widget" + this.widget.widget_id] = component;
this.component = "widget" + this.widget.widget_id;
this.arguments = { ...this.widget.arguments, ...this.config };
this.tmpConfig = { ...this.arguments };
created() {
this.initializeComponent();
},
template: /*html*/ `
<div v-if="loading">
<div class="d-flex justify-content-center align-items-center h-100">
<article
v-if="!hidden || editMode"
class="dashboard-item card overflow-hidden h-100 position-relative"
:class="{
'hidden-widget': hidden,
[arguments?.className]: arguments && arguments.className
}"
>
<div v-if="loading" class="d-flex justify-content-center align-items-center h-100">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
</div>
<div v-else-if="!hidden || editMode" :id="widgetID" class="dashboard-item card overflow-hidden h-100 position-relative" :class="{'hiddenWidget':hidden, 'draggedItem':dragstate, 'dashboard-item-overlay':resizeOverlay, [arguments?.className]:arguments && arguments.className}">
<div v-show="!dragstate" class="h-100 card border-0">
<div v-if="widget" class="card-header d-flex ps-0 pe-2 align-items-center">
<Transition>
<span type="button" v-if="editMode && !isPinned" drag-action="move" class="col-auto mx-2 px-2 cursor-move" aria-label="move widget" v-tooltip="{showDelay:1000, value:'move widget'}"><i class="fa-solid fa-grip-vertical" aria-hidden="true"></i></span>
</Transition>
<span class="col mx-2 px-2">{{ widget.setup.name }}</span>
<template v-if="isPinned">
<div type="button" role="button" v-if="editMode" pinned="true" @click="unpin" title="unpin item" aria-label="unpin item" class="pin cursor-pointer col-auto me-2">
<i class="fa-solid fa-thumbtack " aria-hidden="true"></i>
</div>
<div v-else class="col-auto me-2">
<i class="fa-solid fa-thumbtack "></i>
</div>
</template>
<template v-else>
<div type="button" role="button" v-if="editMode" class="col-auto me-2 pin" @click="pinItem" aria-label="pin item" title="pin item">
<i class="fa-solid fa-thumbtack" aria-hidden="true" style="color:lightgray;"></i>
</div>
</template>
<a type="button" v-if="widget.setup.cis4link" :href="getWidgetC4Link(widget)" aria-label="widget link" v-tooltip="{showDelay:1000, value:'widget link'}" class="col-auto ms-auto ">
<i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true"></i>
</a>
<a type="button" v-if="hasConfig" class="col-auto px-1" href="#" @click.prevent="openConfig" aria-label="configure widget" v-tooltip="{showDelay:1000,value:'configure widget'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></a>
<a type="button" v-if="custom && editMode" class="col-auto px-1" aria-label="delete widget" v-tooltip="{showDelay:1000,value:'delete widget'}" href="#" @click.prevent="$emit('remove')">
<i class="fa-solid fa-trash" aria-hidden="true"></i>
</a>
<Transition>
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
<input class="form-check-input ms-0" type="checkbox" role="switch" aria-label="toggle widget" id="flexSwitchCheckChecked" v-model="visible" :value="true">
</div>
</Transition>
</div>
<div v-if="ready" class="card-body overflow-hidden p-0">
<component :is="component" v-model:shared-data="sharedData" :config="arguments" :width="width" :height="height" @setConfig="setConfig" @change="changeConfigManually"></component>
</div>
<div v-else class="card-body overflow-hidden text-center d-flex flex-column justify-content-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
<bs-modal v-if="hasConfig" ref="config" @hideBsModal="handleHideBsModal" @showBsModal="handleShowBsModal">
<template v-slot:title>
{{ widget ? 'Config for ' + widget.setup.name : '' }}
</template>
<template v-slot:default>
<component v-if="ready && !isLoading" :is="component" v-model:shared-data="sharedData" :config="tmpConfig" @change="changeConfig" :configMode="true"></component>
<div v-else class="text-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
</template>
<template v-if="!widget?.setup?.hideFooter" v-slot:footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" @click="changeConfig">Save changes</button>
</template>
</bs-modal>
<height-transition>
<div v-if="editMode && isResizeable && !isPinned " class="card-footer d-flex justify-content-end p-0">
<template v-if="maxWidth < 2">
<span type="button" drag-action="resize" class="col-auto px-1 cursor-ns-resize" aria-label="resize widget" v-tooltip="{showDelay:1000, value:'resize widget'}">
<i class="fa-solid fa-up-down pe-2" aria-hidden="true"></i>
</span>
</template>
<template v-else-if="maxHeight < 2">
<span type="button" drag-action="resize" class="col-auto px-1 cursor-ew-resize" aria-label="resize widget" v-tooltip="{showDelay:1000, value:'resize widget'}">
<i class="fa-solid fa-left-right pe-2" aria-hidden="true"></i>
<template v-else>
<header
v-if="widgetTemplate"
class="card-header d-flex ps-0 pe-2 align-items-center"
>
<!-- move handle -->
<Transition>
<span
v-if="editMode && !isPinned"
type="button"
drag-action="move"
class="col-auto mx-2 px-2 cursor-move"
draggable="true"
aria-hidden="true"
:aria-label="$p.t('dashboard/widget_move')"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_move') }"
>
<i class="fa-solid fa-grip-vertical" aria-hidden="true"></i>
</span>
</Transition>
<!-- TITLE -->
<h4 class="col mb-0 mx-2 px-2 fs-6 lh-base">
{{ widgetTemplate.setup.name }}
</h4>
<!-- source info -->
<div
v-if="source"
class="col-auto me-2"
:aria-label="sourceInfoTooltip"
v-tooltip="{ class: 'w-100', value: sourceInfoTooltip }"
>
<i class="fa-solid fa-circle-info" aria-hidden="true"></i>
</div>
<!-- pin button -->
<template v-if="isPinned">
<div
v-if="editMode"
type="button"
role="button"
class="pin cursor-pointer col-auto me-2"
:title="$p.t('dashboard/widget_unpin')"
aria-hidden="true"
:aria-label="$p.t('dashboard/widget_unpin')"
pinned="true"
@click="unpin"
>
<i class="fa-solid fa-thumbtack" aria-hidden="true"></i>
</div>
<div v-else class="col-auto me-2" aria-hidden="true">
<i class="fa-solid fa-thumbtack"></i>
</div>
</template>
<template v-else>
<span type="button" drag-action="resize" class="col-auto px-1 cursor-nw-resize" aria-label="resize widget" v-tooltip="{showDelay:1000, value:'resize widget'}">
<i class="fa-solid fa-up-right-and-down-left-from-center mirror-x" aria-hidden="true"></i>
</span>
<div
v-if="editMode"
type="button"
role="button"
class="col-auto me-2 pin"
:title="$p.t('dashboard/widget_pin')"
aria-hidden="true"
:aria-label="$p.t('dashboard/widget_pin')"
@click="pinItem"
>
<i class="fa-solid fa-thumbtack" aria-hidden="true" style="color:lightgray;"></i>
</div>
</template>
<!-- widget link -->
<a
v-if="widgetTemplate.setup.cis4link"
:href="getWidgetC4Link(widgetTemplate)"
class="col-auto ms-auto"
:aria-label="$p.t('dashboard/widget_link')"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_link') }"
>
<i class="fa fa-arrow-up-right-from-square me-1" aria-hidden="true"></i>
</a>
<!-- config button -->
<a
v-if="hasConfig"
href="#"
class="col-auto px-1"
:aria-label="$p.t('dashboard/widget_configure')"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_configure') }"
@click.prevent="openConfig"
>
<i class="fa-solid fa-gear" aria-hidden="true"></i>
</a>
<!-- delete button -->
<a
v-if="custom && editMode"
href="#"
class="col-auto px-1"
:aria-label="$p.t('dashboard/widget_delete')"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_delete') }"
@click.prevent="$emit('remove')"
>
<i class="fa-solid fa-trash" aria-hidden="true"></i>
</a>
<!-- hide button -->
<Transition>
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
<input
type="checkbox"
role="switch"
v-model="visible"
class="form-check-input ms-0"
:value="true"
:aria-label="$p.t('dashboard/widget_toggle_visibility')"
>
</div>
</Transition>
</header>
<div v-if="ready" class="card-body overflow-hidden p-0">
<component
:is="component"
v-model:shared-data="sharedData"
:config="arguments"
:width="width"
:height="height"
@setConfig="setConfig"
@change="changeConfigManually"
></component>
</div>
</height-transition>
</div>
</div>`,
<div
v-else
class="card-body overflow-hidden text-center d-flex flex-column justify-content-center"
>
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
<bs-modal
v-if="hasConfig"
ref="config"
@hideBsModal="handleHideBsModal"
@showBsModal="handleShowBsModal"
>
<template v-slot:title>
{{ widgetTemplate ? $p.t('dashboard/widget_config_title', widgetTemplate.setup) : '' }}
</template>
<template v-slot:default>
<component
:is="component"
v-if="ready && !isLoading"
v-model:shared-data="sharedData"
:config="tmpConfig"
@change="changeConfig"
:configMode="true"
></component>
<div v-else class="text-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
</template>
<template v-if="!widgetTemplate?.setup?.hideFooter" v-slot:footer>
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>{{ $p.t('ui/schliessen') }}</button>
<button
type="button"
class="btn btn-primary"
@click="changeConfig"
>{{ $p.t('ui/speichern') }}</button>
</template>
</bs-modal>
<height-transition>
<footer
v-if="editMode && isResizeable && !isPinned"
class="card-footer d-flex justify-content-end p-0"
>
<span
type="button"
drag-action="resize"
class="col-auto px-1"
:class="resizeClasses.button"
draggable="true"
aria-hidden="true"
:aria-label="$p.t('dashboard/widget_resize')"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/widget_resize') }"
>
<i
class="fa-solid"
:class="resizeClasses.icon"
aria-hidden="true"
></i>
</span>
</footer>
</height-transition>
</template>
</article>`,
};
+136 -123
View File
@@ -1,9 +1,12 @@
import BsConfirm from "../Bootstrap/Confirm.js";
import DropGrid from '../Drop/Grid.js'
import DashboardItem from "./Item.js";
import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
import WidgetIcon from "./Widget/WidgetIcon.js"
import dragClick from '../../directives/dragClick.js';
import ObjectUtils from "../../helpers/ObjectUtils.js";
export default {
name: 'Section',
components: {
@@ -11,8 +14,11 @@ export default {
DashboardItem,
WidgetIcon,
},
directives: {
dragClick
},
inject: {
widgetsSetup:{
widgetsSetup: {
type: Array,
default: [],
},
@@ -39,9 +45,8 @@ export default {
configOpened: false,
gridWidth: 1,
gridHeight: null,
draggedItem:null,
additionalRow:false,
}
additionalRow: false
};
},
provide() {
return {
@@ -49,22 +54,40 @@ export default {
this.editModeIsActive
),
sectionName: Vue.computed(() => this.name),
}
};
},
computed: {
computedWidgetsSetup(){
if(!this.widgetsSetup) return {};
return this.widgetsSetup.reduce((acc, setup)=>{
acc[setup.widget_id] = setup.setup;
sectionNameTranslation() {
switch (this.name) {
case "general":
return this.$p.t('dashboard', this.name);
case "custom":
return this.$p.t('dashboard', this.name);
default:
return this.name;
}
},
showSectionInformation() {
switch (this.name) {
case "general":
return this.$p.t('dashboard', 'dashboardGeneralSectionDescription');
case "custom":
return this.$p.t('dashboard', 'dashboardCustomSectionDescription');
default:
return this.$p.t('dashboard', 'dashboardSectionDescription', [this.name]);
}
},
indexedWidgetsTemplates() {
if (!this.widgetsSetup)
return {};
return this.widgetsSetup.reduce((acc, setup) => {
acc[setup.widget_id] = setup;
return acc;
},{})
}, {});
},
editModeIsActive() {
return (this.editMode || this.adminMode) && !this.configOpened
},
getSectionStyle() {
return 'margin-bottom: 8px;';
},
items() {
// reuses the nearest placement of the widget from another viewport
/* const computeNearestPlace = (item, gridWidth) =>{
@@ -85,76 +108,55 @@ export default {
if(!item?.widgetid && item?.id){
item.widgetid = item.id;
}
return { ...item, reorder: false, ...(item.place[this.gridWidth] || { reorder: true, ...{ x: 0, y: 0, w: 1, h: 1 } })};
let weight = 5;
if (!item.source)
weight = 6;
else if (item.source == 'general')
weight = 4;
let placement = item.place[this.gridWidth];
if (!placement) {
weight -= 3;
placement = {};
}
return { ...item, ...placement, weight };
});
return placedItems;
},
if (this.editModeIsActive)
return placedItems;
return placedItems.filter(item => !item.hidden);
}
},
watch: {
items() {
this.additionalRow = false;
}
},
methods: {
sectionNameTranslation(){
switch(this.name){
case "general":
return this.$p.t('dashboard',this.name);
break;
case "custom":
return this.$p.t('dashboard',this.name);
break;
default:
return this.name;
break;
}
},
showSectionInformation(){
if (this.name == "general"){
return this.$p.t('dashboard', 'dashboardGeneralSectionDescription');
}
else if(this.name == "custom"){
return this.$p.t('dashboard', 'dashboardCustomSectionDescription');
}
else{
return this.$p.t('dashboard', 'dashboardSectionDescription', [this.name]);
}
},
handleConfigOpened() {
this.configOpened = true
},
handleConfigClosed() {
this.configOpened = false
},
checkResizeLimit(item, w, h) {
// NOTE(chris): widgets needs to be loaded for this to work
let widget = this.widgetState[item.widget];
if (widget) {
let minmaxW = { ...widget.setup.width };
if (minmaxW.max)
minmaxW.min = minmaxW.min || 1;
else
minmaxW = { min: minmaxW, max: minmaxW };
if (w < minmaxW.min)
w = minmaxW.min;
if (w > minmaxW.max)
w = minmaxW.max;
let minmaxH = { ...widget.setup.height };
if (minmaxH.max)
minmaxH.min = minmaxH.min || 1;
else
minmaxH = { min: minmaxH, max: minmaxH };
if (h < minmaxH.min)
h = minmaxH.min;
if (h > minmaxH.max)
h = minmaxH.max;
}
return [w, h];
},
removeWidget(item, revert) {
if (item.custom) {
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', this.name, item.id));
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', item.id, this.name));
} else {
let update = {};
update[item.id] = { hidden: !revert };
if (!revert) {
// NOTE(chris): move to last line
update[item.id].place = [];
let y = this.gridHeight;
if (this.additionalRow)
y--;
update[item.id].place[this.gridWidth] = { x: 0, y };
}
this.updatePreset(update);
}
},
@@ -163,49 +165,42 @@ export default {
payload[item.id] = { config };
this.updatePreset(payload);
},
updatePositions(updated, pinned=false) {
updatePositions(updated) {
let result = {};
updated.forEach(update => {
let item = {...update.item};
if (!item.placeholder) {
if (!item.place[this.gridWidth])
item.place[this.gridWidth] = {x: 0, y: 0, w: 1, h: 1};
delete item.x;
delete item.y;
delete item.w;
delete item.h;
delete item.place[this.gridWidth].pinned;
if (update.x !== undefined)
item.place[this.gridWidth].x = update.x;
if (update.y !== undefined)
item.place[this.gridWidth].y = update.y;
if (update.w !== undefined)
item.place[this.gridWidth].w = update.w;
if (update.h !== undefined)
item.place[this.gridWidth].h = update.h;
if (pinned){
item.place[this.gridWidth].pinned = true;
}
let item = structuredClone(ObjectUtils.deepToRaw(update.item));
result[item.id] = item;
if (!item.placeholder) {
if (!item.place[this.gridWidth])
item.place[this.gridWidth] = { x: 0, y: 0, w: 1, h: 1 };
delete item.x;
delete item.y;
delete item.w;
delete item.h;
delete item.pinned;
delete item.weight;
if (update.x !== undefined)
item.place[this.gridWidth].x = update.x;
if (update.y !== undefined)
item.place[this.gridWidth].y = update.y;
if (update.w !== undefined)
item.place[this.gridWidth].w = update.w;
if (update.h !== undefined)
item.place[this.gridWidth].h = update.h;
if (update.pinned !== undefined)
item.place[this.gridWidth].pinned = update.pinned;
result[item.id] = item;
}
});
this.updatePreset(result);
},
updatePreset(update) {
let payload = {};
payload[this.name] = update;
this.$emit('widgetUpdate', this.name, payload);
this.$emit('widgetUpdate', update, this.name);
}
},
setup() {
const { state: widgetState } = useCachedWidgetLoader();
return {
widgetState
};
},
mounted() {
let self = this;
let cont = self.$refs.container;
@@ -215,44 +210,62 @@ export default {
self.gridWidth = parseInt(window.getComputedStyle(cont).getPropertyValue('--fhc-dashboard-grid-size'));
});
},
template: `
<div class="dashboard-section position-relative pb-3 border-bottom" ref="container" :style="getSectionStyle">
<h4 v-if="editModeIsActive" class=" mb-2">
<i v-tooltip="showSectionInformation(name)" class="fa-solid fa-circle-info section-info" ></i>
{{sectionNameTranslation()}}:
</h4>
<button v-tooltip="$p.t('dashboard','addLine')" v-if="!additionalRow && editModeIsActive" @click="additionalRow=true" class="btn btn-outline-secondary rounded-circle newGridRow d-flex justify-content-center align-items-center">+</button>
<drop-grid v-model:cols="gridWidth" v-model:additionalRow="additionalRow" :items="items" :itemsSetup="computedWidgetsSetup" :active="editModeIsActive" :resize-limit="checkResizeLimit" :margin-for-extra-row=".01" @draggedItem="draggedItem=$event" @rearrange-items="updatePositions" @gridHeight="gridHeight=$event" >
template: /* html */`
<section
class="dashboard-section position-relative pb-3 mb-3 border-bottom"
ref="container"
:class="{ 'edit-active': editModeIsActive }"
>
<h3 v-if="adminMode" class="h4">
<i v-tooltip="showSectionInformation" class="fa-solid fa-circle-info section-info"></i>
{{ sectionNameTranslation }}:
</h3>
<button
v-tooltip="$p.t('dashboard/addLine')"
v-if="!additionalRow && editModeIsActive"
class="btn btn-outline-secondary rounded-circle newGridRow d-flex justify-content-center align-items-center"
@click="additionalRow=true"
v-drag-click="() => additionalRow=true"
>+</button>
<drop-grid
v-model:cols="gridWidth"
:additional-row="additionalRow"
:items="items"
:items-setup="indexedWidgetsTemplates"
:active="editModeIsActive"
@rearrange-items="updatePositions"
@grid-height="gridHeight=$event"
>
<template #default="item">
<div v-if="item.placeholder" class="empty-tile-hover" @pointerdown="$emit('widgetAdd', name, { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 })"></div>
<div
v-if="item.placeholder"
class="empty-tile-hover"
@click="$emit('widgetAdd', { widget: 1, config: {}, place: {[gridWidth]: {x:item.x,y:item.y,w:1,h:1}}, custom: 1 }, name)"
></div>
<dashboard-item
v-else
:id="item.widget"
:dragstate="item.blank || (item.widgetid && item.widgetid == draggedItem?.data.widgetid)"
:resizeOverlay="item.resizeOverlay"
:widgetID="item.id"
:width="item.w"
:height="item.h"
:item_data="{config:item.config, custom:item.custom, h:item.h, w:item.w,id:item.id,reorder:item.reorder,place:item.place,widget:item.widget,widgetid:item.widgetid,x:item.x,y:item.y}"
:item_data="{config:item.config, custom:item.custom, h:item.h, w:item.w,id:item.id,place:item.place,widget:item.widget,widgetid:item.widgetid,x:item.x,y:item.y}"
:loading="item.loading"
:config="item.config"
:custom="item.custom"
:hidden="item.hidden"
:editMode="editModeIsActive"
:place="item.place[gridWidth]"
:setup="computedWidgetsSetup[item.widget]"
:widget-template="indexedWidgetsTemplates[item.widget]"
:source="adminMode ? null : item.source || 'custom'"
@change="saveConfig($event, item)"
@remove="removeWidget(item, $event)"
@config-opened="handleConfigOpened"
@config-closed="handleConfigClosed"
@pinItem="updatePositions($event,true)"
@unPinItem="updatePositions">
</dashboard-item>
@pin-item="updatePositions"
@un-pin-item="updatePositions"
></dashboard-item>
</template>
</drop-grid>
</div>`
</section>`
}
/*
@@ -32,19 +32,31 @@ export default {
},
},
template: `<div class="dashboard-widget-picker">
<bs-modal ref="modal" class="fade" :dialog-class="{'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0}" @hiddenBsModal="close">
<template v-slot:title>Create new widget</template>
template: /* html */`
<div class="dashboard-widget-picker">
<bs-modal
ref="modal"
class="fade"
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0 }"
@hiddenBsModal="close"
>
<template v-slot:title>{{ $p.t('dashboard/createWidget') }}</template>
<template v-slot:default>
<div v-if="widgets" class="row g-2">
<div v-if="!widgets.length">
No Widgets available
{{ $p.t('dashboard/noWidgetsAvailable') }}
</div>
<div v-for="widget in widgets" :key="widget.widget_id" class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2">
<widget-icon @select="pick" :widget="widget" ></widget-icon>
<div
v-for="widget in widgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
>
<widget-icon @select="pick" :widget="widget"></widget-icon>
</div>
</div>
<div v-else class="text-center"><i class="fa-solid fa-spinner fa-pulse fa-3x"></i></div>
<div v-else class="text-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
</template>
</bs-modal>
</div>`
@@ -11,9 +11,6 @@ export default {
mixins: [
AbstractWidget
],
inject: [
"timezone"
],
methods: {
getPromiseFunc(start, end) {
return [
@@ -27,6 +24,6 @@ export default {
},
template: /*html*/`
<div class="dashboard-widget-lvplan d-flex flex-column h-100">
<fhc-calendar :timezone="timezone" :get-promise-func="getPromiseFunc" />
<fhc-calendar :get-promise-func="getPromiseFunc" />
</div>`
}
File diff suppressed because it is too large Load Diff
+9 -71
View File
@@ -1,88 +1,26 @@
export default {
name:'GridItem',
components: {
},
inject: {
},
props: {
item: Object,
active: Boolean
item: Object
},
emits: [
"mouseDown",
"mouseUp",
"startMove",
"startResize",
"dragging",
"endDrag",
"dropDrag",
"item",
"touchStart",
"touchEnd",
"startResize"
],
data() {
return {
dragAction: '',
dragging: false
}
},
computed: {
},
methods: {
registerDragAction(evt) {
this.$emit('mouseDown', evt);
if (evt.target.hasAttribute('drag-action')) {
this.dragAction = evt.target.getAttribute('drag-action');
} else {
let parent = evt.target.closest('[drag-action]');
if (parent) {
this.dragAction = parent.getAttribute('drag-action');
} else {
this.dragAction = '';
}
}
},
tryDragStart(evt, item) {
let dragAction = this.dragAction || evt.target.getAttribute('drag-action');
tryDragStart(evt) {
let dragAction = evt.target.getAttribute('drag-action');
if (dragAction) {
this.dragging = true;
if (dragAction == 'move')
return this.$emit('startMove', evt, item);
return this.$emit('startMove', evt, this.item);
else if (dragAction == 'resize')
return this.$emit('startResize', evt, item);
}
//evt.preventDefault();
},
touchDragEnd(evt) {
if (!this.dragging)
return;
this.dragging = false;
this.$emit('touchEnd', evt);
},
touchStart(event){
this.$emit('touchStart', event);
this.registerDragAction(event);
this.tryDragStart(event, this.item);
},
touchMove(event){
if(this.dragging){
event.preventDefault();
this.$emit('dragging', event);
return this.$emit('startResize', evt, this.item);
}
}
},
template: `
<div class="drop-grid-item"
@mousedown="registerDragAction"
@mouseup="$emit('mouseUp', $event)"
@touchstart="touchStart"
@touchend="touchDragEnd"
@dragstart="tryDragStart($event, item)"
@drag="$emit('dragging',$event)"
@touchmove="touchMove"
@dragend="$emit('endDrag', $event); dragging = false"
:draggable="active && !item.placeholder">
template: /* html */`
<li class="drop-grid-item" @dragstart="tryDragStart">
<slot v-bind="item"></slot>
</div>`
</li>`
}
@@ -133,6 +133,7 @@ export default {
return this.$api
.call(ApiMessages.getDataVorlage(vorlage_kurzbz))
.then(response => {
this.editor.setContent(response.data.text);
this.formData.body = response.data.text;
this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError);
@@ -203,24 +204,6 @@ export default {
},
},
watch: {
'formData.body': {
handler(newVal) {
const tinymcsVal = this.editor.getContent();
if (newVal && tinymcsVal != newVal) {
//Inhalt des Editors aktualisieren
this.editor.setContent(newVal);
}
}
},
'formData.vorlage_kurzbz': {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getDataVorlage(newVal);
}
}
},
messageId: {
immediate: true,
handler: async function (newMessageId) {
@@ -231,6 +214,7 @@ export default {
this.replyData = result.data;
if (this.replyData.length > 0) {
this.editor.setContent(this.replyData[0].replyBody);
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = newMessageId;
@@ -290,19 +274,6 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
//case of reply
if(this.messageId) {
this.$api
.call(ApiMessages.getReplyData(this.messageId))
.then(result => {
this.replyData = result.data;
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = this.messageId;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
async mounted() {
this.initTinyMCE();
@@ -64,7 +64,16 @@ export default {
target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
//height: 800,
//plugins: ['lists'],
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify',
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | link',
plugins: 'link',
link_context_toolbar: true,
automatic_uploads: true,
default_link_target: "_blank",
link_title: true,
target_list: [
{ title: 'New tab', value: '_blank' },
{ title: 'Same tab', value: '_self' }
],
style_formats: [
{title: 'Blocks', block: 'div'},
{title: 'Paragraph', block: 'p'},
@@ -98,7 +107,8 @@ export default {
return this.$api
.call(ApiMessages.sendMessage(this.typeId, data))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
if(this.openMode == "inSamePage")
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
this.hideTemplate();
this.resetForm();
this.messageSent = true;
@@ -114,19 +124,17 @@ export default {
return this.$api
.call(ApiMessages.getDataVorlage(vorlage_kurzbz))
.then(response => {
this.editor.setContent(response.data.text);
this.formData.body = response.data.text;
this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError);
},
getPreviewText(){
console.log("subj" + this.formData.subject);
const data = new FormData();
data.append('data', JSON.stringify(this.formData.body));
data.append('ids', JSON.stringify(this.id));
console.log("subj" + this.formData.subject);
return this.$api
.call(ApiMessages.getPreviewText(
this.typeId, data))
@@ -195,6 +203,7 @@ export default {
.call(ApiMessages.getReplyData(messageId))
.then(result => {
this.replyData = result.data;
this.editor.setContent(this.replyData[0].replyBody);
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = messageId;
@@ -202,27 +211,6 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
}
},
watch: {
'formData.body': {
handler(newVal) {
const tinymcsVal = this.editor.getContent();
if (newVal && tinymcsVal != newVal) {
//Inhalt des Editors aktualisieren
this.editor.setContent(newVal);
}
}
},
'formData.vorlage_kurzbz': {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getDataVorlage(newVal);
}
}
},
},
created(){
const missingparamsmsgs = [];
if(!this.typeId)
@@ -291,17 +279,8 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
//case of reply
if(this.messageId != null) {
if(this.messageId) {
this.loadReplyData(this.messageId);
/* this.$api
.call(ApiMessages.getReplyData(this.messageId))
.then(result => {
this.replyData = result.data;
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = this.messageId;
})
.catch(this.$fhcAlert.handleSystemError);*/
}
},
@@ -499,10 +478,10 @@ export default {
<div class="row">
<div class="col-6" style="border-right: 1px">
You can safely close this window.
You can safely close this window/tab.
</div>
<div class="col-6">
Sie können dieses Fenster schließen.
Fenster/Reiter kann geschlossen werden!
</div>
</div>
</div>
@@ -65,7 +65,14 @@ export default {
buildTreemap(messages) {
if (!messages || !messages.data || messages.data.length === 0)
{
return {data: [], last_page: 0};
if(this.tabulatorOptions.pagination)
{
return {data: [], last_page: 0};
}
else
{
return [];
}
}
const last_page = messages.meta.count;
@@ -106,7 +113,15 @@ export default {
// to avoid endless loop
if (iteration > messages.length) break;
}
return {data: messageNested, last_page: last_page};
if(this.tabulatorOptions.pagination)
{
return {data: messageNested, last_page: last_page};
}
else
{
return messageNested;
}
},
loadAjaxCall(url, config, params){
return this.$api.call(
@@ -252,7 +267,7 @@ export default {
frozen: true
}
],
pagination: true,
pagination: false,
paginationMode: "remote",
paginationSize: 15,
paginationInitialPage: 1,
+8 -6
View File
@@ -82,14 +82,16 @@ export default {
this.$refs.modalMsg.show();
}
else if (this.openMode == "inSamePage"){
console.log("in same Page");
this.isVisibleDiv = true;
if(messageId)
this.$refs.templateNewDivMessage.loadReplyData(messageId);
else
this.$refs.templateNewDivMessage.resetForm();
this.$refs.templateNewDivMessage.showTemplate();
this.$nextTick(() => {
if(messageId)
this.$refs.templateNewDivMessage.loadReplyData(messageId);
else
this.$refs.templateNewDivMessage.resetForm();
this.$refs.templateNewDivMessage.showTemplate();
});
}
else
console.log("no valid openMode");
@@ -1,36 +0,0 @@
import ApiWidget from "../../api/factory/dashboard/widget.js";
const promises = Vue.ref([]);
const stateRef = Vue.ref([]);
const state = Vue.readonly(stateRef);
export function useCachedWidgetLoader() {
const $api = Vue.inject('$api');
const $fhcAlert = Vue.inject('$fhcAlert');
function load(id) {
if (state.value[id])
return Promise.resolve(state.value[id]);
if (!promises.value[id])
promises.value[id] = new Promise((resolve, reject) => {
$api
.call(ApiWidget.get(id))
.then(res => {
stateRef.value[id] = res.data;
promises.value[id] = undefined;
resolve(state.value[id]);
})
.catch($fhcAlert.handleSystemError);
});
return promises.value[id];
}
return {
state,
actions: {
load
}
};
}
+80 -52
View File
@@ -1,4 +1,10 @@
// TODO(chris): Comments
/**
* This class arranges rectangular items on a grid with a defined width and
* a potential infinite height. It calculates repositioning of already placed
* items if a new item would overlap one or more of said placed items.
* This can be manipulated by adding weights to the items or by defining an
* item as pinned.
*/
const DIR_UP = 0;
const DIR_LEFT = 1;
@@ -23,33 +29,23 @@ class GridLogic {
const i = y*this.w + x;
return !this.grid[i] && this.grid[i] !== 0;
}
getMaxY(){
return this.data.reduce((acc, item) => {
if (item?.y > acc) {
acc = item.y;
}
return acc;
}, 0);
}
getFreeSlots() {
const freeSlots = [];
let biggestY = this.getMaxY();
let totalSpaces = this.w * (biggestY+1);
for(let i=0; i < totalSpaces; i++){
if (!this.grid[i] && this.grid[i] !== 0){
this.grid[i] = undefined;
}
}
for(let i =0; i < this.grid.length; i++){
if (!this.grid[i] && this.grid[i] !== 0){
let i = this.w * this.h;
while (i--) {
if (!this.grid[i] && this.grid[i] !== 0) {
let x = i % this.w;
let y = Math.floor(i / this.w);
freeSlots.push({x, y});
}
}
return freeSlots;
}
add(item, prefer) {
if (!item.frame)
item.frame = this.getItemFrame(item);
let occupiers = this.getItemsInFrame(item.frame);
if (!occupiers.length) {
item.frame.forEach(f => this.grid[f] = item.index);
@@ -61,6 +57,14 @@ class GridLogic {
item.frame.forEach(f => intermGrid.grid[f] = -1);
intermGrid.data.forEach(currItem => {
if (currItem.pinned) {
if (!currItem.frame)
currItem.frame = intermGrid.getItemFrame(currItem);
currItem.frame.forEach(f => intermGrid.grid[f] = -1);
}
});
const possiblities = intermGrid.tryMoving(occupiers, prefer);
if (possiblities.length) {
const bestOption = possiblities.sort((a,b) => {
@@ -83,7 +87,9 @@ class GridLogic {
result[move.index] = {
index: currItem.index,
x: currItem.x,
y: currItem.y
y: currItem.y,
w: currItem.w,
h: currItem.h
};
});
item.frame.forEach(f => this.grid[f] = item.index);
@@ -91,12 +97,12 @@ class GridLogic {
return result;
} else {
console.error('FATAL', "can't arrange item on grid");
return null;
}
}
}
move(item, x, y) {
if (item.data.place[this.w]?.pinned)
if (item.pinned)
return [];
if (item.x == x && item.y == y)
return [];
@@ -116,8 +122,6 @@ class GridLogic {
prefer = DIR_RIGHT;
}
const originalFrame = Array.isArray(item.frame) ? [...item.frame] : [item.frame];
const currItem = {...item};
currItem.x = x;
currItem.y = y;
@@ -125,33 +129,60 @@ class GridLogic {
let occupiers = this.getItemsInFrame(currItem.frame);
// does not update if the target conatins pinned widgets
if (occupiers.some(frame => this.data[frame]?.data.place[this.w]?.pinned)) {
if (occupiers.some(frame => this.data[frame]?.pinned)) {
return [];
}
// checks if target contains widget with the same high and width
let occupiersData = occupiers.map(occupier => this.data[occupier]);
let occupiersFrame = occupiersData.map(occupier => occupier.frame).flat();
if (!occupiersFrame.some(frame => !currItem.frame.includes(frame)) && !occupiersFrame.some(frame => originalFrame.includes(frame))){
let replaceUpdate = [];
let newOccupierFrames = [];
for(let f of originalFrame){
if(newOccupierFrames.includes(f)){
continue;
}
let occ = occupiersData.shift();
if(occ){
newOccupierFrames = [...newOccupierFrames, ...this.getItemFrame({ ...occ, ...this.getSingleFramePosition(f) })];
replaceUpdate[occ.index] = { index: occ.index, ...this.getSingleFramePosition(f)}
}
}
replaceUpdate[item.index] = { index: item.index, x, y };
// checks if target contains moving widgets start position
// so swapping should be avoided
const targetAndItemOverlap = this.getItemFrame(item).some(frame => currItem.frame.includes(frame))
if (!targetAndItemOverlap) {
// checks if target contains widget with the same high and width
// so swapping is possible
const occupiersFrame = occupiers.map(occupier => this.data[occupier].frame).flat();
const occupiersInsideMovingItem = occupiersFrame.every(frame => currItem.frame.includes(frame));
if (occupiersInsideMovingItem) {
// every slot of all items in the target zone is inside said zone
const replaceUpdate = [];
return replaceUpdate;
const diffX = item.x - x;
const diffY = item.y - y;
occupiers.forEach(occupier => {
const data = { ...this.data[occupier] };
data.x += diffX;
data.y += diffY;
data.frame = this.getItemFrame(data);
this.remove(data);
this.add(data);
replaceUpdate[occupier] = {
index: data.index,
x: data.x,
y: data.y,
w: data.w,
h: data.h
};
});
this.add({ ...item, x, y });
replaceUpdate[item.index] = {
index: item.index,
x,
y,
w: item.w,
h: item.h
};
return replaceUpdate;
}
}
const updates = this.add(currItem, prefer);
updates[item.index] = {index: item.index, x, y};
if (updates)
updates[item.index] = { index: item.index, x, y, w: item.w, h: item.h };
return updates;
}
resize(item, w, h) {
@@ -166,7 +197,7 @@ class GridLogic {
const updates = this.add(currItem);
if(updates)
updates[item.index] = {index: item.index, w, h, x:item.x, y:item.y, resize:true};
updates[item.index] = { index: item.index, w, h, x: item.x, y: item.y };
return updates;
}
@@ -205,13 +236,13 @@ class GridLogic {
let targetframe;
switch(dir) {
case DIR_UP:
if (this.data[index].data?.place[this.w]?.pinned || this.data[index].y - amount < 0)
if (this.data[index].pinned || this.data[index].y - amount < 0)
return false;
targetframe = this.data[index].frame.map(i => i-this.w*amount);
move.y = -amount;
break;
case DIR_DOWN:
if (this.data[index].data?.place[this.w]?.pinned)
if (this.data[index].pinned)
return false;
if (this.data[index].y + this.data[index].h + amount > this.h)
cost += .4;
@@ -219,13 +250,13 @@ class GridLogic {
move.y = amount;
break;
case DIR_LEFT:
if (this.data[index].data?.place[this.w]?.pinned || this.data[index].x - amount < 0)
if (this.data[index].pinned || this.data[index].x - amount < 0)
return false;
targetframe = this.data[index].frame.map(i => i-amount);
move.x = -amount;
break;
case DIR_RIGHT:
if (this.data[index].data?.place[this.w]?.pinned || this.data[index].x + this.data[index].w + amount > this.w)
if (this.data[index].pinned || this.data[index].x + this.data[index].w + amount > this.w)
return false;
targetframe = this.data[index].frame.map(i => i+amount);
move.x = amount;
@@ -262,9 +293,6 @@ class GridLogic {
frame.push(i + item.x + (j + item.y) * this.w);
return frame;
}
getSingleFramePosition(frame){
return { x: frame % this.w, y: Math.floor(frame / this.w)};
}
debug() {
return this.grid;
}
+50
View File
@@ -0,0 +1,50 @@
import ApiRenderers from '../api/factory/renderers.js';
/**
* @return object { renderers: Object }
*/
export function useRenderers() {
/* Result Vars */
const renderers = Vue.ref(null);
/* Helper Vars */
const $api = Vue.inject('$api');
const $fhcAlert = Vue.inject('$fhcAlert');
/* Main Logic */
$api
.call(ApiRenderers.loadRenderers())
.then(res => {
const head = document.head;
for (const rendertype of Object.keys(res.data)) {
const renderersForType = {};
for (const name of Object.keys(res.data[rendertype])) {
const rendererUrl = res.data[rendertype][name];
if (rendererUrl.substr(-4) == ".css") {
// add to head
if (!head.querySelector(`link[href="${rendererUrl}"]`)) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = rendererUrl;
head.appendChild(link);
}
} else {
renderersForType[name] = Vue.markRaw(
Vue.defineAsyncComponent(() => import(rendererUrl))
);
}
}
if (Object.keys(renderersForType).length) {
if (renderers.value === null)
renderers.value = {};
renderers.value[rendertype] = renderersForType;
}
}
})
.catch($fhcAlert.handleSystemErrors);
return {
renderers
};
}
+7
View File
@@ -1,5 +1,12 @@
import { bindDragEnterLeave } from '../helpers/DragAndDrop.js';
import { enableDragDropTouch } from "../../../vendor/drag-drop-touch-js/dragdroptouch/dist/drag-drop-touch.esm.min.js";
if (!document.dragDropTouchActive) {
enableDragDropTouch();
document.dragDropTouchActive = true;
}
export default {
mounted(el, binding) {
const delay = parseInt(binding.arg) || 300;
+7
View File
@@ -1,5 +1,12 @@
import { setTransferData, convertToValidDragObject } from '../helpers/DragAndDrop.js';
import { enableDragDropTouch } from "../../../vendor/drag-drop-touch-js/dragdroptouch/dist/drag-drop-touch.esm.min.js";
if (!document.dragDropTouchActive) {
enableDragDropTouch();
document.dragDropTouchActive = true;
}
const EFFECTS = [
'none',
'copy',
+7
View File
@@ -1,5 +1,12 @@
import { getValidTransferData, eventHasTypes, bindDragEnterLeave } from '../helpers/DragAndDrop.js';
import { enableDragDropTouch } from "../../../vendor/drag-drop-touch-js/dragdroptouch/dist/drag-drop-touch.esm.min.js";
if (!document.dragDropTouchActive) {
enableDragDropTouch();
document.dragDropTouchActive = true;
}
const EFFECTS = [
'move',
'copy',
+7
View File
@@ -93,6 +93,8 @@ require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php')
require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php');
require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php');
require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/70376_lohnguide.php');
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -240,6 +242,11 @@ $tabellen=array(
"hr.tbl_valorisierung_instanz" => array("updateamum", "oe_kurzbz", "valorisierungsdatum", "valorisierung_kurzbz", "beschreibung", "ausgewaehlt", "updatevon", "valorisierung_instanz_id"),
"hr.tbl_valorisierung_instanz_methode" => array("valorisierung_instanz_id", "valorisierung_methode_kurzbz", "beschreibung", "valorisierung_methode_parameter"),
"hr.tbl_valorisierung_methode" => array("beschreibung", "valorisierung_methode_kurzbz"),
"hr.tbl_lohnguide_jobfamilie" => array("jobfamilie_kurzbz", "bezeichnung", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_modellfunktion" => array("modellfunktion_kurzbz", "bezeichnung", "jobfamilie_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_modellstelle" => array("modellstelle_kurzbz", "bezeichnung", "grade", "modellfunktion_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_fachrichtung" => array("fachrichtung_kurzbz", "bezeichnung", "aktiv", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_vertragsbestandteil_lohnguide" => array("vertragsbestandteil_id", "stellenbezeichnung", "vordienstzeit", "fachrichtung_kurzbz", "modellstelle_kurzbz", "kommentar_person", "kommentar_modellstelle"),
"lehre.tbl_abschlussbeurteilung" => array("abschlussbeurteilung_kurzbz","bezeichnung","bezeichnung_english","sort"),
"lehre.tbl_abschlusspruefung" => array("abschlusspruefung_id","student_uid","vorsitz","pruefer1","pruefer2","pruefer3","abschlussbeurteilung_kurzbz","akadgrad_id","pruefungstyp_kurzbz","datum","uhrzeit","sponsion","anmerkung","updateamum","updatevon","insertamum","insertvon","ext_id","note","protokoll","endezeit","pruefungsantritt_kurzbz","freigabedatum"),
"lehre.tbl_abschlusspruefung_antritt" => array("pruefungsantritt_kurzbz","bezeichnung","bezeichnung_english","sort"),
+14 -1
View File
@@ -1,6 +1,19 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if ($result = @$db->db_query("SELECT 1 FROM system.tbl_app WHERE app='lvevaluierung' LIMIT 1"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_app (app) VALUES ('lvevaluierung');";
if(!$db->db_query($qry))
echo '<strong>system.tbl_app: '.$db->db_last_error().'</strong><br>';
else
echo ' system.tbl_app: lvevaluierung hinzugefügt<br>';
}
}
//Add column evaluierung to lehre.tbl_lehrveranstaltung
if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"))
{
@@ -12,4 +25,4 @@ if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"
echo '<strong>lehre.tbl_lehrveranstaltung '.$db->db_last_error().'</strong><br>';
else
echo '<br>Spalte evaluierung zu Tabelle lehre.tbl_lehrveranstaltung hinzugefügt';
}
}
@@ -0,0 +1,91 @@
<?php
/* Copyright (C) 2026 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Authors: Christopher Hacker <christopher.hacker@technikum-wien.at>,
*
* Description:
* Cleanup Dashboard DB data
*/
if (! defined('DB_NAME')) exit('No direct script access allowed');
// Cleanup presets
if ($result = @$db->db_query("
SELECT 1
FROM dashboard.tbl_dashboard_preset
WHERE preset ? COALESCE(funktion_kurzbz, 'general')
OR preset ? 'custom'
LIMIT 1
")) {
if ($db->db_num_rows($result)) {
$qry = "
UPDATE dashboard.tbl_dashboard_preset
SET preset = COALESCE(preset->COALESCE(funktion_kurzbz, 'general'), preset->'custom')->'widgets'
WHERE preset ? COALESCE(funktion_kurzbz, 'general')
OR preset ? 'custom'
";
$result = $db->db_query($qry);
if (!$result) {
echo '<strong>dashboard.tbl_dashboard_preset '.$db->db_last_error().'</strong><br>';
} else {
$affected_rows = $db->db_affected_rows($result);
echo 'dashboard.tbl_dashboard_preset: ' . $affected_rows . ' rows migrated<br>';
}
}
}
// Cleanup user overrides
if ($result = @$db->db_query("
SELECT 1
FROM dashboard.tbl_dashboard_benutzer_override
WHERE EXISTS (
SELECT 1
FROM jsonb_each(override)
WHERE value ? 'widgets'
LIMIT 1
) AND override <> '[]'::jsonb
LIMIT 1
")) {
if ($db->db_num_rows($result)) {
$qry = "
UPDATE dashboard.tbl_dashboard_benutzer_override
SET override = COALESCE((
SELECT json_object_agg(key, value) FROM (
SELECT value->'widgets' AS widgets
FROM jsonb_each(override)
WHERE jsonb_typeof(value->'widgets') = 'object'
) x, jsonb_each(widgets)
), '[]')
WHERE EXISTS (
SELECT 1
FROM jsonb_each(override)
WHERE value ? 'widgets'
LIMIT 1
) AND override <> '[]'::jsonb
";
$result = $db->db_query($qry);
if (!$result) {
echo '<strong>dashboard.tbl_dashboard_benutzer_override '.$db->db_last_error().'</strong><br>';
} else {
$affected_rows = $db->db_affected_rows($result);
echo 'dashboard.tbl_dashboard_benutzer_override: ' . $affected_rows . ' rows migrated<br>';
}
}
}
+407
View File
@@ -0,0 +1,407 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_jobfamilie' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_jobfamilie (
jobfamilie_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(64) NOT NULL,
aktiv boolean DEFAULT FALSE,
sort smallint,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_jobfamilie_pkey PRIMARY KEY (jobfamilie_kurzbz)
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_jobfamilie TO vilesci;
INSERT INTO hr.tbl_lohnguide_jobfamilie(jobfamilie_kurzbz, bezeichnung,aktiv, sort, insertvon, insertamum) VALUES
('FÜHRUNG','Führung',true,1,'system',NOW()),
('ALLGEMEIN','Allgemein',true,2,'system',NOW()),
('TECHNIK','Technik',true,3,'system',NOW()),
('IT','IT',true,4,'system',NOW()),
('PRODUKTION','Produktion',true,5,'system',NOW()),
('HANDW_IH_LOG','Handwerk, Instandhaltung + Logistik',true,6,'system',NOW())
ON CONFLICT (jobfamilie_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Jobfamilie: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_jobfamilie wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_modellfunktion' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellfunktion (
modellfunktion_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(64) NOT NULL,
jobfamilie_kurzbz character varying(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
sort smallint,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_modellfunktion_pkey PRIMARY KEY (modellfunktion_kurzbz),
CONSTRAINT tbl_lohnguide_modellfunktion_jobfamilie_fk FOREIGN KEY (jobfamilie_kurzbz) REFERENCES hr.tbl_lohnguide_jobfamilie (jobfamilie_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_modellfunktion TO vilesci;
INSERT INTO hr.tbl_lohnguide_modellfunktion(modellfunktion_kurzbz, bezeichnung, jobfamilie_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('FÜHRUNG_I','Führung I','FÜHRUNG',true,1,'system',NOW()),
('FÜHRUNG_II','Führung I','FÜHRUNG',true,2,'system',NOW()),
('FÜHRUNG_III','Führung I','FÜHRUNG',true,3,'system',NOW()),
('GF','Geschäftsführung','FÜHRUNG',true,4,'system',NOW()),
/* Allgemein */
('FK_ALLGM','Fachkraft Allgemein','ALLGEMEIN',true,5,'system',NOW()),
('SFK_ALLGM','Spezial-Fachkraft Allgemein','ALLGEMEIN',true,6,'system',NOW()),
('SP_ALLGM','Spezialist:in Allgemein','ALLGEMEIN',true,7,'system',NOW()),
('EXP_ALLGM','Expert:in Allgemein','ALLGEMEIN',true,8,'system',NOW()),
('TOP_EXP_ALLGM','Top-Expert:in Allgemein','ALLGEMEIN',true,9,'system',NOW()),
/* Technik */
('FK_TECH','Fachkraft Technik','TECHNIK',true,10,'system',NOW()),
('SFK_TECH','Spezial-Fachkraft Technik','TECHNIK',true,11,'system',NOW()),
('SP_TECH','Spezialist:in Technik','TECHNIK',true,12,'system',NOW()),
('EXP_TECH','Expert:in Technik','TECHNIK',true,13,'system',NOW()),
('TOP_EXP_TECH','Top-Expert:in Technik','TECHNIK',true,14,'system',NOW()),
/* IT */
('FK_IT','Fachkraft IT','IT',true,15,'system',NOW()),
('SFK_IT','Spezial-Fachkraft IT','IT',true,16,'system',NOW()),
('SP_IT','Spezialist:in IT','IT',true,17,'system',NOW()),
('EXP_IT','Expert:in IT','IT',true,18,'system',NOW()),
('TOP_EXP_IT','Top-Expert:in IT','IT',true,19,'system',NOW()),
/* Produktion */
('HK_PROD','Hilfskraft Produktion','PRODUKTION',true,20,'system',NOW()),
('FK_PROD','Fachkraft Produktion','PRODUKTION',true,21,'system',NOW()),
('SFK_PROD','Spezial-Fachkraft Produktion','PRODUKTION',true,22,'system',NOW()),
('SP_PROD','Spezialist:in Produktion','PRODUKTION',true,23,'system',NOW()),
/* Handwerk, Instandhaltung, Logistik */
('HK_HIL','Hilfskraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,24,'system',NOW()),
('FK_HIL','Fachkraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,25,'system',NOW()),
('SFK_HIL','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,26,'system',NOW()),
('SP_HIL','Spezialist:in Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,27,'system',NOW())
ON CONFLICT (modellfunktion_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Modellfunktion: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_modellfunktion wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_modellstelle' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellstelle (
modellstelle_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(128) NOT NULL,
grade int NOT NULL,
modellfunktion_kurzbz character varying(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
sort smallint,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_modellstelle_pkey PRIMARY KEY (modellstelle_kurzbz),
CONSTRAINT tbl_lohnguide_modellstelle_modellfunktion_fk FOREIGN KEY (modellfunktion_kurzbz) REFERENCES hr.tbl_lohnguide_modellfunktion (modellfunktion_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_modellstelle TO vilesci;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('111','Führung III 1/5',9,'FÜHRUNG_III',true,13,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('112','Führung III 2/5',10,'FÜHRUNG_III',true,14,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('113','Führung III 3/5',11,'FÜHRUNG_III',true,15,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('114','Führung III 4/5',12,'FÜHRUNG_III',true,16,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('115','Führung III 5/5',13,'FÜHRUNG_III',true,17,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('121','Führung II 1/4',14,'FÜHRUNG_II',true,7,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('122a','Führung II 2a/4',15,'FÜHRUNG_II',true,8,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('122b','Führung II 2b/4',15,'FÜHRUNG_II',true,9,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('123a','Führung II 3a/4',16,'FÜHRUNG_II',true,10,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('123b','Führung II 3b/4',16,'FÜHRUNG_II',true,11,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('124','Führung II 4/4',17,'FÜHRUNG_II',true,12,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('131','Führung I 1/4',18,'FÜHRUNG_I',true,1,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('132a','Führung I 2a/4',19,'FÜHRUNG_I',true,2,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('132b','Führung I 2b/4',19,'FÜHRUNG_I',true,3,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('133a','Führung I 3a/4',20,'FÜHRUNG_I',true,4,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('133b','Führung I 3b/4',20,'FÜHRUNG_I',true,5,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('134','Führung I 4/4',21,'FÜHRUNG_I',true,6,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- GF
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('141','Geschäftsführung 1/5',22,'GF',true,18,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('142a','Geschäftsführung 2a/5',23,'GF',true,19,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('142b','Geschäftsführung 2b/5',23,'GF',true,20,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('143a','Geschäftsführung 3a/5',24,'GF',true,21,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('143b','Geschäftsführung 3b/5',24,'GF',true,22,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('144a','Geschäftsführung 4a/5',25,'GF',true,23,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('144b','Geschäftsführung 4b/5',25,'GF',true,24,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('145','Geschäftsführung 5/5',26,'GF',true,25,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Allgemein
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('211','Fachkraft Allgemein 1/3',4,'FK_ALLGM',true,26,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('212a','Fachkraft Allgemein 2a/3',5,'FK_ALLGM',true,27,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('212b','Fachkraft Allgemein 2b/3',5,'FK_ALLGM',true,28,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('213','Fachkraft Allgemein 3/3',6,'FK_ALLGM',true,29,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('221','Spezial-Fachkraft Allgemein 1/4', 7,'SFK_ALLGM',true,30,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('222a','Spezial-Fachkraft Allgemein 2a/4',8,'SFK_ALLGM',true,31,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('222b','Spezial-Fachkraft Allgemein 2b/4',8,'SFK_ALLGM',true,32,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('223a','Spezial-Fachkraft Allgemein 3a/4',9,'SFK_ALLGM',true,33,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('223b','Spezial-Fachkraft Allgemein 3b/4',9,'SFK_ALLGM',true,34,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('224','Spezial-Fachkraft Allgemein 4/4',10,'SFK_ALLGM',true,35,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('231','Spezialist:in Allgemein 1/4',11,'SP_ALLGM',true,36,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('232a','Spezialist:in Allgemein 2a/4',12,'SP_ALLGM',true,37,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('232b','Spezialist:in Allgemein 2b/4',12,'SP_ALLGM',true,38,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('233a','Spezialist:in Allgemein 3a/4',13,'SP_ALLGM',true,39,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('233b','Spezialist:in Allgemein 3b/4',13,'SP_ALLGM',true,40,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('234','Spezialist:in Allgemein 4/4',14,'SP_ALLGM',true,41,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('241','Expert:in Allgemein 1/4',15,'EXP_ALLGM',true,42,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('242a','Expert:in Allgemein 2a/4',16,'EXP_ALLGM',true,43,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('242b','Expert:in Allgemein 2b/4',16,'EXP_ALLGM',true,44,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('243a','Expert:in Allgemein 3a/4',17,'EXP_ALLGM',true,45,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('243b','Expert:in Allgemein 3b/4',17,'EXP_ALLGM',true,46,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('244','Expert:in Allgemein 4/4',18,'EXP_ALLGM',true,47,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('251','Top-Expert:in Allgemein 1/1',19,'TOP_EXP_ALLGM',true,48,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Technik
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('311','Fachkraft Technik 1/3',4,'FK_TECH',true,49,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('312a','Fachkraft Technik 2a/3',5,'FK_TECH',true,50,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('312b','Fachkraft Technik 2b/3',5,'FK_TECH',true,51,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('313','Fachkraft Technik 3/3',6,'FK_TECH',true,52,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('321','Spezial-Fachkraft Technik 1/4',7,'SFK_TECH',true,53,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('322a','Spezial-Fachkraft Technik 2a/4',8,'SFK_TECH',true,54,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('322b','Spezial-Fachkraft Technik 2b/4',8,'SFK_TECH',true,55,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('323a','Spezial-Fachkraft Technik 3a/4',9,'SFK_TECH',true,56,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('323b','Spezial-Fachkraft Technik 3b/4',9,'SFK_TECH',true,57,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('324','Spezial-Fachkraft Technik 4/4',10,'SFK_TECH',true,58,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('331','Spezialist:in Technik 1/4',11,'SP_TECH',true,59,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('332a','Spezialist:in Technik 2a/4',12,'SP_TECH',true,60,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('332b','Spezialist:in Technik 2b/4',12,'SP_TECH',true,61,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('333a','Spezialist:in Technik 3a/4',13,'SP_TECH',true,62,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('333b','Spezialist:in Technik 3b/4',13,'SP_TECH',true,63,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('334','Spezialist:in Technik 4/4',14,'SP_TECH',true,64,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('341','Expert:in Technik 1/4',15,'EXP_TECH',true,65,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('342a','Expert:in Technik 2a/4',16,'EXP_TECH',true,66,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('342b','Expert:in Technik 2b/4',16,'EXP_TECH',true,67,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('343a','Expert:in Technik 3a/4',17,'EXP_TECH',true,68,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('343b','Expert:in Technik 3b/4',17,'EXP_TECH',true,69,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('344','Expert:in Technik 4/4',18,'EXP_TECH',true,70,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('351','Top-Expert:in Technik 1/1',19,'TOP_EXP_TECH',true,71,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- IT
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('411','Fachkraft IT 1/2',5,'FK_IT',true,72,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('412','Fachkraft IT 2/2',6,'FK_IT',true,73,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('421','Spezial-Fachkraft IT 1/4',7,'SFK_IT',true,74,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('422','Spezial-Fachkraft IT 2/4',8,'SFK_IT',true,75,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('423','Spezial-Fachkraft IT 3/4',9,'SFK_IT',true,76,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('424','Spezial-Fachkraft IT 4/4',10,'SFK_IT',true,77,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('431','Spezialist:in IT 1/4',11,'SP_IT',true,78,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('432a','Spezialist:in IT 2a/4',12,'SP_IT',true,79,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('432b','Spezialist:in IT 2b/4',12,'SP_IT',true,80,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('433a','Spezialist:in IT 3a/4',13,'SP_IT',true,81,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('433b','Spezialist:in IT 3b/4',13,'SP_IT',true,82,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('434','Spezialist:in IT 4/4',14,'SP_IT',true,83,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('441','Expert:in IT 1/4',15,'EXP_IT',true,84,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('442','Expert:in IT 2/4',16,'EXP_IT',true,85,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('443','Expert:in IT 3/4',17,'EXP_IT',true,86,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('444','Expert:in IT 4/4',18,'EXP_IT',true,87,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('451','Top-Expert:in IT 1/1',19,'TOP_EXP_IT',true,88,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Produktion
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('511','Hilfskraft Produktion 1/4',1,'HK_PROD',true,89,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('512','Hilfskraft Produktion 2/4',2,'HK_PROD',true,90,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('513','Hilfskraft Produktion 3/4',3,'HK_PROD',true,91,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('514','Hilfskraft Produktion 4/4',4,'HK_PROD',true,92,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('521','Fachkraft Produktion 1/2',5,'FK_PROD',true,93,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('522','Fachkraft Produktion 2/2',6,'FK_PROD',true,94,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('531','Spezial-Fachkraft Produktion 1/4',7,'SFK_PROD',true,95,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('532','Spezial-Fachkraft Produktion 2/4',8,'SFK_PROD',true,96,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('533','Spezial-Fachkraft Produktion 3/4',9,'SFK_PROD',true,97,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('534','Spezial-Fachkraft Produktion 4/4',10,'SFK_PROD',true,98,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('541','Spezialist:in Produktion 1/4',11,'SP_PROD',true,99,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('542a','Spezialist:in Produktion 2a/4',12,'SP_PROD',true,100,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('542b','Spezialist:in Produktion 2b/4',12,'SP_PROD',true,101,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('543a','Spezialist:in Produktion 3a/4',13,'SP_PROD',true,102,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('543b','Spezialist:in Produktion 3b/4',13,'SP_PROD',true,103,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('544','Spezialist:in Produktion 4/4',14,'SP_PROD',true,104,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Handwerk, Logistik, ..
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('611','Hilfskraft Handwerk, Instandhaltung + Logistik 1/4',1,'HK_HIL',true,105,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('612','Hilfskraft Handwerk, Instandhaltung + Logistik 2/4',2,'HK_HIL',true,106,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('613','Hilfskraft Handwerk, Instandhaltung + Logistik 3/4',3,'HK_HIL',true,107,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('614','Hilfskraft Handwerk, Instandhaltung + Logistik 4/4',4,'HK_HIL',true,108,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('621','Fachkraft Handwerk, Instandhaltung + Logistik 1/2',5,'FK_HIL',true,109,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('622','Fachkraft Handwerk, Instandhaltung + Logistik 2/2',6,'FK_HIL',true,110,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('631','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 1/4',7,'SFK_HIL',true,111,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('632','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 2/4',8,'SFK_HIL',true,112,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('633','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 3/4',9,'SFK_HIL',true,113,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('634','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 4/4',10,'SFK_HIL',true,114,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Modellstelle: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_modellstelle wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_fachrichtung' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_fachrichtung (
fachrichtung_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_fachrichtung_pkey PRIMARY KEY (fachrichtung_kurzbz)
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_fachrichtung TO vilesci;
INSERT INTO hr.tbl_lohnguide_fachrichtung(fachrichtung_kurzbz,bezeichnung,aktiv,insertvon,insertamum) VALUES
('FA00','Keine Berücksichtigung',true,'system',NOW()),
('FA01','Administration allgemein',true,'system',NOW()),
('FA02','Dienste Infrastruktur',true,'system',NOW()),
('FA03','Finanzwesen & Controlling',true,'system',NOW()),
('FA04','IT',true,'system',NOW()),
('FA05','Logistik',true,'system',NOW()),
('FA06','Marketing & Digitales Marketing',true,'system',NOW()),
('FA07','Produktion',true,'system',NOW()),
('FA08','Technik',true,'system',NOW()),
('FA09','Verkauf',true,'system',NOW())
ON CONFLICT (fachrichtung_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Fachrichtung: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_fachrichtung wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_vertragsbestandteil_lohnguide' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_vertragsbestandteil_lohnguide (
vertragsbestandteil_id integer NOT NULL,
vordienstzeit int,
stellenbezeichnung varchar(255),
fachrichtung_kurzbz character varying(32) NOT NULL,
modellstelle_kurzbz character varying(32) NOT NULL,
kommentar_person varchar(255),
kommentar_modellstelle varchar(255),
CONSTRAINT tbl_vertragsbestandteil_lohnguide_pk PRIMARY KEY (vertragsbestandteil_id),
CONSTRAINT tbl_vertragsbestandteil_fk FOREIGN KEY (vertragsbestandteil_id) REFERENCES hr.tbl_vertragsbestandteil (vertragsbestandteil_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT tbl_vertragsbestandteil_lohnguide_fachrichtung_fk FOREIGN KEY (fachrichtung_kurzbz) REFERENCES hr.tbl_lohnguide_fachrichtung (fachrichtung_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT tbl_vertragsbestandteil_modellstelle_fachrichtung_fk FOREIGN KEY (modellstelle_kurzbz) REFERENCES hr.tbl_lohnguide_modellstelle (modellstelle_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE
);
COMMENT ON TABLE hr.tbl_vertragsbestandteil_lohnguide IS E'Zuordnung für EU-Entgelttransparenzrichtlinie';
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_vertragsbestandteil_lohnguide TO vilesci;
";
if (! $db->db_query($qry))
echo '<strong>Vertragsbestandteil Lohnguide: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_vertragsbestandteil_lohnguide wurde neu erstellt<br>';
}
}
if($result = $db->db_query("SELECT 1 FROM hr.tbl_vertragsbestandteiltyp WHERE vertragsbestandteiltyp_kurzbz = 'lohnguide'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "insert into hr.tbl_vertragsbestandteiltyp (vertragsbestandteiltyp_kurzbz,bezeichnung,ueberlappend) values('lohnguide','Lohnguide',false)";
if(!$db->db_query($qry))
echo '<strong>Public Tabelle person: '.$db->db_last_error().'</strong><br>';
else
echo "<br>Vertragsbestandteiltyp 'lohnguide' hinzugefuegt";
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.columns WHERE column_name='vordienstzeit' AND table_name='tbl_vertragsbestandteil_lohnguide' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
ALTER TABLE
hr.tbl_vertragsbestandteil_lohnguide
ADD COLUMN
vordienstzeit int;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Spalte vordienstzeit wurde in hr.tbl_vertragsbestandteil_lohnguide neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM hr.tbl_gehaltstyp WHERE gehaltstyp_kurzbz='ueberstundenpauschale'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
INSERT INTO hr.tbl_gehaltstyp
(gehaltstyp_kurzbz, bezeichnung, valorisierung, sort, aktiv, lvexport)
VALUES
('ueberstundenpauschale','Überstundenpauschale', true, 8, true, true);
";
if (! $db->db_query($qry))
echo '<strong>Gehaltstyp: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Gehaltstyp "Überstundenpauschale" erstellt.<br />';
}
}
if ($result = $db->db_query("SELECT * FROM hr.tbl_gehaltstyp WHERE gehaltstyp_kurzbz='sachbezug_pkw'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
INSERT INTO hr.tbl_gehaltstyp
(gehaltstyp_kurzbz, bezeichnung, valorisierung, sort, aktiv, lvexport)
VALUES
('sachbezug_pkw','Sachbezug PKW', true, 9, true, true);
";
if (! $db->db_query($qry))
echo '<strong>Gehaltstyp: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Gehaltstyp "Sachbezug PKW" erstellt.<br />';
}
}
File diff suppressed because it is too large Load Diff