Merge branch 'rework-dashboard' into 'master'

Rework dashboard

See merge request fronk/thetool!880
This commit is contained in:
Luca Haid
2025-01-10 11:12:33 +00:00
5 changed files with 204 additions and 55 deletions

View File

@@ -158,6 +158,26 @@ class DashboardNewController extends mfBaseController {
}
};
$baufortschritt_140 = $countFunction([">status_code" => "139", "<status_code" => "899"]);
$timeline_baufortschritt_140 = $this->getTimeline('weekly-leerrohr', $campaign_ids, $gemeinde_ids);
$ont_installiert_300 = $countFunction([">status_code" => "299", "<status_code" => "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", "<status_code" => "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", "<status_code" => "899"]),
'ont_installiert_300' => $countFunction([">status_code" => "299", "<status_code" => "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

View File

@@ -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_code", $filter)
&& !array_key_exists("<status_id", $filter) && !array_key_exists(">status_id", $filter) && !array_key_exists("status_id", $filter) ) {
$filter["<status_code"] = 899;
}
$db = FronkDB::singleton();
$where = self::getSqlFilter($filter);
$sql = "SELECT COUNT(*) as cnt
FROM `".FRONKDB_DBNAME."`.Preorder tt_preorder
LEFT JOIN `".FRONKDB_DBNAME."`.Preorderstatus tt_preorderstatus ON tt_preorder.status_id = tt_preorderstatus.id
LEFT JOIN `".ADDRESSDB_DBNAME."`.view_hausnummer as adb_hausnummer ON (tt_preorder.adb_hausnummer_id = adb_hausnummer.hausnummer_id)
INNER JOIN `".FRONKDB_DBNAME."`.PreorderHistory ph ON tt_preorder.id = ph.preorder_id
WHERE $where AND ph.`key` like 'status_id' AND tt_preorderstatus.code = ".$status_code;
$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;

View File

@@ -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;
}

View File

@@ -2,6 +2,10 @@
user-select: none;
}
#topnav {
z-index: 99999;
}
.dashboard-container {
width: 100%;
max-width: 600px;

View File

@@ -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', {
<div class="dashboard-chart">
<tt-timeline-chart
v-if="dashboardData.timeline.length > 0"
:chartData="dashboardData.timeline[0]"
:datasets="[
{
label: 'Bestellungen',
data: dashboardData.timeline[0],
color: 'rgb(75, 192, 192)',
fill: true,
yAxisID: 'y',
order: 3
},
{
label: 'Leerrohr',
data: dashboardData.timeline_leerrohr[0],
color: 'rgb(255, 99, 132)',
fill: false,
yAxisID: 'y',
order: 2
},
{
label: 'ONT installiert',
data: dashboardData.timeline_ont_installed[0],
color: 'rgb(54, 162, 235)',
fill: false,
yAxisID: 'y',
order: 1
}
]"
label="KW"
dataColor="rgb(75, 192, 192)"
:fillColor="true"
:showGrid="false"
/>
</div>
</div>
</tt-card>