From 11ebed70a78fa809aea34139caa476960def3dc2 Mon Sep 17 00:00:00 2001 From: Luca Haid Date: Tue, 13 May 2025 11:41:39 +0200 Subject: [PATCH] added new adb dashboard --- .../ADBHausnummer/ADBHausnummerModel.php | 88 +++++++-- .../DashboardNew/DashboardNewController.php | 77 ++++++++ .../20250513113800_hausnummer_add_indexes.php | 29 +++ public/js/pages/DashboardAdb/DashboardAdb.css | 93 +++++++++ public/js/pages/DashboardAdb/DashboardAdb.js | 186 ++++++++++++++++++ 5 files changed, 453 insertions(+), 20 deletions(-) create mode 100644 db/migrations/20250513113800_hausnummer_add_indexes.php create mode 100644 public/js/pages/DashboardAdb/DashboardAdb.css create mode 100644 public/js/pages/DashboardAdb/DashboardAdb.js diff --git a/application/ADBHausnummer/ADBHausnummerModel.php b/application/ADBHausnummer/ADBHausnummerModel.php index ab63b1346..780ceb0c6 100644 --- a/application/ADBHausnummer/ADBHausnummerModel.php +++ b/application/ADBHausnummer/ADBHausnummerModel.php @@ -34,6 +34,7 @@ class ADBHausnummerModel { public $manual_update_by; public $manual_update_info; public $rimo_id; + public $rimo_type; public $rimo_ex_state; public $rimo_op_state; public $rimo_fcp_name; @@ -111,13 +112,15 @@ class ADBHausnummerModel { return $items; } - - - public static function count($filter) { - $db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); - - $where = self::getSqlFilter($filter); - $sql = "SELECT COUNT(*) as cnt FROM ( + + + public static function count($filter, $join_tables = true) { + $db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); + + $where = self::getSqlFilter($filter); + + if ($join_tables) { + $sql = "SELECT COUNT(*) as cnt FROM ( SELECT Hausnummer.id FROM Hausnummer LEFT JOIN Ortschaft ON (Ortschaft.id = Hausnummer.ortschaft_id) LEFT JOIN Gemeinde ON (Gemeinde.id = Ortschaft.gemeinde_id) @@ -125,21 +128,43 @@ class ADBHausnummerModel { LEFT JOIN Plz ON (Hausnummer.plz_id = Plz.id) LEFT JOIN HausnummerStatusflagValue ON (HausnummerStatusflagValue.hausnummer_id = Hausnummer.id) WHERE $where - GROUP BY Hausnummer.id - ) hn - "; + ) hn"; + } else $sql = "SELECT COUNT(*) as cnt FROM Hausnummer WHERE $where"; - mfLoghandler::singleton()->debug($sql); - - $res = $db->query($sql); - if($db->num_rows($res)) { - $data = $db->fetch_object($res); - return $data->cnt; + mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if ($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + + return 0; } - return 0; - } - - public static function search($filter, $limit = false, $returnDBRessource = false) { + + public static function countHomes($filter) { + $db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); + + $where = self::getSqlFilter($filter); + + $sql = "SELECT COUNT(Wohneinheit.id) as cnt + FROM Hausnummer + LEFT JOIN Wohneinheit ON (Wohneinheit.hausnummer_id = Hausnummer.id) + WHERE $where"; + + mfLoghandler::singleton()->debug($sql); + + $res = $db->query($sql); + if ($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + + return 0; + } + + + public static function search($filter, $limit = false, $returnDBRessource = false) { $items = []; $db = FronkDB::singleton(ADDRESSDB_DBHOST, ADDRESSDB_DBUSER, ADDRESSDB_DBPASS, ADDRESSDB_DBNAME); @@ -436,6 +461,29 @@ class ADBHausnummerModel { $where .= " AND HausnummerStatusflagValue.flag_id = $status_flag AND HausnummerStatusflagValue.value=1"; } } + + if (array_key_exists("rimo_type", $filter)) { + $rimo_type = $filter['rimo_type']; + $where .= is_array($rimo_type) + ? " AND Hausnummer.rimo_type IN ('" . implode("', '", $rimo_type) . "')" + : " AND Hausnummer.rimo_type = '$rimo_type'"; + } + + if (array_key_exists("rimo_ex_state", $filter)) { + $rimo_ex_state = $filter['rimo_ex_state']; + $where .= is_array($rimo_ex_state) + ? " AND Hausnummer.rimo_ex_state IN ('" . implode("', '", $rimo_ex_state) . "')" + : " AND Hausnummer.rimo_ex_state = '$rimo_ex_state'"; + } + + if (array_key_exists("rimo_op_state", $filter)) { + $rimo_op_state = $filter['rimo_op_state']; + $where .= is_array($rimo_op_state) + ? " AND Hausnummer.rimo_op_state IN ('" . implode("', '", $rimo_op_state) . "')" + : " AND Hausnummer.rimo_op_state = '$rimo_op_state'"; + } + + //var_dump($filter, $where);exit; return $where; diff --git a/application/DashboardNew/DashboardNewController.php b/application/DashboardNew/DashboardNewController.php index 7e928201f..e7d8eeb8b 100644 --- a/application/DashboardNew/DashboardNewController.php +++ b/application/DashboardNew/DashboardNewController.php @@ -22,6 +22,18 @@ class DashboardNewController extends mfBaseController { Helper::renderVue($this, $this->mod, "Dashboard", ["IS_ADMIN" => $this->me->is("Admin") ? "true" : "false"]); } + protected function adbAction() { + if (!$this->me->can("Statistics") || !$this->me->is(["Admin", "netowner", "salespartner"])) { + $this->redirect("Dashboard"); + } + + if (!$this->me->is("Admin")) { + $this->redirect("Dashboard"); + } + + Helper::renderVue($this, "DashboardAdb", "Dashboard ADB", ["IS_ADMIN" => $this->me->is("Admin") ? "true" : "false"]); + } + protected function getNetOwnerFilterOptionsAction() { $allPreorderCampaigns = PreordercampaignModel::getAll(); @@ -43,6 +55,20 @@ class DashboardNewController extends mfBaseController { self::returnJson($netowners); } + protected function getNetworkFilterOptionsAction() { + $netowners = []; + $allNetworks = ADBNetzgebietModel::getAll(); + foreach ($allNetworks as $network) { + if (!$this->me->is("Admin") && $network->owner_id != $this->me->address_id) continue; + + if (!in_array($network->id, array_column($netowners, 'value'))) { + $netowners[] = ['text' => $network->name, 'value' => $network->id]; + } + } + + self::returnJson($netowners); + } + protected function getCampaignFilterOptionsAction() { $post = json_decode(file_get_contents('php://input'), true); $netowner_ids = isset($post['netOwners']) ? [$post['netOwners']] : []; @@ -460,4 +486,55 @@ class DashboardNewController extends mfBaseController { } + + protected function getDashboardAddressDBDataAction() { + if (!$this->me->is("Admin")) self::sendError("Keine Berechtigung"); + $baseFilter = []; + + if (!empty($str = $this->request->netzgebiet_id)) $baseFilter["netzgebiet_id"] = $this->request->netzgebiet_id; + + $sum_counts = [ + "sum" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0]), false), + "sum_homes" => ADBHausnummerModel::countHomes(array_merge($baseFilter, ["is_deleted" => 0])), + "op_state_planned" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Planned"]), false), + "op_state_passed" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Passed"]), false), + "op_state_connected" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Connected"]), false), + ]; + + $efh_rimo_types = ["residential", "company", "2/3 familienhaus", "public"]; + + $efh_counts = [ + "sum" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => $efh_rimo_types]), false), + "sum_homes" => ADBHausnummerModel::countHomes(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => $efh_rimo_types])), + "op_state_planned" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Planned", "rimo_type" => $efh_rimo_types]), false), + "op_state_passed" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Passed", "rimo_type" => $efh_rimo_types]), false), + "op_state_connected" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Connected", "rimo_type" => $efh_rimo_types]), false), + ]; + + $mph_rimo_types = ["multiple dwellings"]; + + $mph_counts = [ + "sum" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => $mph_rimo_types]), false), + "sum_homes" => ADBHausnummerModel::countHomes(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => $mph_rimo_types])), + "op_state_planned" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Planned", "rimo_type" => $mph_rimo_types]), false), + "op_state_passed" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Passed", "rimo_type" => $mph_rimo_types]), false), + "op_state_connected" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_op_state" => "Connected", "rimo_type" => $mph_rimo_types]), false), + ]; + + $other_types = [ + "type_greenfield" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => "greenfield"]), false), + "type_greenfield_homes" => ADBHausnummerModel::countHomes(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => "greenfield"])), + "type_transformer_station" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => "transformer station"]), false), + "type_transformer_station_homes" => ADBHausnummerModel::countHomes(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => "transformer station"])), + "type_others" => ADBHausnummerModel::count(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => "other"]), false), + "type_others_homes" => ADBHausnummerModel::countHomes(array_merge($baseFilter, ["is_deleted" => 0, "rimo_type" => "other"])), + ]; + + self::returnJson([ + "sum_counts" => $sum_counts, + "efh_counts" => $efh_counts, + "mph_counts" => $mph_counts, + "other_types" => $other_types + ]); + } } \ No newline at end of file diff --git a/db/migrations/20250513113800_hausnummer_add_indexes.php b/db/migrations/20250513113800_hausnummer_add_indexes.php new file mode 100644 index 000000000..b84543d7b --- /dev/null +++ b/db/migrations/20250513113800_hausnummer_add_indexes.php @@ -0,0 +1,29 @@ +getEnvironment() == "addressdb") { + $table = $this->table("Hausnummer"); + $table->addIndex("rimo_type", ["name" => "idx_rimo_type"]); + $table->addIndex("rimo_op_state", ["name" => "idx_rimo_op_state"]); + $table->addIndex(["rimo_type", "rimo_op_state"], ["name" => "idx_combined_type_state"]); + $table->update(); + } + } + + public function down(): void + { + if($this->getEnvironment() == "addressdb") { + $table = $this->table("Hausnummer"); + $table->removeIndex(["rimo_type"], ["name" => "idx_rimo_type"]); + $table->removeIndex(["rimo_op_state"], ["name" => "idx_rimo_op_state"]); + $table->removeIndex(["rimo_type", "rimo_op_state"], ["name" => "idx_combined_type_state"]); + $table->update(); + } + } +} \ No newline at end of file diff --git a/public/js/pages/DashboardAdb/DashboardAdb.css b/public/js/pages/DashboardAdb/DashboardAdb.css new file mode 100644 index 000000000..8b9658cfa --- /dev/null +++ b/public/js/pages/DashboardAdb/DashboardAdb.css @@ -0,0 +1,93 @@ +#app { + user-select: none; +} + +#topnav { + z-index: 99999; +} + +.dashboard-container { + width: 100%; + max-width: 600px; + margin: 0 auto; +} + +.filter-section, +.campaign-section { + width: 100%; +} + +.dashboard-cards { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +.dashboard-data-selector { + padding-top: 12px; + display: grid; + grid-template-columns: repeat(1, 1fr); + justify-content: center; + align-items: center; + gap: 1rem; +} + +.dashboard-data-selector--no-net-owner { + grid-template-columns: repeat(2, 1fr); +} + +.dashboard-chart { + height: 400px; + margin-top: 20px; +} + +.dashboard-buttons { + display: flex; + gap: 1rem; + margin-top: 20px; +} + + +@media (min-width: 768px) { + .dashboard-cards { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (min-width: 1000px) { + .dashboard-cards { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (min-width: 1332px) { + .dashboard-cards { + grid-template-columns: repeat(3, 1fr); + } +} + +#app > div.card > div > div > div:nth-child(4) { + grid-template-columns: repeat(3, 1fr); +} + +@media (max-width: 767px) { + .filter-section, + .campaign-section { + grid-column: 1 / -1; + } + + .dashboard-cards { + grid-template-columns: repeat(1, 1fr); + } + + #app > div.card > div > div > div:nth-child(4) { + grid-template-columns: repeat(1, 1fr); + } + + + .dashboard-data-selector { + grid-template-columns: repeat(1, 1fr); + } +} + + diff --git a/public/js/pages/DashboardAdb/DashboardAdb.js b/public/js/pages/DashboardAdb/DashboardAdb.js new file mode 100644 index 000000000..b7cc5720d --- /dev/null +++ b/public/js/pages/DashboardAdb/DashboardAdb.js @@ -0,0 +1,186 @@ +Vue.component('dashboard-location-selector-adb', { + template: ` +
+ +
+ `, + props: { + selectedNetwork: {type: String, required: true} + }, + data() { + return { + filterOptions: { + netOwners: [] + } + }; + }, + async mounted() { + await this.fetchNetworkFilterOptions(); + }, + methods: { + async fetchNetworkFilterOptions() { + const response = await axios.get(`${window.TT_CONFIG['BASE_PATH']}/DashboardNew/getNetworkFilterOptions`); + this.filterOptions.netOwners = [{value: 'all', text: 'Alle'}, ...response.data]; + } + } +}); + +Vue.component('tt-dashboard-display-card', { + props: ['header', 'icon', 'text', 'subHeaders', 'color'], + computed: { + cardHeight() { + return `${96 + 25 * this.subHeaders?.length ?? 0}px`; + } + }, + template: ` +
+
+
+
+ +
+
{{ header }}
+

{{ text }}

+
+

{{ subHeader }}

+
+
+
+
+ ` +}) + +Vue.component('dashboard-adb-content', { + props: ['addressDbData'], + template: ` +
+

Gebäude

+
+ + + + + +
+ + +
+

Andere Typen

+
+ + + +
+
+ ` +}); + +Vue.component('dashboard-adb', { + template: ` + + + +
+ +
+ + +
+
+ `, + data() { + return { + addressDbData: null, + selectedNetwork: 'all', + isLoading: false + }; + }, + async mounted() { + await this.fetchAddressDbData(); + }, + methods: { + async fetchAddressDbData() { + this.isLoading = true; + try { + // Use the correct API endpoint as specified + const netzgebietId = this.selectedNetwork === 'all' ? '' : this.selectedNetwork; + const url = `${window.TT_CONFIG['BASE_PATH']}/DashboardNew/getDashboardAddressDBData${netzgebietId ? '?netzgebiet_id=' + netzgebietId : ''}`; + + const response = await axios.get(url); + this.addressDbData = response.data; + console.log('Address DB data:', this.addressDbData); + } catch (error) { + console.error('Error fetching address DB data:', error); + } finally { + this.isLoading = false; + } + } + }, + watch: { + selectedNetwork() { + this.fetchAddressDbData(); + } + } +}); \ No newline at end of file