From 50fb2ca7d84417793560df648d5eaed17389c37e Mon Sep 17 00:00:00 2001 From: Luca Haid Date: Fri, 10 Jan 2025 11:12:33 +0000 Subject: [PATCH] Rework dashboard --- .../DashboardNew/DashboardNewController.php | 109 +++++++++++++++--- application/Preorder/PreorderModel.php | 45 +++++++- .../PreorderHistory/PreorderHistoryModel.php | 13 +++ public/js/pages/DashboardNew/DashboardNew.css | 4 + public/js/pages/DashboardNew/DashboardNew.js | 88 +++++++------- 5 files changed, 204 insertions(+), 55 deletions(-) diff --git a/application/DashboardNew/DashboardNewController.php b/application/DashboardNew/DashboardNewController.php index 5210ffc40..682ba4f62 100644 --- a/application/DashboardNew/DashboardNewController.php +++ b/application/DashboardNew/DashboardNewController.php @@ -158,6 +158,26 @@ class DashboardNewController extends mfBaseController { } }; + + $baufortschritt_140 = $countFunction([">status_code" => "139", " "899"]); + $timeline_baufortschritt_140 = $this->getTimeline('weekly-leerrohr', $campaign_ids, $gemeinde_ids); + + $ont_installiert_300 = $countFunction([">status_code" => "299", " "899"]); + $timeline_ont_installiert_300 = $this->getTimeline('weekly-ont-installed', $campaign_ids, $gemeinde_ids); + + $baufortschritt_140_base_value_before_history = $baufortschritt_140 - $timeline_baufortschritt_140[0][count($timeline_baufortschritt_140[0]) - 1]['value']; + + foreach ($timeline_baufortschritt_140[0] as $key => $value) { + $timeline_baufortschritt_140[0][$key]['value'] += $baufortschritt_140_base_value_before_history; + } + + $ont_installiert_300_base_value_before_history = $ont_installiert_300 - $timeline_ont_installiert_300[0][count($timeline_ont_installiert_300[0]) - 1]['value']; + + foreach ($timeline_ont_installiert_300[0] as $key => $value) { + $timeline_ont_installiert_300[0][$key]['value'] += $ont_installiert_300_base_value_before_history; + } + + self::returnJson([ 'order_max_home_addrdb' => $order_max_homes, 'order_actual_order' => $countFunction([]), @@ -167,21 +187,23 @@ class DashboardNewController extends mfBaseController { 'order_mph' => $countFunction(["connection_type" => $mph_connection_types]), 'order_mph_vorsorge' => $countFunction(["connection_type" => $mph_connection_types, "type" => "provision"]), 'order_mph_vollanschluss' => $countFunction(["connection_type" => $mph_connection_types, "type" => "order"]), - 'baufortschritt_140' => $countFunction([">status_code" => "139", " "899"]), + 'baufortschritt_140' => $baufortschritt_140, 'installationspaket_erhalten' => $countFunction(["connection_type" => $efh_connection_types], 145), 'lehrrohr_im_haus' => $countFunction(["connection_type" => $efh_connection_types], 200), 'inhouse_kabel_verlegt_efh' => $countFunction(["connection_type" => $efh_connection_types], 242), 'inhouse_kabel_verlegt_mph' => $countFunction(["connection_type" => $mph_connection_types], 242), 'installationsfortschritt_245' => $countFunction([">status_code" => "244", " "899"]), - 'ont_installiert_300' => $countFunction([">status_code" => "299", " "899"]), + 'ont_installiert_300' => $ont_installiert_300, 'vollanschluss_dokumentiert_350' => $countFunction(["status_code" => ["350","500"], "type" => "order"]), 'vorsorge_dokumentiert_351' => $countFunction(["status_code" => ["351","500"], "type" => "provision"]), 'provider_bestellt_500' => $countFunction(["status_code" => "500"]), - 'timeline' => $this->getTimeline('weekly-orders', $campaign_ids, $gemeinde_ids) + 'timeline' => $this->getTimeline('weekly-orders', $campaign_ids, $gemeinde_ids), + 'timeline_leerrohr' => $timeline_baufortschritt_140, + 'timeline_ont_installed' => $timeline_ont_installiert_300 ]); } - private function getTimeline($type, $campaign_ids, $gemeinde_ids) { + private function getTimeline($type, $campaign_ids, $gemeinde_ids) { //TODO: fix gemeinde $timeline = []; $baseParams = ["preordercampaign_id" => $campaign_ids]; if (!empty($gemeinde_ids)) { @@ -196,6 +218,11 @@ class DashboardNewController extends mfBaseController { return $timeline; } + + + // add Leerrohr to timeline + // add ont installed to timeline + private function getTimelineData($type, $params) { $timeline = []; $start = strtotime('-1 year'); @@ -203,18 +230,74 @@ class DashboardNewController extends mfBaseController { $interval = new DateInterval('P1W'); $daterange = new DatePeriod(date_create(date('Y-m-d', $start)), $interval, date_create(date('Y-m-d', $end))); - foreach ($daterange as $date) { - $date = $date->format('Y-m-d'); - $params['add-where'] = " AND tt_preorder.`create` >= UNIX_TIMESTAMP('" . $date . " 00:00:00') AND tt_preorder.`create` <= UNIX_TIMESTAMP('" . $date . " 23:59:59')"; - $timeline[] = [ - 'date' => date(DATE_ATOM, strtotime($date)), - 'value' => PreorderModel::countActive($params) - ]; + // Generate a unique cache key based on the function parameters + $cacheKey = md5($type . serialize($params)); + $cacheFile = TEMP_DIR . "/Dashboard/" . $cacheKey . ".json"; + + // Ensure the cache directory exists + if (!is_dir(TEMP_DIR . "/Dashboard/")) { + mkdir(TEMP_DIR . "/Dashboard/", 0755, true); } - return $timeline; + // Load cached data if it exists + if (file_exists($cacheFile)) { + $cachedData = json_decode(file_get_contents($cacheFile), true); + $lastCachedDate = end($cachedData)['date']; + $lastCachedTimestamp = strtotime($lastCachedDate); -} + // Only process the last 5 weeks + $daterange = new DatePeriod( + date_create(date('Y-m-d', max($lastCachedTimestamp, strtotime('-5 weeks')))), + $interval, + date_create(date('Y-m-d', $end)) + ); + + $timeline = $cachedData; + } + + foreach ($daterange as $date) { + $date = $date->format('Y-m-d'); + $value = 0; + + switch ($type) { + case 'weekly-orders': + $params['add-where'] = " AND tt_preorder.`create` <= UNIX_TIMESTAMP('" . $date . " 23:59:59')"; + $value = PreorderModel::countActive($params); + break; + case 'weekly-leerrohr': + $params['add-where'] = " AND ph.`create` <= UNIX_TIMESTAMP('" . $date . " 23:59:59')"; + $value = PreorderModel::countHistoryStatus($params, 140); + break; + case 'weekly-ont-installed': + $params['add-where'] = " AND ph.`create` <= UNIX_TIMESTAMP('" . $date . " 23:59:59')"; + $value = PreorderModel::countHistoryStatus($params, 300); + break; + } + + $newData = [ + 'date' => date(DATE_ATOM, strtotime($date)), + 'value' => $value + ]; + + // Update existing data or add new data + $existingIndex = array_search($newData['date'], array_column($timeline, 'date')); + if ($existingIndex !== false) { + $timeline[$existingIndex] = $newData; + } else { + $timeline[] = $newData; + } + } + + // Sort the timeline by date + usort($timeline, function($a, $b) { + return strtotime($a['date']) - strtotime($b['date']); + }); + + // Cache the updated data + file_put_contents($cacheFile, json_encode($timeline)); + + return $timeline; + } private function getTotalHomes(array $preordercampaign_id = [], array $gemeinde_id = []) { $baseSQL = "SELECT COUNT(adb_wohneinheit.id) as cnt FROM `" . ADDRESSDB_DBNAME . "`.Wohneinheit adb_wohneinheit diff --git a/application/Preorder/PreorderModel.php b/application/Preorder/PreorderModel.php index b66e47b1c..155adf305 100644 --- a/application/Preorder/PreorderModel.php +++ b/application/Preorder/PreorderModel.php @@ -395,9 +395,10 @@ class PreorderModel { WHERE $where "; - mfLoghandler::singleton()->debug($sql); - + + $queryStart = microtime(true); $res = $db->query($sql); + mfLoghandler::singleton()->debug("[Query took: ".(microtime(true) - $queryStart)." seconds] " . $sql); if($db->num_rows($res)) { $data = $db->fetch_object($res); return $data->cnt; @@ -860,9 +861,47 @@ class PreorderModel { INNER JOIN `".FRONKDB_DBNAME."`.PreorderStatusflag psf ON psfv.flag_id = psf.id WHERE $where AND psf.code = ".$statusFlag." AND psfv.value = 1"; - mfLoghandler::singleton()->debug($sql); + $queryStart = microtime(true); + $res = $db->query($sql); + mfLoghandler::singleton()->debug("[Query took: ".(microtime(true) - $queryStart)." seconds] " . $sql); + if ($db->num_rows($res)) { + $data = $db->fetch_object($res); + return $data->cnt; + } + return 0; + } + + public static function countHistoryStatus($filter = [], $status_code = null) { + if ($status_code === null) { + die("Please select a status code"); + } + + if(!is_array($filter)) return false; + + if(!array_key_exists("deleted", $filter)) { + $filter["deleted"] = null; + } + + if( !array_key_exists("status_code", $filter) && !array_key_exists("status_code", $filter) + && !array_key_exists("status_id", $filter) && !array_key_exists("status_id", $filter) ) { + $filter["num_rows($res)) { $data = $db->fetch_object($res); return $data->cnt; diff --git a/application/PreorderHistory/PreorderHistoryModel.php b/application/PreorderHistory/PreorderHistoryModel.php index e13ef7fd7..97575a27d 100644 --- a/application/PreorderHistory/PreorderHistoryModel.php +++ b/application/PreorderHistory/PreorderHistoryModel.php @@ -120,6 +120,8 @@ class PreorderHistoryModel { $preorder_id = $filter['preorder_id']; if (is_numeric($preorder_id)) { $where .= " AND PreorderHistory.preorder_id=$preorder_id"; + } else if (is_array($preorder_id)) { + $where .= " AND PreorderHistory.preorder_id IN (" . implode(",", $preorder_id) . ")"; } } @@ -138,6 +140,17 @@ class PreorderHistoryModel { } } + if (array_key_exists("new_value", $filter)) { + $new_value = FronkDB::singleton()->escape($filter["new_value"]); + if($new_value) { + $where .= " AND `new_value` like '$new_value'"; + } + } + + if (array_key_exists("add-where", $filter)) { + $where .= $filter['add-where']; + } + return $where; } diff --git a/public/js/pages/DashboardNew/DashboardNew.css b/public/js/pages/DashboardNew/DashboardNew.css index 700967b1e..59d279ebd 100644 --- a/public/js/pages/DashboardNew/DashboardNew.css +++ b/public/js/pages/DashboardNew/DashboardNew.css @@ -2,6 +2,10 @@ user-select: none; } +#topnav { + z-index: 99999; +} + .dashboard-container { width: 100%; max-width: 600px; diff --git a/public/js/pages/DashboardNew/DashboardNew.js b/public/js/pages/DashboardNew/DashboardNew.js index b3d194def..f07db27c0 100644 --- a/public/js/pages/DashboardNew/DashboardNew.js +++ b/public/js/pages/DashboardNew/DashboardNew.js @@ -80,7 +80,7 @@ Vue.component('tt-dashboard-display-card', { Vue.component('tt-timeline-chart', { props: { - chartData: { + datasets: { type: Array, required: true }, @@ -88,14 +88,6 @@ Vue.component('tt-timeline-chart', { type: String, default: '' }, - dataColor: { - type: String, - default: 'rgb(75, 192, 192)' - }, - fillColor: { - type: Boolean, - default: true - }, showGrid: { type: Boolean, default: true @@ -112,19 +104,24 @@ Vue.component('tt-timeline-chart', { }, methods: { renderChart() { + if (this.chart) { + this.chart.destroy(); + } const ctx = this.$refs.chart.getContext('2d'); this.chart = new Chart(ctx, { type: 'line', data: { - labels: this.chartData.map(item => item.date), - datasets: [{ - label: 'Bestellungen', - data: this.chartData.map(item => item.value), - borderColor: this.dataColor, - backgroundColor: this.fillColor ? this.dataColor : 'transparent', + labels: this.datasets[0].data.map(item => item.date), + datasets: this.datasets.map((dataset, index) => ({ + label: dataset.label, + data: dataset.data.map(item => item.value), + borderColor: dataset.color, + backgroundColor: dataset.fill ? dataset.color : 'transparent', tension: 0.1, - fill: this.fillColor - }] + fill: dataset.fill, + yAxisID: dataset.yAxisID, + order: dataset.order || index + })) }, options: { responsive: true, @@ -148,11 +145,7 @@ Vue.component('tt-timeline-chart', { ticks: { callback: (value, index, values) => { const date = moment(value); - if (this.label === 'KW') { - return `KW ${date.isoWeek()}`; - } else { - return date.format('DD.MM'); - } + return this.label === 'KW' ? `KW ${date.isoWeek()}` : date.format('DD.MM'); } } }, @@ -160,7 +153,7 @@ Vue.component('tt-timeline-chart', { beginAtZero: true, title: { display: true, - text: 'Anzahl der Bestellungen' + text: 'Anzahl' }, grid: { display: this.showGrid @@ -168,18 +161,12 @@ Vue.component('tt-timeline-chart', { } }, plugins: { - legend: { - display: false - }, tooltip: { callbacks: { title: (context) => { const date = moment(context[0].label); - if (this.label === 'KW') { - return `KW ${date.isoWeek()}`; - } else { - return date.format('DD.MM'); - } } + return this.label === 'KW' ? `KW ${date.isoWeek()}` : date.format('DD.MM.YYYY'); + } } } } @@ -188,11 +175,10 @@ Vue.component('tt-timeline-chart', { } }, watch: { - chartData() { - if (this.chart) { - this.chart.data.labels = this.chartData.map(item => item.date); - this.chart.data.datasets[0].data = this.chartData.map(item => item.value); - this.chart.update(); + datasets: { + deep: true, + handler() { + this.renderChart(); } } } @@ -332,12 +318,36 @@ Vue.component('dashboard-new', {
+