sensor management page

This commit is contained in:
rafaeldpsilva
2025-09-02 15:39:45 +01:00
parent 42f9fa5aed
commit 1522f70f08
8 changed files with 1159 additions and 14 deletions

View File

@@ -0,0 +1,220 @@
<template>
<div class="space-y-6">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900">Sensor & IoT Management</h1>
<p class="text-gray-600">Manage sensors, assign rooms, and control device actions</p>
</div>
<div class="mt-4 sm:mt-0">
<div class="flex items-center gap-2 text-sm text-gray-600">
<div
class="w-3 h-3 rounded-full"
:class="energyStore.isConnected ? 'bg-green-500' : 'bg-red-500'"
></div>
<span>{{ energyStore.isConnected ? 'Connected' : 'Disconnected' }}</span>
<span class="mx-2"></span>
<span>{{ sensorList.length }} sensors</span>
</div>
</div>
</div>
<!-- Filters and View Toggle -->
<div class="flex flex-col lg:flex-row gap-4">
<!-- Filters -->
<div class="flex flex-col sm:flex-row gap-4 flex-1">
<select
v-model="selectedRoom"
class="px-4 py-2 border border-gray-200 rounded-lg bg-white"
>
<option value="">All Rooms</option>
<option v-for="room in energyStore.availableRooms" :key="room" :value="room">
{{ room }}
</option>
</select>
<select
v-model="selectedType"
class="px-4 py-2 border border-gray-200 rounded-lg bg-white"
>
<option value="">All Types</option>
<option value="energy">Energy</option>
<option value="co2">CO2</option>
<option value="temperature">Temperature</option>
<option value="hvac">HVAC</option>
<option value="lighting">Lighting</option>
<option value="security">Security</option>
</select>
<select
v-model="selectedStatus"
class="px-4 py-2 border border-gray-200 rounded-lg bg-white"
>
<option value="">All Status</option>
<option value="online">Online</option>
<option value="offline">Offline</option>
<option value="error">Error</option>
</select>
</div>
<!-- View Toggle -->
<div class="flex items-center gap-2 bg-gray-100 rounded-lg p-1">
<button
@click="viewMode = 'simple'"
class="px-3 py-1.5 rounded text-sm font-medium transition-colors"
:class="viewMode === 'simple'
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-600 hover:text-gray-900'"
>
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
</svg>
Simple
</div>
</button>
<button
@click="viewMode = 'detailed'"
class="px-3 py-1.5 rounded text-sm font-medium transition-colors"
:class="viewMode === 'detailed'
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-600 hover:text-gray-900'"
>
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
Detailed
</div>
</button>
</div>
</div>
<!-- Sensors Grid -->
<div
class="grid gap-4"
:class="viewMode === 'simple'
? 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'
: 'grid-cols-1 lg:grid-cols-2 xl:grid-cols-3'"
>
<!-- Simple Cards -->
<SimpleSensorCard
v-if="viewMode === 'simple'"
v-for="sensor in filteredSensors"
:key="sensor.id"
:sensor="sensor"
:is-executing-action="isExecutingAction"
@execute-action="executeAction"
@show-more="showSensorDetails"
/>
<!-- Detailed Cards -->
<DetailedSensorCard
v-if="viewMode === 'detailed'"
v-for="sensor in filteredSensors"
:key="sensor.id"
:sensor="sensor"
:available-rooms="energyStore.availableRooms"
:is-executing-action="isExecutingAction"
@update-room="updateRoom"
@execute-action="executeAction"
/>
</div>
<!-- Empty State -->
<div v-if="filteredSensors.length === 0" class="text-center py-12">
<div class="text-gray-400 text-6xl mb-4">🔍</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">No sensors found</h3>
<p class="text-gray-600">Try adjusting your filters or check if sensors are connected.</p>
</div>
<!-- Action Modal -->
<ActionModal
v-if="showActionModal"
:sensor="selectedSensor"
:action="selectedAction"
@execute="handleActionExecute"
@close="closeActionModal"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useEnergyStore } from '@/stores/energy'
import ActionModal from '@/components/modals/ActionModal.vue'
import SimpleSensorCard from '@/components/cards/SimpleSensorCard.vue'
import DetailedSensorCard from '@/components/cards/DetailedSensorCard.vue'
const energyStore = useEnergyStore()
// View mode
const viewMode = ref<'simple' | 'detailed'>('simple')
// Filters
const selectedRoom = ref('')
const selectedType = ref('')
const selectedStatus = ref('')
// Action modal
const showActionModal = ref(false)
const selectedSensor = ref<any>(null)
const selectedAction = ref<any>(null)
const isExecutingAction = ref(false)
const sensorList = computed(() => {
return Array.from(energyStore.sensorDevices.values()).sort((a, b) =>
a.name.localeCompare(b.name)
)
})
const filteredSensors = computed(() => {
return sensorList.value.filter(sensor => {
const matchesRoom = !selectedRoom.value || sensor.room === selectedRoom.value
const matchesType = !selectedType.value || sensor.type === selectedType.value
const matchesStatus = !selectedStatus.value || sensor.status === selectedStatus.value
return matchesRoom && matchesType && matchesStatus
})
})
const updateRoom = (sensorId: string, newRoom: string) => {
energyStore.updateSensorRoom(sensorId, newRoom)
}
const executeAction = (sensor: any, action: any) => {
if (action.parameters) {
// Show modal for actions with parameters
selectedSensor.value = sensor
selectedAction.value = action
showActionModal.value = true
} else {
// Execute simple actions directly
handleActionExecute(sensor.id, action.id, {})
}
}
const handleActionExecute = async (sensorId: string, actionId: string, parameters: any) => {
isExecutingAction.value = true
try {
await energyStore.executeSensorAction(sensorId, actionId, parameters)
} catch (error) {
console.error('Action execution failed:', error)
} finally {
isExecutingAction.value = false
closeActionModal()
}
}
const closeActionModal = () => {
showActionModal.value = false
selectedSensor.value = null
selectedAction.value = null
}
const showSensorDetails = (sensor: any) => {
// Switch to detailed view when user wants to see more actions
viewMode.value = 'detailed'
// Optionally scroll to the sensor or highlight it
}
</script>