378 lines
13 KiB
Vue
378 lines
13 KiB
Vue
<template>
|
|
<app-layout>
|
|
<v-container fluid class="pa-6">
|
|
<!-- Breadcrumbs -->
|
|
<v-breadcrumbs :items="[ { title: 'Home', disabled: false, href: '/' }, { title: 'Dashboards', disabled: true } ]" class="px-0 py-3"></v-breadcrumbs>
|
|
|
|
<!-- Header com estatísticas melhoradas -->
|
|
<v-row>
|
|
<v-col cols="12" md="4">
|
|
<v-card elevation="2" class="rounded-lg">
|
|
<v-card-text class="pa-6">
|
|
<div class="d-flex justify-space-between align-center">
|
|
<div>
|
|
<div class="text-subtitle-1 text-medium-emphasis mb-1">Total de Dashboards</div>
|
|
<div class="text-h4 font-weight-bold">{{ totalModels }}</div>
|
|
<div class="text-caption text-medium-emphasis mt-2">
|
|
<v-icon small color="success" class="mr-1">mdi-arrow-up</v-icon>
|
|
<span>+5% desde o último mês</span>
|
|
</div>
|
|
</div>
|
|
<v-icon size="48" color="primary" class="opacity-6">mdi-view-dashboard</v-icon>
|
|
</div>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
|
|
<v-col cols="12" md="4">
|
|
<v-card elevation="2" class="rounded-lg">
|
|
<v-card-text class="pa-6">
|
|
<div class="d-flex justify-space-between align-center">
|
|
<div>
|
|
<div class="text-subtitle-1 text-medium-emphasis mb-1">Dashboards Ativos</div>
|
|
<div class="text-h4 font-weight-bold">{{ activeModels }}</div>
|
|
<div class="text-caption text-success mt-2">
|
|
<v-icon small color="success" class="mr-1">mdi-check-circle</v-icon>
|
|
<span>Todos funcionando normalmente</span>
|
|
</div>
|
|
</div>
|
|
<v-icon size="48" color="success" class="opacity-6">mdi-shield-check</v-icon>
|
|
</div>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
|
|
<v-col cols="12" md="4">
|
|
<v-card elevation="2" class="rounded-lg">
|
|
<v-card-text class="pa-6">
|
|
<div class="d-flex justify-space-between align-center">
|
|
<div>
|
|
<div class="text-subtitle-1 text-medium-emphasis mb-1">Câmeras Conectadas</div>
|
|
<div class="text-h4 font-weight-bold">{{ connectedCameras }}</div>
|
|
<div class="text-caption text-warning mt-2">
|
|
<v-icon small color="warning" class="mr-1">mdi-alert-circle</v-icon>
|
|
<span>2 câmeras precisam de atenção</span>
|
|
</div>
|
|
</div>
|
|
<v-icon size="48" color="info" class="opacity-6">mdi-camera</v-icon>
|
|
</div>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<!-- Barra de ações e pesquisa melhorada -->
|
|
<v-card elevation="2" class="mt-6 rounded-lg">
|
|
<v-card-title class="py-4 px-6">
|
|
<div class="d-flex flex-wrap align-center w-100">
|
|
<h2 class="text-h5 font-weight-bold mr-4">Dashboards Treinados</h2>
|
|
<v-spacer></v-spacer>
|
|
<div class="d-flex align-center">
|
|
<v-btn
|
|
color="primary"
|
|
elevation="1"
|
|
class="mr-4"
|
|
prepend-icon="mdi-plus"
|
|
>
|
|
Novo Dashboard
|
|
</v-btn>
|
|
<v-text-field
|
|
v-model="search"
|
|
prepend-inner-icon="mdi-magnify"
|
|
label="Pesquisar dashboards..."
|
|
single-line
|
|
hide-details
|
|
outlined
|
|
dense
|
|
class="custom-search-field"
|
|
style="max-width: 300px"
|
|
></v-text-field>
|
|
</div>
|
|
</div>
|
|
</v-card-title>
|
|
|
|
<!-- Filtros rápidos -->
|
|
<v-card-text class="py-2 px-6">
|
|
<v-chip-group>
|
|
<v-chip filter outlined>Todos</v-chip>
|
|
<v-chip filter outlined color="success">Ativos</v-chip>
|
|
<v-chip filter outlined color="warning">Em Treinamento</v-chip>
|
|
<v-chip filter outlined color="error">Inativos</v-chip>
|
|
</v-chip-group>
|
|
</v-card-text>
|
|
|
|
<!-- Grid de Dashboards melhorado -->
|
|
<v-card-text class="px-6">
|
|
<v-data-iterator
|
|
:items="models"
|
|
:search="search"
|
|
:loading="loading"
|
|
:items-per-page="itemsPerPage"
|
|
hide-default-footer
|
|
>
|
|
<template v-slot:default="{ items }">
|
|
<v-row>
|
|
<v-col
|
|
v-for="model in items"
|
|
:key="model.id"
|
|
cols="12"
|
|
sm="6"
|
|
md="4"
|
|
lg="3"
|
|
>
|
|
<v-card
|
|
elevation="2"
|
|
class="rounded-lg transition-swing"
|
|
:class="{'border-left-4': true, [`border-${getStatusColor(model.status)}`]: true}"
|
|
>
|
|
<v-card-title class="py-3 px-4">
|
|
<div class="d-flex align-center">
|
|
<v-icon
|
|
:color="getStatusColor(model.status)"
|
|
class="mr-2"
|
|
>
|
|
{{ getStatusIcon(model.status) }}
|
|
</v-icon>
|
|
<span class="text-subtitle-1 font-weight-medium">{{ model.name }}</span>
|
|
</div>
|
|
</v-card-title>
|
|
|
|
<v-card-text class="py-2 px-4">
|
|
<v-list-item dense class="px-0">
|
|
<v-list-item-content>
|
|
<div class="d-flex align-center mb-2">
|
|
<v-icon small class="mr-2">mdi-camera</v-icon>
|
|
<span class="text-body-2">Câmera: {{ model.camera_id }}</span>
|
|
</div>
|
|
<div class="d-flex align-center">
|
|
<v-icon small class="mr-2">mdi-clock-outline</v-icon>
|
|
<span class="text-body-2">Última atualização: {{ model.lastUpdate }}</span>
|
|
</div>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
</v-card-text>
|
|
|
|
<v-divider></v-divider>
|
|
|
|
<v-card-actions class="py-2 px-2">
|
|
<v-tooltip bottom>
|
|
<template v-slot:activator="{ on, attrs }">
|
|
<v-btn
|
|
text
|
|
small
|
|
color="primary"
|
|
v-bind="attrs"
|
|
v-on="on"
|
|
@click="viewDetails(model)"
|
|
>
|
|
<v-icon left>mdi-information</v-icon>
|
|
Detalhes
|
|
</v-btn>
|
|
</template>
|
|
<span>Ver detalhes completos</span>
|
|
</v-tooltip>
|
|
|
|
<v-spacer></v-spacer>
|
|
|
|
<v-tooltip bottom>
|
|
<template v-slot:activator="{ on, attrs }">
|
|
<v-btn
|
|
text
|
|
small
|
|
color="error"
|
|
v-bind="attrs"
|
|
v-on="on"
|
|
@click="deactivateModel(model)"
|
|
>
|
|
<v-icon left>mdi-power</v-icon>
|
|
Desativar
|
|
</v-btn>
|
|
</template>
|
|
<span>Desativar dashboard</span>
|
|
</v-tooltip>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|
|
</v-data-iterator>
|
|
</v-card-text>
|
|
|
|
<!-- Paginação melhorada -->
|
|
<v-card-actions class="py-3 px-6 d-flex justify-center">
|
|
<v-pagination
|
|
v-model="page"
|
|
:length="Math.ceil(models.length / itemsPerPage)"
|
|
@input="updatePage"
|
|
circle
|
|
></v-pagination>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-container>
|
|
|
|
<!-- Dialog de Detalhes melhorado -->
|
|
<v-dialog v-model="detailDialog" max-width="700" scrollable>
|
|
<v-card v-if="selectedModel">
|
|
<v-toolbar flat dense color="primary" dark>
|
|
<v-toolbar-title>Detalhes do Dashboard</v-toolbar-title>
|
|
<v-spacer></v-spacer>
|
|
<v-btn icon @click="detailDialog = false">
|
|
<v-icon>mdi-close</v-icon>
|
|
</v-btn>
|
|
</v-toolbar>
|
|
|
|
<v-card-text class="pa-6">
|
|
<v-row>
|
|
<v-col cols="12">
|
|
<div class="text-h6 mb-4">{{ selectedModel.name }}</div>
|
|
|
|
<v-list>
|
|
<v-list-item>
|
|
<v-list-item-icon>
|
|
<v-icon :color="getStatusColor(selectedModel.status)">
|
|
{{ getStatusIcon(selectedModel.status) }}
|
|
</v-icon>
|
|
</v-list-item-icon>
|
|
<v-list-item-content>
|
|
<v-list-item-title>Status</v-list-item-title>
|
|
<v-list-item-subtitle>
|
|
<v-chip
|
|
small
|
|
:color="getStatusColor(selectedModel.status)"
|
|
text-color="white"
|
|
>
|
|
{{ selectedModel.status }}
|
|
</v-chip>
|
|
</v-list-item-subtitle>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
|
|
<v-list-item>
|
|
<v-list-item-icon>
|
|
<v-icon>mdi-camera</v-icon>
|
|
</v-list-item-icon>
|
|
<v-list-item-content>
|
|
<v-list-item-title>Câmera</v-list-item-title>
|
|
<v-list-item-subtitle>{{ selectedModel.camera_id }}</v-list-item-subtitle>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
|
|
<v-list-item>
|
|
<v-list-item-icon>
|
|
<v-icon>mdi-clock-outline</v-icon>
|
|
</v-list-item-icon>
|
|
<v-list-item-content>
|
|
<v-list-item-title>Última atualização</v-list-item-title>
|
|
<v-list-item-subtitle>{{ selectedModel.lastUpdate }}</v-list-item-subtitle>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-col>
|
|
</v-row>
|
|
</v-card-text>
|
|
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn text @click="detailDialog = false">Fechar</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</app-layout>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
search: '',
|
|
page: 1,
|
|
itemsPerPage: 8,
|
|
loading: false,
|
|
totalModels: 123,
|
|
activeModels: 110,
|
|
connectedCameras: 85,
|
|
models: [
|
|
{ id: 1, name: 'Dashboard 1', camera_id: 'Cam-01', lastUpdate: '01/01/2025', status: 'active' },
|
|
{ id: 2, name: 'Dashboard 2', camera_id: 'Cam-02', lastUpdate: '01/01/2025', status: 'inactive' },
|
|
{ id: 3, name: 'Dashboard 3', camera_id: 'Cam-03', lastUpdate: '01/01/2025', status: 'training' },
|
|
{ id: 4, name: 'Dashboard 4', camera_id: 'Cam-04', lastUpdate: '01/01/2025', status: 'active' },
|
|
// outros modelos
|
|
],
|
|
selectedModel: null,
|
|
detailDialog: false,
|
|
};
|
|
},
|
|
methods: {
|
|
getStatusColor(status) {
|
|
switch (status) {
|
|
case 'active':
|
|
return 'green';
|
|
case 'inactive':
|
|
return 'red';
|
|
case 'training':
|
|
return 'yellow';
|
|
default:
|
|
return 'gray';
|
|
}
|
|
},
|
|
getStatusIcon(status) {
|
|
switch (status) {
|
|
case 'active':
|
|
return 'mdi-check-circle';
|
|
case 'inactive':
|
|
return 'mdi-close-circle';
|
|
case 'training':
|
|
return 'mdi-cogs';
|
|
default:
|
|
return 'mdi-help-circle';
|
|
}
|
|
},
|
|
viewDetails(model) {
|
|
this.selectedModel = model;
|
|
this.detailDialog = true;
|
|
},
|
|
deactivateModel(model) {
|
|
model.status = 'inactive';
|
|
},
|
|
updatePage(page) {
|
|
this.page = page;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.border-left-4 {
|
|
border-left: 4px solid !important;
|
|
}
|
|
|
|
.transition-swing {
|
|
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
|
|
}
|
|
|
|
.transition-swing:hover {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.v-pagination .v-pagination__item {
|
|
transition: transform 0.3s ease-in-out;
|
|
}
|
|
|
|
.v-pagination .v-pagination__item:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
.custom-search-field .v-input__slot {
|
|
background-color: #f5f5f5 !important;
|
|
}
|
|
|
|
.v-chip {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.v-chip:hover {
|
|
background-color: #dcdcdc;
|
|
}
|
|
</style>
|