diff --git a/src/new/src/PSC/System/SettingsBundle/Resources/config/services.yml b/src/new/src/PSC/System/SettingsBundle/Resources/config/services.yml
index 3091e6abe..c3dbbac68 100755
--- a/src/new/src/PSC/System/SettingsBundle/Resources/config/services.yml
+++ b/src/new/src/PSC/System/SettingsBundle/Resources/config/services.yml
@@ -8,6 +8,19 @@ services:
PSC\System\SettingsBundle\:
resource: '../../*/*'
+ PSC\System\SettingsBundle\Service\DiskUsage:
+ arguments:
+ $directories:
+ 'Uploads (Data Volume)': '/data/www/old/public/uploads'
+ 'Pakete (Data Volume)': '/data/www/old/data/packages'
+ 'Templateprint Layouter (Data Volume)': '/data/www/old/market/templateprint'
+ 'Form Based Layouter (Data Volume)': '/data/www/old/market/collectlayouter'
+ 'Creative Layouter (Data Volume)': '/data/www/old/market/steplayouter'
+ # Daten-Volume (/dev/sdb): Bezug für die obere Liste inkl. "Übrige Belegung"
+ $dataPath: '/data/www/new/watch'
+ # System-/Overlay-Dateisystem: Bezug für Belegt/Frei/Gesamt unten
+ $systemPath: '/'
+
PSC\System\SettingsBundle\Form\Backend\CopyType:
tags:
- { name: form.type }
@@ -64,4 +77,4 @@ services:
PSC\System\SettingsBundle\Resolver\AbsolutePathResolver:
tags:
- - { name: liip_imagine.cache.resolver, resolver: absolutePathResolver }
\ No newline at end of file
+ - { name: liip_imagine.cache.resolver, resolver: absolutePathResolver }
diff --git a/src/new/src/PSC/System/SettingsBundle/Service/DiskUsage.php b/src/new/src/PSC/System/SettingsBundle/Service/DiskUsage.php
new file mode 100644
index 000000000..ffe2d27fd
--- /dev/null
+++ b/src/new/src/PSC/System/SettingsBundle/Service/DiskUsage.php
@@ -0,0 +1,144 @@
+ $directories Label => absoluter Pfad
+ * @param string $dataPath Pfad auf dem Daten-Volume (z.B. /dev/sdb), gegen das
+ * die überwachten Verzeichnisse abgeglichen werden
+ * @param string $systemPath Pfad auf dem System-/Overlay-Dateisystem
+ */
+ public function __construct(
+ private Connection $connection,
+ private CacheInterface $cache,
+ private array $directories,
+ private string $dataPath = '/data/www/old',
+ private string $systemPath = '/',
+ private int $cacheTtl = 3600,
+ ) {
+ }
+
+ /**
+ * @return array{
+ * database: ?int,
+ * directories: array,
+ * dataDisk: array{total: ?int, free: ?int, used: ?int},
+ * systemDisk: array{total: ?int, free: ?int, used: ?int}
+ * }
+ */
+ public function getUsage(): array
+ {
+ // Versionssuffix: ändert sich die Struktur, werden alte Cache-Einträge ignoriert
+ return $this->cache->get('psc_dashboard_disk_usage_v2', function (ItemInterface $item): array {
+ $item->expiresAfter($this->cacheTtl);
+
+ return [
+ 'database' => $this->getDatabaseSize(),
+ 'directories' => $this->getDirectorySizes(),
+ 'dataDisk' => $this->getDiskSpace($this->dataPath),
+ 'systemDisk' => $this->getDiskSpace($this->systemPath),
+ ];
+ });
+ }
+
+ /**
+ * Gesamtkapazität, freier und belegter Speicher des Dateisystems am
+ * angegebenen Pfad in Bytes.
+ *
+ * @return array{total: ?int, free: ?int, used: ?int}
+ */
+ private function getDiskSpace(string $path): array
+ {
+ if (!is_dir($path)) {
+ return ['total' => null, 'free' => null, 'used' => null];
+ }
+
+ $total = @disk_total_space($path);
+ $free = @disk_free_space($path);
+
+ if ($total === false || $free === false) {
+ return ['total' => null, 'free' => null, 'used' => null];
+ }
+
+ return [
+ 'total' => (int) $total,
+ 'free' => (int) $free,
+ 'used' => (int) ($total - $free),
+ ];
+ }
+
+ /**
+ * Größe der aktuellen Datenbank in Bytes (Daten + Indizes).
+ */
+ private function getDatabaseSize(): ?int
+ {
+ try {
+ $database = $this->connection->getDatabase();
+
+ if ($database === null) {
+ return null;
+ }
+
+ $size = $this->connection->fetchOne(
+ 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = :db',
+ ['db' => $database],
+ );
+
+ return $size !== false && $size !== null ? (int) $size : null;
+ } catch (\Throwable) {
+ return null;
+ }
+ }
+
+ /**
+ * @return array Label => Größe in Bytes (oder null)
+ */
+ private function getDirectorySizes(): array
+ {
+ $result = [];
+
+ foreach ($this->directories as $label => $path) {
+ $result[$label] = $this->getDirectorySize($path);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Größe eines Verzeichnisses in Bytes via "du" (schnell). Liefert null,
+ * wenn das Verzeichnis fehlt oder nicht ermittelt werden kann.
+ */
+ private function getDirectorySize(string $path): ?int
+ {
+ if (!is_dir($path)) {
+ return null;
+ }
+
+ $output = [];
+ $exitCode = 0;
+ @exec('du -sb ' . escapeshellarg($path) . ' 2>/dev/null', $output, $exitCode);
+
+ if ($exitCode === 0 && isset($output[0])) {
+ $parts = preg_split('/\s+/', trim($output[0]));
+
+ if (isset($parts[0]) && is_numeric($parts[0])) {
+ return (int) $parts[0];
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/new/var/plugins/Custom/Ahrweiler/Harry/Controller/Backend/StartController.php b/src/new/var/plugins/Custom/Ahrweiler/Harry/Controller/Backend/StartController.php
index 5fee781af..af94e1542 100644
--- a/src/new/var/plugins/Custom/Ahrweiler/Harry/Controller/Backend/StartController.php
+++ b/src/new/var/plugins/Custom/Ahrweiler/Harry/Controller/Backend/StartController.php
@@ -197,7 +197,7 @@ class StartController extends AbstractController
$docData = [];
if ($contactEntity->getAbteilung() != '') {
$docData[] = [
- 'name' => 'data[grad][enable]',
+ 'name' => 'data[betrieb][enable]',
'value' => '1',
];
}
diff --git a/src/new/var/tailwind/backend.built.css b/src/new/var/tailwind/backend.built.css
index ae6cf8351..9f1319538 100644
--- a/src/new/var/tailwind/backend.built.css
+++ b/src/new/var/tailwind/backend.built.css
@@ -1587,6 +1587,10 @@ html {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
+.grid-cols-3{
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+
.grid-cols-4{
grid-template-columns: repeat(4, minmax(0, 1fr));
}
@@ -1974,6 +1978,11 @@ html {
border-color: rgb(250 204 21 / var(--tw-border-opacity));
}
+.bg-amber-500{
+ --tw-bg-opacity: 1;
+ background-color: rgb(245 158 11 / var(--tw-bg-opacity));
+}
+
.bg-black\/70{
background-color: rgb(0 0 0 / 0.7);
}
@@ -2003,6 +2012,11 @@ html {
background-color: rgb(29 78 216 / var(--tw-bg-opacity));
}
+.bg-emerald-500{
+ --tw-bg-opacity: 1;
+ background-color: rgb(16 185 129 / var(--tw-bg-opacity));
+}
+
.bg-gray-100{
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
@@ -2110,6 +2124,16 @@ html {
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
}
+.bg-rose-500{
+ --tw-bg-opacity: 1;
+ background-color: rgb(244 63 94 / var(--tw-bg-opacity));
+}
+
+.bg-sky-500{
+ --tw-bg-opacity: 1;
+ background-color: rgb(14 165 233 / var(--tw-bg-opacity));
+}
+
.bg-slate-100{
--tw-bg-opacity: 1;
background-color: rgb(241 245 249 / var(--tw-bg-opacity));
@@ -2125,6 +2149,11 @@ html {
background-color: rgb(245 245 244 / var(--tw-bg-opacity));
}
+.bg-violet-500{
+ --tw-bg-opacity: 1;
+ background-color: rgb(139 92 246 / var(--tw-bg-opacity));
+}
+
.bg-white{
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -2312,10 +2341,18 @@ html {
padding-right: 1rem;
}
+.pt-1{
+ padding-top: 0.25rem;
+}
+
.pt-2{
padding-top: 0.5rem;
}
+.pt-3{
+ padding-top: 0.75rem;
+}
+
.text-left{
text-align: left;
}
@@ -2453,6 +2490,11 @@ html {
color: rgb(30 64 175 / var(--tw-text-opacity));
}
+.text-emerald-600{
+ --tw-text-opacity: 1;
+ color: rgb(5 150 105 / var(--tw-text-opacity));
+}
+
.text-gray-400{
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
@@ -2777,6 +2819,10 @@ html {
transition-duration: 300ms;
}
+.duration-500{
+ transition-duration: 500ms;
+}
+
.duration-75{
transition-duration: 75ms;
}
@@ -4511,6 +4557,11 @@ html {
background-color: rgb(30 58 138 / 0.2);
}
+:is(.dark .dark\:bg-gray-500){
+ --tw-bg-opacity: 1;
+ background-color: rgb(107 114 128 / var(--tw-bg-opacity));
+}
+
:is(.dark .dark\:bg-gray-600){
--tw-bg-opacity: 1;
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
@@ -4581,6 +4632,11 @@ html {
color: rgb(59 130 246 / var(--tw-text-opacity));
}
+:is(.dark .dark\:text-emerald-400){
+ --tw-text-opacity: 1;
+ color: rgb(52 211 153 / var(--tw-text-opacity));
+}
+
:is(.dark .dark\:text-gray-100){
--tw-text-opacity: 1;
color: rgb(243 244 246 / var(--tw-text-opacity));
@@ -4968,6 +5024,12 @@ html {
gap: 2rem;
}
+ .lg\:space-y-8 > :not([hidden]) ~ :not([hidden]){
+ --tw-space-y-reverse: 0;
+ margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(2rem * var(--tw-space-y-reverse));
+ }
+
.lg\:border-r{
border-right-width: 1px;
}
diff --git a/src/new/version.yaml b/src/new/version.yaml
index 9b712381f..54b5fe56e 100755
--- a/src/new/version.yaml
+++ b/src/new/version.yaml
@@ -1,11 +1,12 @@
info:
- datum: 02.06.2026
+ datum: 10.06.2026
release: 2.3.6
changelog:
- version: 2.3.6
- datum: 02.06.2026
+ datum: 10.06.2026
changes:
+ - "Verbrauchsanzeige im Dashboard"
- "Shop kann eigene SMTP Einstellungen haben"
- "Passwort Start und Finish Aktion Absender und Empfänger Bug behoben"
- "Form Based Layouter speichert jetzt die Firma vom angemeldeten Benutzer"