diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b8d20acb6c365ad8036a7b36d47dabd876d08efa..300cb04aa0e3d7866bca6e62eb2bbf55af18fbc8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,3 +1,17 @@
 include:
-  - project: 'quiqqer/stabilization/semantic-release'
-    file: '/ci-templates/.gitlab-ci.yml'
+  - component: dev.quiqqer.com/quiqqer/stabilization/ci-cd-components/quiqqer-package-bundle/quiqqer-package-bundle@2
+
+# Remove the entire phpunit-php8.1 block, to allow PHPUnit to run on PHP 8.1 in your pipeline
+phpunit-php8.1:
+  rules:
+    - when: never
+
+# Remove the entire phpunit-php8.2 block, to allow PHPUnit to run on PHP 8.2 in your pipeline
+phpunit-php8.2:
+  rules:
+    - when: never
+
+# Remove the entire phpunit-php8.3 block, to allow PHPUnit to run on PHP 8.3 in your pipeline
+phpunit-php8.3:
+  rules:
+    - when: never
\ No newline at end of file
diff --git a/.phive/phars.xml b/.phive/phars.xml
index 46ebf8cb906c4365e0c3f8a256b5f309b11afab3..38e7a0b219135a0e7d6996378109b9198ddbbb2d 100644
--- a/.phive/phars.xml
+++ b/.phive/phars.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <phive xmlns="https://phar.io/phive">
-  <phar name="phpstan" version="^1.10.67" installed="1.10.67" location="./tools/phpstan" copy="false"/>
+  <phar name="phpstan" version="1.11.8" installed="1.11.8" location="./tools/phpstan" copy="false"/>
   <phar name="phpunit" version="^10.5.20" installed="10.5.20" location="./tools/phpunit" copy="false"/>
   <phar name="phpcs" version="^3.9.2" installed="3.9.2" location="./tools/phpcs" copy="false"/>
   <phar name="phpcbf" version="^3.9.2" installed="3.9.2" location="./tools/phpcbf" copy="false"/>
-  <phar name="captainhook" version="^5.23.0" installed="5.23.0" location="./tools/captainhook" copy="false"/>
+  <phar name="captainhook" version="^5.23.3" installed="5.23.3" location="./tools/captainhook" copy="false"/>
 </phive>
diff --git a/ajax/add.php b/ajax/add.php
index 64dd0bb3ce3efaca79137e1b9d183a8287d4392f..6164a786f011480b9e31d7d915ad3537bb530194 100644
--- a/ajax/add.php
+++ b/ajax/add.php
@@ -18,8 +18,6 @@ QUI::$Ajax->registerFunction(
         $params = json_decode($params, true);
 
         $Manager = new QUI\Cron\Manager();
-
-
         $Manager->add($cron, $min, $hour, $day, $month, $dayOfWeek, $params);
     },
     ['cron', 'min', 'hour', 'day', 'month', 'dayOfWeek', 'params'],
diff --git a/ajax/cron/toggle.php b/ajax/cron/toggle.php
index 82fad109ae6ed661611b04bbaeecffda29b073ee..2daf95791ca01e39264c1bacf3533d5d3b42c7c1 100644
--- a/ajax/cron/toggle.php
+++ b/ajax/cron/toggle.php
@@ -12,7 +12,7 @@ QUI::$Ajax->registerFunction(
     'package_quiqqer_cron_ajax_cron_toggle',
     function ($cronId) {
         $Manager = new QUI\Cron\Manager();
-        $data    = $Manager->getCronById($cronId);
+        $data = $Manager->getCronById($cronId);
 
         if (!$data) {
             throw new QUI\Exception('Cron not exists', 404);
diff --git a/ajax/delete.php b/ajax/delete.php
index 0c2a613946994aca639599e154c6f160b2d03531..d78e3a5354d49c8b478d7475e00f8616a90cb9d2 100644
--- a/ajax/delete.php
+++ b/ajax/delete.php
@@ -9,7 +9,7 @@
 QUI::$Ajax->registerFunction(
     'package_quiqqer_cron_ajax_delete',
     function ($ids) {
-        $ids     = json_decode($ids, true);
+        $ids = json_decode($ids, true);
         $Manager = new QUI\Cron\Manager();
 
         $Manager->deleteCronIds($ids);
diff --git a/ajax/execute.php b/ajax/execute.php
index 1a7d2fdc1407c10c2198c4765fbcf8d145e3593d..9dfa4f43c74a0771c56360ac551a6bf1ad00f9d0 100644
--- a/ajax/execute.php
+++ b/ajax/execute.php
@@ -14,6 +14,13 @@ QUI::$Ajax->registerFunction(
             return;
         }
 
+        // not execute at the first log in
+        if (QUI::getPackage('quiqqer/cron')->getConfig()->get('update', 'logged_in_before') === false) {
+            QUI::getPackage('quiqqer/cron')->getConfig()->set('update', 'logged_in_before', 1);
+            QUI::getPackage('quiqqer/cron')->getConfig()->save();
+            return;
+        }
+
         try {
             $Manager = new QUI\Cron\Manager();
             $Manager->execute();
diff --git a/ajax/getList.php b/ajax/getList.php
index 74108b69cf49910e3d32c4cc4105b385a105fbae..d04bd28388613337db347f0a6488638c96bde28a 100644
--- a/ajax/getList.php
+++ b/ajax/getList.php
@@ -13,8 +13,8 @@ QUI::$Ajax->registerFunction(
         $list = $CronManager->getList();
         $Locale = QUI::getLocale();
         $Formatter = $Locale->getDateFormatter(
-            \IntlDateFormatter::SHORT,
-            \IntlDateFormatter::SHORT
+            IntlDateFormatter::SHORT,
+            IntlDateFormatter::SHORT
         );
 
         foreach ($list as $key => $cron) {
@@ -23,7 +23,11 @@ QUI::$Ajax->registerFunction(
                 $list[$key]['title'] = $Locale->get($locale[0], $locale[1]);
             }
 
-            $list[$key]['lastexec'] = $Formatter->format(\strtotime($list[$key]['lastexec']));
+            if (!empty($list[$key]['lastexec'])) {
+                $list[$key]['lastexec'] = $Formatter->format(strtotime($list[$key]['lastexec']));
+            } else {
+                $list[$key]['lastexec'] = '';
+            }
         }
 
         return $list;
diff --git a/ajax/history/get.php b/ajax/history/get.php
index 646820eac1399034ebc62bc3ae5d543c5aa19c4b..560a356f817f2cd823c437416032fa77c3d8237f 100644
--- a/ajax/history/get.php
+++ b/ajax/history/get.php
@@ -11,11 +11,11 @@ QUI::$Ajax->registerFunction(
     'package_quiqqer_cron_ajax_history_get',
     function ($params) {
         $CronManager = new QUI\Cron\Manager();
-        $params      = json_decode($params, true);
+        $params = json_decode($params, true);
 
         return [
-            'page'  => (int)$params['page'],
-            'data'  => $CronManager->getHistoryList($params),
+            'page' => (int)$params['page'],
+            'data' => $CronManager->getHistoryList($params),
             'total' => $CronManager->getHistoryCount()
         ];
     },
diff --git a/bin/executeCronViaAdmin.js b/bin/executeCronViaAdmin.js
new file mode 100644
index 0000000000000000000000000000000000000000..6de8d9f18d5b7d5661db3d2d3bc55ec59658b021
--- /dev/null
+++ b/bin/executeCronViaAdmin.js
@@ -0,0 +1,40 @@
+window.addEvent('quiqqerLoaded', function() {
+    require(['Ajax', 'Locale'], function(QUIAjax, QUILocale) {
+
+        const RunningInfo = new Element('div', {
+            html: '' +
+                '<span style="padding-right: 10px; font-size: 20px">' +
+                '   <span class="fa fa-circle-o-notch fa-spin"></span>' +
+                '</span>' +
+                '<span>' + QUILocale.get('quiqqer/cron', 'message.admin.cron.execution') + '</span>',
+            styles: {
+                background: '#fff',
+                bottom: 20,
+                boxShadow: 'rgba(35, 46, 60, .04) 0 2px 4px 0',
+                border: '1px solid rgba(101, 109, 119, .16)',
+                borderLeft: '.25rem solid #4299e1',
+                borderRadius: 4,
+                display: 'flex',
+                maxWidth: '90%',
+                padding: 20,
+                position: 'fixed',
+                right: 20,
+                width: 500,
+                zIndex: 10000
+            }
+        }).inject(document.body);
+
+        QUIAjax.post('package_quiqqer_cron_ajax_execute', function() {
+            moofx(RunningInfo).animate({
+                bottom: 10,
+                opacity: 0
+            }, {
+                callback: () => {
+                    RunningInfo.destroy();
+                }
+            });
+        }, {
+            'package': 'quiqqer/cron'
+        });
+    });
+});
diff --git a/captainhook.json b/captainhook.json
index d043c267627f08ccbf13b773bea896f269516700..3702e1a358868bedd5ff4c7eae40bb1abb589267 100644
--- a/captainhook.json
+++ b/captainhook.json
@@ -1,16 +1,13 @@
 {
-  "config": {
-    "bootstrap": "tests/captainhook-bootstrap.php"
-  },
-  "pre-commit": {
-    "enabled": true,
-    "actions": [
-      {
-        "action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting"
-      },
-      {
-          "action": "composer test"
-      }
-    ]
-  }
-}
+    "pre-commit": {
+        "enabled": true,
+        "actions": [
+            {
+                "action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting"
+            },
+            {
+                "action": "composer test"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/locale.xml b/locale.xml
index d900d0c46a4672961425d1c50caf40d7c73bb1f5..2adc2a6f46f3d91b0c84721e83607964c549192f 100644
--- a/locale.xml
+++ b/locale.xml
@@ -470,7 +470,7 @@
             <de><![CDATA[Führt ein automatisches Security Update durch.]]></de>
             <en><![CDATA[Performs an automatic security update.]]></en>
         </locale>
-        
+
     </groups>
     <groups name="quiqqer/cron" datatype="js">
         <locale name="cron.id">
@@ -913,6 +913,17 @@ This is realized with an external server on which QUIQQER systems can register t
             <de><![CDATA[Nicht jetzt]]></de>
             <en><![CDATA[Not yet]]></en>
         </locale>
-
+        <locale name="message.admin.cron.execution">
+            <de><![CDATA[
+            Zurzeit wird eine geplante Systemaufgabe (Cron-Job) ausgeführt.
+            Dadurch können andere Aktionen wie Anfragen oder das Laden von Dateien etwas langsamer sein.
+            Bitte haben Sie ein wenig Geduld. Vielen Dank für Ihr Verständnis.
+            ]]></de>
+            <en><![CDATA[
+            A scheduled system task (cron job) is currently being executed.
+            As a result, other actions such as requests or loading files may be somewhat slower.
+            Please be patient. Thank you for your understanding.
+            ]]></en>
+        </locale>
     </groups>
 </locales>
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e565ab75f7942439532ab10b44a50f0478da9099 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -0,0 +1,21 @@
+parameters:
+	ignoreErrors:
+		-
+			message: "#^Parameter \\#1 \\$cronId of method QUI\\\\Cron\\\\Console\\\\ExecCrons\\:\\:runCron\\(\\) expects bool\\|int, string given\\.$#"
+			count: 1
+			path: src/QUI/Cron/Console/ExecCrons.php
+
+		-
+			message: "#^Access to an undefined property DOMNode\\:\\:\\$tagName\\.$#"
+			count: 1
+			path: src/QUI/Cron/Manager.php
+
+		-
+			message: "#^PHPDoc tag @return with type QUI\\\\Cron\\\\Manager is not subtype of native type static\\(QUI\\\\Cron\\\\Manager\\)\\.$#"
+			count: 1
+			path: src/QUI/Cron/Manager.php
+
+		-
+			message: "#^Call to an undefined method QUI\\\\Projects\\\\Site\\:\\:deactivate\\(\\)\\.$#"
+			count: 1
+			path: src/QUI/Cron/QuiqqerCrons.php
diff --git a/phpstan.dist.neon b/phpstan.dist.neon
index 37e6b52ac8d173c7f1e1645fbf40eab4adeccdb2..4007e87ea05412ef4bdf233cc327c8b05ec07691 100644
--- a/phpstan.dist.neon
+++ b/phpstan.dist.neon
@@ -2,10 +2,16 @@ includes:
 	- phpstan-baseline.neon
 
 parameters:
-    level: 1
+    level: 5
     paths:
         - src
         - ajax
     bootstrapFiles:
         - tests/phpstan-bootstrap.php
+    customRulesetUsed: true
+services:
+    -
+        class: \PHPStan\Rules\Properties\TypesAssignedToPropertiesRule
+        tags:
+            - phpstan.rules.rule
 
diff --git a/src/QUI/Cron/EventHandler.php b/src/QUI/Cron/EventHandler.php
index 553181409d7fdfccb3a160222cddf2a48493c9f3..9438b1e038ceb731608e91133e3aed5515eb415f 100644
--- a/src/QUI/Cron/EventHandler.php
+++ b/src/QUI/Cron/EventHandler.php
@@ -135,21 +135,7 @@ class EventHandler
 
         // execute cron ?
         if ($Config->get('settings', 'executeOnAdminLogin')) {
-            echo '
-            <script>
-            window.addEvent("load", function()
-            {
-                require(["Ajax"], function(QUIAjax)
-                {
-                    QUIAjax.post("package_quiqqer_cron_ajax_execute", function()
-                    {
-
-                    }, {
-                        "package" : "quiqqer/cron"
-                    });
-                });
-            });
-            </script>';
+            echo '<script src="' . URL_OPT_DIR . 'quiqqer/cron/bin/executeCronViaAdmin.js"></script>';
         }
 
         if (self::$cronWarning) {
diff --git a/tests/captainhook-bootstrap.php b/tests/captainhook-bootstrap.php
deleted file mode 100644
index 1b61b73a71ef5fb804addf29a4266548612c8cf0..0000000000000000000000000000000000000000
--- a/tests/captainhook-bootstrap.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-
-// This file is supposed to be empty, see https://github.com/captainhookphp/captainhook/issues/248