Refactor dashboard layout and update sensor table columns

- Make metric cards and charts more compact and consistent - Change
SensorConsumptionTable columns: show Room and Value, remove
Current/Total/Average - Update headings and layout for AnalyticsView and
HomeView - Improve responsiveness and spacing for cards and sections
This commit is contained in:
rafaeldpsilva
2025-10-03 15:06:34 +01:00
parent 3ecd0ab2c4
commit 9a25170b27
5 changed files with 307 additions and 333 deletions

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="bg-white rounded-2xl shadow-sm flex flex-col justify-between h-full w-full p-4"> <div class="bg-white rounded-2xl shadow-sm flex flex-col justify-between aspect-square p-4">
<h6 class="text-sm font-bold text-gray-500">{{ title }}</h6> <h6 class="text-sm font-bold text-gray-500">{{ title }}</h6>
<div class="flex-grow flex items-center justify-start"> <div class="flex-grow flex items-center justify-start">
<p class="text-gray-900 font-bold text-2xl"> <p class="text-gray-900 font-bold text-2xl">

View File

@@ -1,8 +1,8 @@
<template> <template>
<div class="bg-white rounded-2xl shadow-sm flex flex-col h-full min-h-[300px]"> <div class="bg-white rounded-2xl shadow-sm flex flex-col p-4">
<div class="p-4 h-full"> <h6 class="text-sm font-bold text-gray-500 mb-2">{{ title }}</h6>
<h6 class="text-sm font-bold text-gray-500 mb-2">{{ title }}</h6> <div class="w-full h-[400px]">
<v-chart class="h-64 w-full" :option="option" autoresize /> <v-chart class="w-full h-full" :option="option" autoresize />
</div> </div>
</div> </div>
</template> </template>

View File

@@ -1,7 +1,6 @@
<template> <template>
<div class="bg-white rounded-2xl shadow-sm p-4"> <div class="bg-white rounded-2xl shadow-sm p-4">
<h6 class="text-sm font-bold text-gray-500 mb-4">Sensor Consumption</h6> <h6 class="text-sm font-bold text-gray-500 mb-4">Sensor Readings</h6>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="min-w-full"> <table class="min-w-full">
<thead> <thead>
@@ -10,13 +9,10 @@
Sensor ID Sensor ID
</th> </th>
<th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3"> <th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3">
Current Room
</th> </th>
<th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3"> <th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3">
Total Value
</th>
<th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3">
Average
</th> </th>
<th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3"> <th class="text-right text-xs font-medium text-gray-500 uppercase tracking-wider py-3">
Last Updated Last Updated
@@ -55,9 +51,6 @@
sensor.humidity?.unit sensor.humidity?.unit
}} }}
</td> </td>
<td class="py-3 text-sm text-gray-600 text-right">
{{ sensor.room }}
</td>
<td class="py-3 text-sm text-gray-500 text-right"> <td class="py-3 text-sm text-gray-500 text-right">
{{ formatTime(sensor.timestamp) }} {{ formatTime(sensor.timestamp) }}
</td> </td>
@@ -66,7 +59,6 @@
</table> </table>
</div> </div>
<!-- Connection Status Indicator -->
<div class="mt-4 flex items-center justify-between text-xs text-gray-500"> <div class="mt-4 flex items-center justify-between text-xs text-gray-500">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div <div

View File

@@ -1,262 +1,21 @@
<template> <template>
<div class="min-h-screen bg-gray-50"> <div class="space-y-6">
<div class="px-4 py-6 mx-auto max-w-7xl sm:px-6 lg:px-8"> <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
<div class="mb-8"> <div>
<h1 class="text-3xl font-bold text-gray-900">API Dashboard</h1> <h1 class="text-2xl font-bold text-gray-900">Analytics</h1>
<p class="text-gray-600 mt-2">Real-time data from backend APIs</p> <p class="text-gray-600">Manage sensors, assign rooms, and control device actions</p>
</div> </div>
</div>
<!-- API Status Section --> <div class="mb-8">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8"> <h1 class="text-3xl font-bold text-gray-900">API Dashboard</h1>
<div class="bg-white rounded-lg shadow p-6"> <p class="text-gray-600 mt-2">Real-time data from backend APIs</p>
<div class="flex items-center"> </div>
<div class="p-2 bg-blue-100 rounded-lg"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
<svg
class="w-6 h-6 text-blue-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">System Status</p>
<p
class="text-lg font-semibold"
:class="healthStatus?.status === 'healthy' ? 'text-green-600' : 'text-red-600'"
>
{{ healthStatus?.status || 'Unknown' }}
</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-2 bg-green-100 rounded-lg">
<svg
class="w-6 h-6 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 10V3L4 14h7v7l9-11h-7z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">Total Sensors</p>
<p class="text-lg font-semibold text-gray-900">{{ sensorStore.totalSensors }}</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-2 bg-yellow-100 rounded-lg">
<svg
class="w-6 h-6 text-yellow-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">Active Sensors</p>
<p class="text-lg font-semibold text-gray-900">{{ sensorStore.activeSensors }}</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-2 bg-purple-100 rounded-lg">
<svg
class="w-6 h-6 text-purple-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">Total Readings</p>
<p class="text-lg font-semibold text-gray-900">
{{ formatNumber(sensorStore.totalReadings) }}
</p>
</div>
</div>
</div>
</div>
<!-- Loading States -->
<div v-if="isLoading" class="bg-white rounded-lg shadow p-6 mb-8">
<div class="flex items-center justify-center">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<p class="ml-3 text-gray-600">Loading API data...</p>
</div>
</div>
<!-- Error States -->
<div v-if="apiError" class="bg-red-50 border border-red-200 rounded-lg p-4 mb-8">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800">API Error</h3>
<p class="mt-1 text-sm text-red-700">{{ apiError }}</p>
</div>
</div>
</div>
<!-- Energy Metrics Summary -->
<div class="bg-white rounded-lg shadow p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-900 mb-4">Energy Metrics</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<p class="text-sm font-medium text-gray-600 mb-2">Current Energy Consumption</p>
<p class="text-2xl font-bold text-blue-600">
{{ energyStore.currentEnergyValue.toFixed(2) }} kWh
</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 mb-2">Average Energy Usage</p>
<p class="text-2xl font-bold text-green-600">
{{ energyStore.averageEnergyUsage.toFixed(2) }} kWh
</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 mb-2">Total Consumption</p>
<p class="text-2xl font-bold text-purple-600">
{{ energyStore.currentConsumption.toFixed(2) }} kWh
</p>
<p class="text-xs text-gray-500 mt-1">Cumulative</p>
</div>
</div>
</div>
<!-- Sensors Section -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
<div class="bg-white rounded-lg shadow">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">API Sensors</h2>
</div>
<div class="p-6">
<div v-if="apiSensors.length === 0" class="text-center text-gray-500 py-8">
No sensors found from API
</div>
<div v-else class="space-y-3">
<div
v-for="sensor in apiSensors"
:key="sensor.sensor_id"
class="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
>
<div>
<p class="font-medium text-gray-900">{{ sensor.sensor_id }}</p>
<p class="text-sm text-gray-500">{{ sensor.room || 'No room assigned' }}</p>
<p class="text-xs text-gray-400">
{{ sensor.sensor_type }} {{ sensor.total_readings }} readings
</p>
</div>
<div class="flex items-center">
<span
:class="[
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
sensor.status === 'online'
? 'bg-green-100 text-green-800'
: sensor.status === 'offline'
? 'bg-red-100 text-red-800'
: 'bg-yellow-100 text-yellow-800',
]"
>
{{ sensor.status }}
</span>
</div>
</div>
</div>
</div>
</div>
<!-- Rooms Section -->
<div class="bg-white rounded-lg shadow">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">API Rooms</h2>
</div>
<div class="p-6">
<div v-if="apiRooms.length === 0" class="text-center text-gray-500 py-8">
No rooms found from API
</div>
<div v-else class="space-y-3">
<div
v-for="room in apiRooms"
:key="room.name || room.room"
class="p-3 bg-gray-50 rounded-lg"
>
<div class="flex items-center justify-between mb-2">
<p class="font-medium text-gray-900">{{ room.name || room.room }}</p>
<span class="text-sm text-gray-500">{{ room.sensor_count }} sensors</span>
</div>
<div class="text-xs text-gray-600 space-y-1">
<p v-if="room.sensor_types">Types: {{ room.sensor_types.join(', ') }}</p>
<div v-if="room.latest_metrics">
<span v-if="room.latest_metrics.energy" class="mr-4">
Energy: {{ room.latest_metrics.energy.current }}
{{ room.latest_metrics.energy.unit }}
</span>
<span v-if="room.latest_metrics.co2">
CO2: {{ room.latest_metrics.co2.current }} {{ room.latest_metrics.co2.unit }}
</span>
</div>
<p v-else class="text-gray-400 italic">No metrics available</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="bg-white rounded-lg shadow p-6"> <div class="bg-white rounded-lg shadow p-6">
<h2 class="text-lg font-semibold text-gray-900 mb-4">API Actions</h2> <div class="flex items-center">
<div class="flex flex-wrap gap-3"> <div class="p-2 bg-blue-100 rounded-lg">
<button
@click="refreshAllData"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
<svg <svg
v-if="!isLoading" class="w-6 h-6 text-blue-600"
class="-ml-1 mr-2 h-4 w-4"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -265,41 +24,284 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
stroke-width="2" stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
></path> ></path>
</svg> </svg>
<div </div>
v-else <div class="ml-4">
class="animate-spin -ml-1 mr-2 h-4 w-4 border-2 border-white border-t-transparent rounded-full" <p class="text-sm font-medium text-gray-600">System Status</p>
></div> <p
Refresh All Data class="text-lg font-semibold"
</button> :class="healthStatus?.status === 'healthy' ? 'text-green-600' : 'text-red-600'"
>
<button {{ healthStatus?.status || 'Unknown' }}
@click="fetchSensorsOnly" </p>
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" </div>
:disabled="isLoading"
>
Fetch Sensors
</button>
<button
@click="fetchRoomsOnly"
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
Fetch Rooms
</button>
<button
@click="fetchAnalyticsOnly"
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
Fetch Analytics
</button>
</div> </div>
</div> </div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-2 bg-green-100 rounded-lg">
<svg
class="w-6 h-6 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 10V3L4 14h7v7l9-11h-7z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">Total Sensors</p>
<p class="text-lg font-semibold text-gray-900">{{ sensorStore.totalSensors }}</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-2 bg-yellow-100 rounded-lg">
<svg
class="w-6 h-6 text-yellow-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">Active Sensors</p>
<p class="text-lg font-semibold text-gray-900">{{ sensorStore.activeSensors }}</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="p-2 bg-purple-100 rounded-lg">
<svg
class="w-6 h-6 text-purple-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
></path>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">Total Readings</p>
<p class="text-lg font-semibold text-gray-900">
{{ formatNumber(sensorStore.totalReadings) }}
</p>
</div>
</div>
</div>
</div>
<!-- Loading States -->
<div v-if="isLoading" class="bg-white rounded-lg shadow p-6 mb-8">
<div class="flex items-center justify-center">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<p class="ml-3 text-gray-600">Loading API data...</p>
</div>
</div>
<!-- Error States -->
<div v-if="apiError" class="bg-red-50 border border-red-200 rounded-lg p-4 mb-8">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800">API Error</h3>
<p class="mt-1 text-sm text-red-700">{{ apiError }}</p>
</div>
</div>
</div>
<!-- Energy Metrics Summary -->
<div class="bg-white rounded-lg shadow p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-900 mb-4">Energy Metrics</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<p class="text-sm font-medium text-gray-600 mb-2">Current Energy Consumption</p>
<p class="text-2xl font-bold text-blue-600">
{{ energyStore.currentEnergyValue.toFixed(2) }} kWh
</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 mb-2">Average Energy Usage</p>
<p class="text-2xl font-bold text-green-600">
{{ energyStore.averageEnergyUsage.toFixed(2) }} kWh
</p>
</div>
<div>
<p class="text-sm font-medium text-gray-600 mb-2">Total Consumption</p>
<p class="text-2xl font-bold text-purple-600">
{{ energyStore.currentConsumption.toFixed(2) }} kWh
</p>
<p class="text-xs text-gray-500 mt-1">Cumulative</p>
</div>
</div>
</div>
<!-- Sensors Section -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
<div class="bg-white rounded-lg shadow">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">API Sensors</h2>
</div>
<div class="p-6">
<div v-if="apiSensors.length === 0" class="text-center text-gray-500 py-8">
No sensors found from API
</div>
<div v-else class="space-y-3">
<div
v-for="sensor in apiSensors"
:key="sensor.sensor_id"
class="flex items-center justify-between p-3 bg-gray-50 rounded-lg"
>
<div>
<p class="font-medium text-gray-900">{{ sensor.sensor_id }}</p>
<p class="text-sm text-gray-500">{{ sensor.room || 'No room assigned' }}</p>
<p class="text-xs text-gray-400">
{{ sensor.sensor_type }} {{ sensor.total_readings }} readings
</p>
</div>
<div class="flex items-center">
<span
:class="[
'inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium',
sensor.status === 'online'
? 'bg-green-100 text-green-800'
: sensor.status === 'offline'
? 'bg-red-100 text-red-800'
: 'bg-yellow-100 text-yellow-800',
]"
>
{{ sensor.status }}
</span>
</div>
</div>
</div>
</div>
</div>
<!-- Rooms Section -->
<div class="bg-white rounded-lg shadow">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">API Rooms</h2>
</div>
<div class="p-6">
<div v-if="apiRooms.length === 0" class="text-center text-gray-500 py-8">
No rooms found from API
</div>
<div v-else class="space-y-3">
<div
v-for="room in apiRooms"
:key="room.name || room.room"
class="p-3 bg-gray-50 rounded-lg"
>
<div class="flex items-center justify-between mb-2">
<p class="font-medium text-gray-900">{{ room.name || room.room }}</p>
<span class="text-sm text-gray-500">{{ room.sensor_count }} sensors</span>
</div>
<div class="text-xs text-gray-600 space-y-1">
<p v-if="room.sensor_types">Types: {{ room.sensor_types.join(', ') }}</p>
<div v-if="room.latest_metrics">
<span v-if="room.latest_metrics.energy" class="mr-4">
Energy: {{ room.latest_metrics.energy.current }}
{{ room.latest_metrics.energy.unit }}
</span>
<span v-if="room.latest_metrics.co2">
CO2: {{ room.latest_metrics.co2.current }} {{ room.latest_metrics.co2.unit }}
</span>
</div>
<p v-else class="text-gray-400 italic">No metrics available</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="bg-white rounded-lg shadow p-6">
<h2 class="text-lg font-semibold text-gray-900 mb-4">API Actions</h2>
<div class="flex flex-wrap gap-3">
<button
@click="refreshAllData"
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
<svg
v-if="!isLoading"
class="-ml-1 mr-2 h-4 w-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
></path>
</svg>
<div
v-else
class="animate-spin -ml-1 mr-2 h-4 w-4 border-2 border-white border-t-transparent rounded-full"
></div>
Refresh All Data
</button>
<button
@click="fetchSensorsOnly"
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
Fetch Sensors
</button>
<button
@click="fetchRoomsOnly"
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
Fetch Rooms
</button>
<button
@click="fetchAnalyticsOnly"
class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
:disabled="isLoading"
>
Fetch Analytics
</button>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -1,29 +1,13 @@
<template> <template>
<div class="space-y-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 pb-20">
<!-- Filter Controls Row --> <div class="space-y-6">
<!--div class="flex flex-col sm:flex-row gap-4 mb-6"> <div class="grid grid-cols-2 sm:grid-cols-3 gap-4">
<select class="px-4 py-2 border border-gray-200 rounded-lg bg-white">
<option>Timeframe: All-time</option>
</select>
<select class="px-4 py-2 border border-gray-200 rounded-lg bg-white">
<option>People: All</option>
</select>
<select class="px-4 py-2 border border-gray-200 rounded-lg bg-white">
<option>Topic: All</option>
</select>
</div-->
<!-- Top Metric Cards Row -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 min-h-96">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-4 gap-y-3">
<MetricCard title="Current Energy" :content="currentEnergyValue" details="kWh" /> <MetricCard title="Current Energy" :content="currentEnergyValue" details="kWh" />
<MetricCard title="Average Usage" :content="averageEnergyUsage" details="kWh" />
<MetricCard <MetricCard
title="Connection Status" title="Connection Status"
:content="websocketStore.isConnected ? 'Connected' : 'Disconnected'" :content="websocketStore.isConnected ? 'Connected' : 'Disconnected'"
/> />
<MetricCard title="Average Usage" :content="averageEnergyUsage" details="kWh" />
<MetricCard title="Average CO2" :content="averageCO2" details="ppm" />
<MetricCard title="Max CO2" :content="maxCO2" details="ppm" />
<GraphMetricCard <GraphMetricCard
title="Real-time Energy" title="Real-time Energy"
:content="currentEnergyValue" :content="currentEnergyValue"
@@ -31,19 +15,16 @@
:trend-data="energyStore.energyHistory.slice(-8)" :trend-data="energyStore.energyHistory.slice(-8)"
trend-direction="neutral" trend-direction="neutral"
/> />
<GraphMetricCard title="Average CO2" :content="averageCO2" details="ppm" />
<GraphMetricCard title="Max CO2" :content="maxCO2" details="ppm" />
</div> </div>
<div> <SensorConsumptionTable />
<RealtimeEnergyChartCard title="Month" />
</div>
</div> </div>
<!-- Charts and Knowledge Cards Row --> <div class="space-y-6">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4"> <RealtimeEnergyChartCard title="Month" />
<SensorConsumptionTable /> <RoomMetricsCard />
<div class="grid grid-cols-1 gap-4"> <AirQualityCard />
<RoomMetricsCard />
<AirQualityCard />
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -66,7 +47,6 @@ const sensorStore = useSensorStore()
const websocketStore = useWebSocketStore() const websocketStore = useWebSocketStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
// Use energy store for aggregated values across all sensors
const currentEnergyValue = computed(() => { const currentEnergyValue = computed(() => {
return energyStore.currentEnergyValue.toFixed(2) return energyStore.currentEnergyValue.toFixed(2)
}) })