some polishing

main
Giò Diani 2025-01-15 16:57:18 +01:00
parent 959b84d1e1
commit 3290c1cce3
6 changed files with 185 additions and 83 deletions

View File

@ -5,7 +5,7 @@ namespace App;
class Chart class Chart
{ {
public static function colors(int $count = 5){ public static function colors(int $count = 5){
$colors = ['#bfd3e6','#8c96c6','#88419d','#810f7c','#4d004b']; $colors = ['#9ebcda','#8c96c6','#88419d','#810f7c','#4d004b'];
return json_encode($colors); return json_encode($colors);
} }

View File

@ -54,10 +54,19 @@ dd + dt{
margin-top: .2em; margin-top: .2em;
} }
nav + button,
span + button{ span + button{
margin-left: .5em; margin-left: .5em;
} }
ul{
padding-left: 1em;
}
p + ul{
margin-top: 1em;
}
button[popovertarget]{ button[popovertarget]{
background: no-repeat center / .3em #4d004b url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 512'%3E%3C!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--%3E%3Cpath fill='%23fff' d='M48 80a48 48 0 1 1 96 0A48 48 0 1 1 48 80zM0 224c0-17.7 14.3-32 32-32l64 0c17.7 0 32 14.3 32 32l0 224 32 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 512c-17.7 0-32-14.3-32-32s14.3-32 32-32l32 0 0-192-32 0c-17.7 0-32-14.3-32-32z'/%3E%3C/svg%3E%0A"); background: no-repeat center / .3em #4d004b url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 512'%3E%3C!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--%3E%3Cpath fill='%23fff' d='M48 80a48 48 0 1 1 96 0A48 48 0 1 1 48 80zM0 224c0-17.7 14.3-32 32-32l64 0c17.7 0 32 14.3 32 32l0 224 32 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 512c-17.7 0-32-14.3-32-32s14.3-32 32-32l32 0 0-192-32 0c-17.7 0-32-14.3-32-32z'/%3E%3C/svg%3E%0A");
cursor: pointer; cursor: pointer;
@ -95,6 +104,10 @@ button[popovertarget]>span{
background-color: rgba(0,0,0,.5); background-color: rgba(0,0,0,.5);
} }
[popover] h2{
font-size: 1em;
}
/* /*
9. Create a root stacking context 9. Create a root stacking context
@ -118,8 +131,7 @@ body>header{
body>header>nav{ body>header>nav{
text-align: center; text-align: center;
max-width: 10em; min-width: 10em;
width: 100%;
background: #fff; background: #fff;
border-radius: .2em; border-radius: .2em;
position: relative; position: relative;
@ -174,25 +186,28 @@ body.overview main{
"chart3 chart3 chart3 chart2 chart2 chart2 chart4 chart4" "chart3 chart3 chart3 chart2 chart2 chart2 chart4 chart4"
} }
body.property main{ body.region main{
grid-template-columns: repeat(4, minmax(10%, 50%)); grid-template-columns: repeat(4, minmax(10%, 50%));
grid-template-rows: repeat(3, 1fr) 4em; grid-template-rows: repeat(6, 1fr) 4em;
grid-template-areas: grid-template-areas:
"chart2 chart2 chart1 chart1" "chart1 chart1 chart2 chart2"
"chart5 chart5 chart1 chart1" "chart1 chart1 chart2 chart2"
"chart5 chart5 chart3 chart4" "chart1 chart1 chart3 chart4"
"chart5 chart5 timeline timeline"; "chart1 chart1 chart3 chart4"
"chart1 chart1 chart6 chart6"
"chart1 chart1 chart6 chart6"
"chart1 chart1 timeline timeline";
} }
body.region main{ body.property main{
grid-template-columns: repeat(4, minmax(10%, 50%)); grid-template-columns: repeat(4, minmax(10%, 50%));
grid-template-rows: repeat(4, 1fr) 4em; grid-template-rows: repeat(4, 1fr) 4em;
grid-template-areas: grid-template-areas:
"chart1 chart1 chart2 chart2" "chart2 chart2 chart1 chart1"
"chart1 chart1 chart6 chart6" "chart2 chart2 chart1 chart1"
"chart1 chart1 chart3 chart4" "chart5 chart5 chart3 chart4"
"chart1 chart1 chart3 chart4" "chart5 chart5 chart3 chart4"
"chart1 chart1 timeline timeline"; "chart5 chart5 timeline timeline";
} }
article{ article{
@ -205,7 +220,7 @@ article{
article.header{ article.header{
grid-template-columns: 100%; grid-template-columns: 100%;
grid-template-rows: minmax(1%, 2.5em) 1fr; grid-template-rows: minmax(1%, 2em) 1fr;
padding: .5em 1em 1em .5em; padding: .5em 1em 1em .5em;
} }

View File

@ -11,56 +11,89 @@
</nav> </nav>
@endsection @endsection
@section('main') @section('main')
<article class="header" style="grid-area: chart3;">
<header>
<h2>Auslastung aller Mietobjekte über Gesamte Zeit</h2>
<button popovertarget="pop1">
<span>Erklärungen zum Diagramm</span>
</button>
<div popover id="pop1">
<h2>Auslastung aller Mietobjekte über Gesamte Zeit</h2>
<p>
Das Diagramm gibt eine Übersicht, wie die Auslastung von Mietobjekten am Datum des Scrapings waren. Dazu wird für jedes Mietobjekt die durchschnittliche Verfügbarkeit ermittelt.
</p>
<ul>
<li>X-Achse: Zeitpunkt Scraping.</li>
<li>Y-Achse: Mietobjekte.</li>
<li>Kategorien: 0% = Das Mietobjekt ist komplett verfügbar; 100% = Das Mietobjekt ist komplett ausgebucht.</li>
</ul>
</div>
<div>
</header>
<div id="chart-heatmap"></div>
</article>
<article class="header" style="grid-area: chart1;"> <article class="header" style="grid-area: chart1;">
<header> <header>
<h2> <h2>
Anzahl jemals gefundene Kurzzeitmietobjekte pro Region Anzahl jemals gefundene Kurzzeitmietobjekte pro Region
</h2> </h2>
<button popovertarget="pop1"> <button popovertarget="pop2">
<span>Erklärungen zum Diagramm</span> <span>Erklärungen zum Diagramm</span>
</button> </button>
<div popover id="pop1"> <div popover id="pop2">
<p>Das Diagram zeigt...</p> <h2>Anzahl jemals gefundene Kurzzeitmietobjekte pro Region</h2>
</div> <p>
<div> Das Balkendiagramm zeigt wieviele Kurzzeitmietobjekte insgesamt pro Region über den gesamten Datenerhebungszeitraum, gefunden wurden.
</header> </p>
<div id="chart-props-per-region"></div> <ul>
<li>X-Achse: Bezeichnung der Region.</li>
<li>Y-Achse: Anzahl Mietobjekte.</li>
</ul>
</div>
<div>
</header>
<div id="chart-props-per-region"></div>
</article> </article>
<article class="header" style="grid-area: chart2;"> <article class="header" style="grid-area: chart2;">
<header> <header>
<h2> <h2>
Entwicklung der Anzahl jemals gefunden Kurzzeitmietobjekte Entwicklung der Anzahl jemals gefunden Kurzzeitmietobjekte
</h2> </h2>
</header> <button popovertarget="pop3">
<div id="extractions"></div> <span>Erklärungen zum Diagramm</span>
</button>
<div popover id="pop3">
<h2>Entwicklung Anzahl jemals gefundener Kurzzeitmietobjekte pro Region</h2>
<p>
Das Liniendiagramm zeigt die Entwicklung der gefundenen Mietobjekte pro Region.
</p>
<ul>
<li>X-Achse: Zeitpunkt Scraping.</li>
<li>Y-Achse: Anzahl Mietobjekte.</li>
</ul>
</div>
<div>
</header>
<div id="extractions"></div>
</article> </article>
<article style="grid-area: chart4;"> <article style="grid-area: chart4;">
<div id="leaflet"></div> <div id="leaflet"></div>
</article>
<article class="header" style="grid-area: chart3;">
<header>
<h2>
Gesamtauslastung
</h2>
</header>
<div id="chart-heatmap"></div>
</article> </article>
<script type="module"> <script type="module">
const sharedOptions = { const sharedOptions = {
basic: { basic: {
color: {!! $chartOptions['colors'] !!}, color: {!! $chartOptions['colors'] !!},
grid: { grid: {
top: 20, top: 30,
left: 60, left: 70,
right: 0, right: 0,
bottom: 50 bottom: 45
}, },
name: (opt) => { name: (opt) => {
return { return {
name: opt.name, name: opt.name,
nameLocation: opt.location, nameLocation: opt.location,
nameGap: 24, nameGap: 50,
nameTextStyle: { nameTextStyle: {
fontWeight: 'bold', fontWeight: 'bold',
}, },
@ -160,7 +193,7 @@ const cPropsPerRegionOptions = {
xAxis: { xAxis: {
name: 'Region', name: 'Region',
nameLocation: 'center', nameLocation: 'center',
nameGap: 24, nameGap: 30,
nameTextStyle: { nameTextStyle: {
fontWeight: 'bold', fontWeight: 'bold',
}, },
@ -169,9 +202,9 @@ const cPropsPerRegionOptions = {
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
name: 'Anzahl Kurzzeitmietobjekte', name: 'Anzahl Mietobjekte',
nameLocation: 'middle', nameLocation: 'middle',
nameGap: 38, nameGap: 50,
nameTextStyle: { nameTextStyle: {
fontWeight: 'bold', fontWeight: 'bold',
}, },
@ -219,9 +252,9 @@ const cExtractionsOptions = {
data: extractionDates data: extractionDates
}, },
yAxis: { yAxis: {
name: 'Anzahl Kurzzeitmietobjekte', name: 'Anzahl Mietobjekte',
nameLocation: 'center', nameLocation: 'center',
nameGap: 38, nameGap: 50,
nameTextStyle: { nameTextStyle: {
fontWeight: 'bold', fontWeight: 'bold',
}, },

View File

@ -1,7 +1,16 @@
@extends('base') @extends('base')
@section('body-class', 'property') @section('body-class', 'property')
@section('header') @section('header')
<span>Property {{ $base['property_platform_id'] }}</span><button popovertarget="prop-details"></button> <nav>
<strong>Property: {{ $base['check_data'] }}</strong>
<ul>
<li><a href="/">Start</a></li>
@foreach($regions as $r)
<li><a href="/region/{{ $r['region_id'] }}">{{ $r['region_name'] }}</a></li>
@endforeach
</ul>
</nav>
<button popovertarget="prop-details"></button>
<div popover id="prop-details"> <div popover id="prop-details">
<dl> <dl>
<dt>Region</dt> <dt>Region</dt>
@ -80,6 +89,11 @@ const sharedOptions = {
right: 0, right: 0,
bottom: 50 bottom: 50
}, },
tooltip: {
show: true,
trigger: 'axis',
valueFormatter: (value) => value.toFixed(2)+'%'
},
name: (opt) => { name: (opt) => {
return { return {
name: opt.name, name: opt.name,
@ -102,7 +116,7 @@ const cTimelineOptions = {
}, },
timeline: { timeline: {
data: {!! $extractiondates !!}, data: {!! $extractiondates !!},
playInterval: 2000, playInterval: 1000,
axisType: 'time', axisType: 'time',
left: 8, left: 8,
right: 8, right: 8,
@ -119,20 +133,27 @@ const chartCapacityMonthly = document.getElementById('chart-capacity-monthly');
const cCapacityMonthly = echarts.init(chartCapacityMonthly); const cCapacityMonthly = echarts.init(chartCapacityMonthly);
const cCapacityMonthlyOptions = { const cCapacityMonthlyOptions = {
tooltip: sharedOptions.basic.tooltip,
timeline: { timeline: {
show: false, show: false,
data: {!! $extractiondates !!}, data: {!! $extractiondates !!},
axisType: 'time', axisType: 'time',
}, },
grid: { grid: {
top: 0, top: 5,
bottom: 25, bottom: 40,
left: 70, left: 70,
right: 10 right: 10
}, },
xAxis: { xAxis: {
type: 'value', type: 'value',
max: 100 max: 100,
name: 'Auslastung in %',
nameLocation: 'center',
nameGap: 25,
nameTextStyle: {
fontWeight: 'bold',
}
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
@ -161,20 +182,27 @@ const chartCapacityDaily = document.getElementById('chart-capacity-daily');
const cCapacityDaily = echarts.init(chartCapacityDaily); const cCapacityDaily = echarts.init(chartCapacityDaily);
const cCapacityDailyOptions = { const cCapacityDailyOptions = {
tooltip: sharedOptions.basic.tooltip,
timeline: { timeline: {
show: false, show: false,
data: {!! $extractiondates !!}, data: {!! $extractiondates !!},
axisType: 'time', axisType: 'time',
}, },
grid: { grid: {
top: 0, top: 5,
bottom: 25, bottom: 40,
left: 70, left: 70,
right: 10 right: 10
}, },
xAxis: { xAxis: {
type: 'value', type: 'value',
max: 100 max: 100,
name: 'Auslastung in %',
nameLocation: 'center',
nameGap: 25,
nameTextStyle: {
fontWeight: 'bold',
}
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
@ -277,7 +305,7 @@ const cCalendarOptions = {
visualMap: { visualMap: {
categories: [0,1,2], categories: [0,1,2],
inRange: { inRange: {
color: ['#d95f02', '#7570b3', '#1b9e77'] color: ['#ca0020', '#92c5de', '#0571b0']
}, },
formatter: (cat) => { formatter: (cat) => {
switch (cat) { switch (cat) {

View File

@ -19,20 +19,20 @@
</article> </article>
<article class="header" style="grid-area: chart6;"> <article class="header" style="grid-area: chart6;">
<header> <header>
<h2 id="prediction-title">Gleitender Mittelwert für die Auslastung von {{ $region[0]['region_name'] }}</h2> <h2 id="prediction-title">Gleitender Mittelwert für die Auslastung der Region</h2>
</header> </header>
<div id="chart-prediction"></div> <div id="chart-prediction"></div>
</article> </article>
<article class="header" style="grid-area: chart1;"> <article class="header" style="grid-area: chart1;">
<header> <header>
<h2 id="belegung-title">Gesamtauslastung</h2> <h2 id="belegung-title">Auslastung aller Mietobjekte über Gesamte Zeit der Region</h2>
</header> </header>
<div id="chart-heatmap"></div> <div id="chart-heatmap"></div>
</article> </article>
<article class="header" style="grid-area: chart3;"> <article class="header" style="grid-area: chart3;">
<header> <header>
<h2> <h2>
Auslastung der Region nach Monat am <span class="date">{{ $startDate }}</span> Auslastung Region nach Monat am <span class="date">{{ $startDate }}</span>
</h2> </h2>
</header> </header>
<div id="chart-capacity-monthly"> <div id="chart-capacity-monthly">
@ -54,7 +54,7 @@
<article class="header" style="grid-area: chart4;"> <article class="header" style="grid-area: chart4;">
<header> <header>
<h2> <h2>
Auslastung der Region nach Wochentage am <span class="date">{{ $startDate }}</span> Auslastung Wochentage am <span class="date">{{ $startDate }}</span>
</h2> </h2>
</header> </header>
<div id="chart-capacity-daily"> <div id="chart-capacity-daily">
@ -71,6 +71,11 @@ const sharedOptions = {
right: 0, right: 0,
bottom: 50 bottom: 50
}, },
tooltip: {
show: true,
trigger: 'axis',
valueFormatter: (value) => value.toFixed(2)+'%'
},
name: (opt) => { name: (opt) => {
return { return {
name: opt.name, name: opt.name,
@ -89,12 +94,9 @@ const cCapacity = echarts.init(chartCapacity);
const cCapacityOptions = { const cCapacityOptions = {
legend: { legend: {
data: ['Auslastung Region', 'Auslastung alle Regionen'] show: true
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value.toFixed(2)+'%'
}, },
tooltip: sharedOptions.basic.tooltip,
color: sharedOptions.basic.color, color: sharedOptions.basic.color,
grid: { grid: {
top: 20, top: 20,
@ -151,18 +153,25 @@ const cCapacityMonthlyOptions = {
axisType: 'time', axisType: 'time',
}, },
grid: { grid: {
top: 0, top: 5,
bottom: 25, bottom: 40,
left: 70, left: 70,
right: 10 right: 10
}, },
xAxis: { xAxis: {
type: 'value', type: 'value',
max: 100 max: 100,
name: 'Auslastung in %',
nameLocation: 'center',
nameGap: 25,
nameTextStyle: {
fontWeight: 'bold',
}
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
}, },
tooltip: sharedOptions.basic.tooltip,
options: [ options: [
@foreach ($regionCapacities['region_monthly'] as $m) @foreach ($regionCapacities['region_monthly'] as $m)
{ {
@ -192,15 +201,22 @@ const cCapacityDailyOptions = {
data: {!! json_encode($regionCapacities['region']['dates']) !!}, data: {!! json_encode($regionCapacities['region']['dates']) !!},
axisType: 'time', axisType: 'time',
}, },
tooltip: sharedOptions.basic.tooltip,
grid: { grid: {
top: 0, top: 5,
bottom: 25, bottom: 40,
left: 70, left: 70,
right: 10 right: 10
}, },
xAxis: { xAxis: {
type: 'value', type: 'value',
max: 100 max: 100,
name: 'Auslastung in %',
nameLocation: 'center',
nameGap: 25,
nameTextStyle: {
fontWeight: 'bold',
}
}, },
yAxis: { yAxis: {
type: 'category', type: 'category',
@ -229,6 +245,7 @@ const chartPrediction = document.getElementById('chart-prediction');
const cPrediction = echarts.init(chartPrediction); const cPrediction = echarts.init(chartPrediction);
const cPredictionOptions = { const cPredictionOptions = {
color: sharedOptions.basic.color,
timeline: { timeline: {
show: false, show: false,
data: {!! json_encode($regionCapacities['region']['dates']) !!}, data: {!! json_encode($regionCapacities['region']['dates']) !!},
@ -238,10 +255,7 @@ const cPredictionOptions = {
legend: { legend: {
show: true show: true
}, },
tooltip: { tooltip: sharedOptions.basic.tooltip,
trigger: 'axis',
valueFormatter: (value) => value.toFixed(2)+'%'
},
grid: { grid: {
top: 20, top: 20,
left: 25, left: 25,
@ -468,6 +482,16 @@ cTimeline.on('timelinechanged', (e) => {
}) })
document.querySelector('header').addEventListener('click', () => {
console.log('test');
cCapacityMonthly.dispatchAction({
type: 'timelineChange',
currentIndex: 10
});
})
cCapacity.on('click', 'series', (e) => { cCapacity.on('click', 'series', (e) => {
// Switch to correct calendar in the timeline // Switch to correct calendar in the timeline

View File

@ -86,7 +86,8 @@ Route::get('/property/{id}', function (int $id) {
$chartOptions = [ $chartOptions = [
'colors' => Chart::colors() 'colors' => Chart::colors()
]; ];
$regionBaseAll = Api::regionBase(-1);
$regionBaseAll[] = ['region_name' => 'Alle Regionen', 'region_id' => -1];
$propertyBase = Api::propertyBase($id); $propertyBase = Api::propertyBase($id);
$calendars = Api::propertyExtractions($id); $calendars = Api::propertyExtractions($id);
$propertyCapacities = Api::propertyCapacities($id); $propertyCapacities = Api::propertyCapacities($id);
@ -137,6 +138,7 @@ Route::get('/property/{id}', function (int $id) {
'chartOptions' => $chartOptions, 'chartOptions' => $chartOptions,
'startDate' => $propertyCapacities['dates'][0], 'startDate' => $propertyCapacities['dates'][0],
'base' => $propertyBase[0], 'base' => $propertyBase[0],
'regions' => $regionBaseAll,
'extractiondates' => json_encode($propertyCapacities['dates']), 'extractiondates' => json_encode($propertyCapacities['dates']),
'calendar' => $data, 'calendar' => $data,
'propertyCapacities' => $propertyCapacities, 'propertyCapacities' => $propertyCapacities,