Commit 6068d9e3 authored by Gezer's avatar Gezer
Browse files

modified frontend so it looks great and works

parent 3dd20147
...@@ -3,12 +3,14 @@ import NavBar from './components/NavBar.vue'; ...@@ -3,12 +3,14 @@ import NavBar from './components/NavBar.vue';
</script> </script>
<template> <template>
<NavBar></NavBar>
<main class="content">
<NavBar />
</main>
</template> </template>
<style scoped> <style scoped>
header { .content {
line-height: 1.5; padding-top: 100px; /* Platz für fixierten Header (60px Logo + Padding) */
max-height: 100vh;
} }
</style> </style>
frontend/src/assets/bau-1.png

1.25 MB | W: | H:

frontend/src/assets/bau-1.png

2.15 MB | W: | H:

frontend/src/assets/bau-1.png
frontend/src/assets/bau-1.png
frontend/src/assets/bau-1.png
frontend/src/assets/bau-1.png
  • 2-up
  • Swipe
  • Onion skin
<template> <template>
<div class="chart-card"> <div class="chart-card">
<h3>{{ title }}</h3> <h3>{{ title }}</h3>
<Line :data="chartData" :options="chartOptions" /> <Line :data="chartData" :options="chartOptions" />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue'
import type { PropType } from 'vue'; import type { PropType } from 'vue'
import { import {
Chart as ChartJS, Chart as ChartJS,
Title, Title,
Tooltip, Tooltip,
Legend, Legend,
LineElement, LineElement,
PointElement, PointElement,
CategoryScale, CategoryScale,
LinearScale LinearScale
} from 'chart.js'; } from 'chart.js'
import { Line } from 'vue-chartjs'; import { Line } from 'vue-chartjs'
import type { ChartData, ChartOptions } from 'chart.js'; import type { ChartData, ChartOptions } from 'chart.js'
// ⛳️ WICHTIG: Registrierung außerhalb von defineComponent ChartJS.register(
ChartJS.register( Title,
Title, Tooltip,
Tooltip, Legend,
Legend, LineElement,
LineElement, PointElement,
PointElement, CategoryScale,
CategoryScale, LinearScale
LinearScale )
);
export default defineComponent({
export default defineComponent({ name: 'ChartCard',
name: 'ChartCard', components: { Line },
components: { props: {
Line title: { type: String, required: true },
}, labels: { type: Array as PropType<string[]>, required: true },
props: { data: { type: Array as PropType<number[]>, required: true },
title: { borderColor: { type: String, required: true }
type: String, },
required: true computed: {
}, chartData(): ChartData<'line'> {
labels: { return {
type: Array as PropType<string[]>, labels: this.labels,
required: true datasets: [
}, {
data: { label: this.title,
type: Array as PropType<number[]>, data: this.data,
required: true borderColor: this.borderColor,
}, backgroundColor: this.borderColor + '33',
borderColor: { fill: true,
type: String, tension: 0.3
required: true }
]
} }
}, },
computed: { chartOptions(): ChartOptions<'line'> {
chartData(): ChartData<'line'> { return {
return { responsive: true,
labels: this.labels, maintainAspectRatio: false,
datasets: [ plugins: {
{ legend: { position: 'top' },
label: this.title, title: { display: false }
data: this.data, },
fill: false, scales: {
borderColor: this.borderColor, y: { beginAtZero: true }
tension: 0.3 }
}
]
};
},
chartOptions(): ChartOptions<'line'> {
return {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
};
} }
} }
});
</script>
<style scoped>
.chart-card {
width: 800px;
height: 750px;
background: white;
padding: 1rem;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.chart-card h3 {
text-align: center;
margin-bottom: 0.5rem;
font-size: 1rem;
} }
</style> })
</script>
\ No newline at end of file
<style scoped>
.chart-card {
width: 1000px;
height: 500px;
background: white;
padding: 1.5rem;
border-radius: 10px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
}
.chart-card h3 {
text-align: center;
margin-bottom: 1rem;
font-size: 1.25rem;
font-weight: 600;
}
</style>
...@@ -9,10 +9,8 @@ import { RouterLink, RouterView } from 'vue-router' ...@@ -9,10 +9,8 @@ import { RouterLink, RouterView } from 'vue-router'
<nav> <nav>
<RouterLink to="/">Home</RouterLink> <RouterLink to="/">Home</RouterLink>
<RouterLink to="/login">Login</RouterLink> <RouterLink to="/login">Login</RouterLink>
<RouterLink to="/about">About</RouterLink>
<RouterLink to="/buildingsView">Gebäude</RouterLink>
<RouterLink to="/charts">Charts</RouterLink>
<RouterLink to="/register">Registrieren</RouterLink> <RouterLink to="/register">Registrieren</RouterLink>
<RouterLink to="/buildingsView">Gebäude</RouterLink>
</nav> </nav>
</div> </div>
</header> </header>
......
...@@ -8,25 +8,25 @@ const buildings = [ ...@@ -8,25 +8,25 @@ const buildings = [
id: '1', id: '1',
title: 'Bau 1', title: 'Bau 1',
description: 'Zentralgebäude mit Laboren', description: 'Zentralgebäude mit Laboren',
image: '../assets/bau-1.png', image: new URL('@/assets/bau-1.png', import.meta.url).href,
}, },
{ {
id: '2', id: '2',
title: 'Bau 2', title: 'Bau 2',
description: 'Informatik und IT-Räume', description: 'Informatik und IT-Räume',
image: '/images/building2.png', image: new URL('@/assets/bau-2.png', import.meta.url).href,
}, },
{ {
id: '3', id: '3',
title: 'Bau 3', title: 'Bau 3',
description: 'Informatik und IT-Räume', description: 'Informatik und IT-Räume',
image: '/images/building2.png', image: new URL('@/assets/bau-3.png', import.meta.url).href,
}, },
{ {
id: '8', id: '8',
title: 'Bau 8', title: 'Bau 8',
description: 'Informatik und IT-Räume', description: 'Informatik und IT-Räume',
image: '/images/building2.png', image: new URL('@/assets/bau-8.png', import.meta.url).href,
} }
] ]
...@@ -90,19 +90,20 @@ async function loadRooms(buildingId: string) { ...@@ -90,19 +90,20 @@ async function loadRooms(buildingId: string) {
display: flex; display: flex;
padding: 2rem; padding: 2rem;
gap: 2rem; gap: 2rem;
align-items: flex-start; /* wichtige Ergänzung */
} }
.building-grid { .building-grid {
display: grid; display: flex;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); flex-direction: column; /* Gebäude untereinander */
gap: 1.5rem; gap: 1.5rem;
flex: 2; flex-shrink: 0;
} }
.room-panel { .room-panel {
flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
flex-grow: 1;
} }
</style> </style>
...@@ -2,16 +2,38 @@ ...@@ -2,16 +2,38 @@
<div class="chart-view"> <div class="chart-view">
<h2>Daten für Raum: {{ room }}</h2> <h2>Daten für Raum: {{ room }}</h2>
<ChartCard <div class="time-form">
title="CO₂ Verlauf" <label>
:labels="labels" Start:
:data="co2Data" <input class="datetime" type="datetime-local" v-model="start" />
borderColor="#ff6384" </label>
/> <label>
Stop:
<!-- Debug-Ausgabe --> <input class="datetime" type="datetime-local" v-model="stop" />
<pre>Labels: {{ labels }}</pre> </label>
<pre>CO₂: {{ co2Data }}</pre> <button @click="loadData">Zeitraum aktualisieren</button>
</div>
<div class="chart-grid">
<ChartCard
title="CO₂ Verlauf"
:labels="labels"
:data="co2Data"
borderColor="#ff6384"
/>
<ChartCard
title="Temperatur Verlauf"
:labels="labels"
:data="temperatureData"
borderColor="#36a2eb"
/>
<ChartCard
title="Luftfeuchtigkeit Verlauf"
:labels="labels"
:data="humidityData"
borderColor="#4bc0c0"
/>
</div>
</div> </div>
</template> </template>
...@@ -29,36 +51,83 @@ export default defineComponent({ ...@@ -29,36 +51,83 @@ export default defineComponent({
const labels = ref<string[]>([]) const labels = ref<string[]>([])
const co2Data = ref<number[]>([]) const co2Data = ref<number[]>([])
const temperatureData = ref<number[]>([])
const humidityData = ref<number[]>([])
onMounted(async () => { const start = ref('')
const stop = ref('')
function toISOStringSafe(value: string): string {
try { try {
const url = `http://localhost:8000/api/room_data_range?room=${encodeURIComponent(room)}&start=-7d&stop=now()` return new Date(value).toISOString()
const response = await fetch(url) } catch {
return ''
}
}
async function loadData() {
if (!start.value || !stop.value) return
const startISO = toISOStringSafe(start.value)
const stopISO = toISOStringSafe(stop.value)
if (!response.ok) { if (new Date(startISO) >= new Date(stopISO)) {
throw new Error(`HTTP-Fehler: ${response.status}`) alert('❌ Der Startzeitpunkt muss vor dem Endzeitpunkt liegen.')
} return
}
const url = `http://localhost:8000/api/room_data_range?room=${encodeURIComponent(
room
)}&start=${startISO}&stop=${stopISO}`
try {
const response = await fetch(url)
if (!response.ok) throw new Error('Fehler beim Laden')
const json = await response.json() const json = await response.json()
const entries = Object.entries(json.data).sort( const entries = Object.entries(json.data).sort(
([a], [b]) => new Date(a).getTime() - new Date(b).getTime() ([a], [b]) => new Date(a).getTime() - new Date(b).getTime()
) )
const labelsArray = entries.map(([timestamp]) => labels.value = entries.map(([timestamp]) =>
new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) new Date(timestamp).toLocaleString([], {
day: '2-digit',
month: '2-digit',
hour: '2-digit',
minute: '2-digit',
})
)
co2Data.value = entries.map(([, values]: any) => Number(values.co2))
temperatureData.value = entries.map(([, values]: any) =>
Number(values.temperature)
)
humidityData.value = entries.map(([, values]: any) =>
Number(values.humidity)
) )
const co2DataArray = entries.map(([, values]) => values.co2)
labels.value = labelsArray
co2Data.value = co2DataArray
} catch (err) { } catch (err) {
console.error('❌ Fehler beim Laden der Daten:', err) console.error('❌ Fehler beim Laden der Daten:', err)
} }
}
onMounted(async () => {
const now = new Date()
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
start.value = weekAgo.toISOString().slice(0, 16)
stop.value = now.toISOString().slice(0, 16)
await loadData()
}) })
return { room, labels, co2Data } return {
} room,
start,
stop,
labels,
co2Data,
temperatureData,
humidityData,
loadData,
}
},
}) })
</script> </script>
...@@ -66,4 +135,41 @@ export default defineComponent({ ...@@ -66,4 +135,41 @@ export default defineComponent({
.chart-view { .chart-view {
padding: 2rem; padding: 2rem;
} }
.time-form {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
align-items: center;
flex-wrap: wrap;
}
.time-form input {
padding: 0.5rem;
font-size: 1rem;
border-radius: 5px;
}
.time-form button {
background: #007bff;
color: white;
padding: 0.6rem 1.2rem;
border: none;
border-radius: 4px;
font-weight: bold;
cursor: pointer;
transition: background-color 0.2s ease;
}
.time-form button:hover {
background-color: #0056b3;
}
.chart-grid {
display: flex;
flex-wrap: nowrap;
gap: 2rem;
justify-content: flex-start;
flex-direction: row;
}
</style> </style>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment