20 KiB
📊 Dashboard ERP SAAS PraFrota
Sistema de Indicadores e KPIs para Gestão de Frota
🎯 Visão Geral
O Dashboard PraFrota é o centro de controle operacional que oferece visibilidade completa sobre a performance da frota, eficiência das rotas, custos operacionais e indicadores financeiros em tempo real.
📈 KPIs Estratégicos por Categoria
🚚 1. OPERACIONAL - Frota e Veículos
📊 KPIs Principais:
-
Taxa de Utilização da Frota
(Veículos em Operação / Total de Veículos) × 100- Meta: > 85%
- Período: Tempo real, diário, mensal
-
Disponibilidade Operacional
(Veículos Disponíveis / Total de Veículos) × 100- Meta: > 90%
- Exclui: Manutenção, acidentes, licenciamento
-
Tempo Médio de Inatividade
- Média de horas paradas por veículo
- Meta: < 2 horas/dia
- Inclui: Manutenção preventiva/corretiva
-
Quilometragem por Veículo
- KM rodados por período
- Comparativo: Planejado vs Realizado
- Análise de eficiência por modelo/ano
📊 Widgets Visuais:
interface FleetKPIs {
totalVehicles: number;
activeVehicles: number;
maintenanceVehicles: number;
availableVehicles: number;
utilizationRate: number;
averageKmPerVehicle: number;
fuelEfficiencyAverage: number;
}
🛣️ 2. LOGÍSTICA - Rotas e Entregas
📊 KPIs Principais:
-
Taxa de Conclusão de Rotas
(Rotas Completadas / Total de Rotas) × 100- Meta: > 95%
- Segmentação: Por tipo (First Mile, Line Haul, Last Mile)
-
Pontualidade de Entregas
(Entregas no Prazo / Total de Entregas) × 100- Meta: > 90%
- SLA por tipo de cliente
-
Tempo Médio de Rota
- Comparativo: Planejado vs Realizado
- Análise por distância e tipo de carga
- Identificação de gargalos
-
Taxa de Ocupação de Carga
(Peso/Volume Transportado / Capacidade Total) × 100- Meta: > 80%
- Otimização de carregamento
-
Rotas em Atraso
- Quantidade e percentual de rotas atrasadas
- Tempo médio de atraso
- Principais causas identificadas
📊 Widgets Visuais:
interface RoutesKPIs {
totalRoutes: number;
completedRoutes: number;
inProgressRoutes: number;
delayedRoutes: number;
completionRate: number;
onTimeDeliveryRate: number;
averageRouteTime: number;
loadUtilizationRate: number;
}
💰 3. FINANCEIRO - Custos e Receitas
📊 KPIs Principais:
-
Custo por Quilômetro
(Custos Totais / KM Rodados)- Segmentação: Combustível, manutenção, pneus, pedágio
- Comparativo mensal e anual
-
Receita por Rota
- Valor médio faturado por entrega
- Margem de contribuição por tipo de rota
- Análise de rentabilidade por cliente
-
ROI da Frota
(Receita - Custos) / Investimento × 100- Por veículo e por período
- Análise de payback
-
Custo de Combustível
- Consumo médio por veículo
- Variação de preços por região
- Eficiência energética
-
Custos de Manutenção
- Preventiva vs Corretiva
- Custo por veículo/km
- Previsão de gastos
📊 Widgets Visuais:
interface FinancialKPIs {
totalRevenue: number;
totalCosts: number;
profitMargin: number;
costPerKm: number;
revenuePerRoute: number;
fuelCosts: number;
maintenanceCosts: number;
fleetROI: number;
}
👨💼 4. RECURSOS HUMANOS - Motoristas
📊 KPIs Principais:
-
Produtividade por Motorista
- Entregas realizadas por período
- KM rodados por motorista
- Tempo de trabalho efetivo
-
Taxa de Acidentes
(Acidentes / KM Rodados) × 1.000.000- Custo médio por acidente
- Análise de causas principais
-
Pontuação de Condução
- Score baseado em telemetria
- Velocidade, frenagem, aceleração
- Ranking de motoristas
-
Horas Extras
- Percentual sobre horas normais
- Custo adicional de HE
- Planejamento vs realizado
📊 Widgets Visuais:
interface DriversKPIs {
totalDrivers: number;
activeDrivers: number;
averageProductivity: number;
accidentRate: number;
averageDrivingScore: number;
overtimeHours: number;
trainingCompliance: number;
}
🎨 Layout do Dashboard
📱 Estrutura Responsiva (4 Colunas → 2 → 1)
.dashboard-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
padding: 20px;
@media (max-width: 1200px) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
🎯 Seções do Dashboard
1. Header com Resumo Executivo
<div class="executive-summary">
<div class="kpi-card highlight">
<h3>Frota Ativa</h3>
<div class="value">{{ activeFleetCount }}/{{ totalFleetCount }}</div>
<div class="trend" [ngClass]="getTrendClass('fleet')">
<i class="fas fa-arrow-up"></i> +5.2%
</div>
</div>
<div class="kpi-card highlight">
<h3>Rotas Hoje</h3>
<div class="value">{{ todayRoutesCompleted }}/{{ todayRoutesTotal }}</div>
<div class="progress-bar">
<div class="progress" [style.width.%]="todayCompletionRate"></div>
</div>
</div>
<div class="kpi-card highlight">
<h3>Receita Mensal</h3>
<div class="value">{{ monthlyRevenue | currency:'BRL' }}</div>
<div class="target">Meta: {{ monthlyTarget | currency:'BRL' }}</div>
</div>
</div>
2. Gráficos Operacionais
<div class="charts-section">
<!-- Utilização da Frota - Donut Chart -->
<div class="chart-card">
<h4>Utilização da Frota</h4>
<canvas #fleetUtilizationChart></canvas>
<div class="chart-legend">
<span class="legend-item active">Em Operação ({{ activeVehicles }})</span>
<span class="legend-item maintenance">Manutenção ({{ maintenanceVehicles }})</span>
<span class="legend-item idle">Parados ({{ idleVehicles }})</span>
</div>
</div>
<!-- Performance de Rotas - Line Chart -->
<div class="chart-card">
<h4>Performance de Rotas (7 dias)</h4>
<canvas #routesPerformanceChart></canvas>
</div>
<!-- Custos por Categoria - Bar Chart -->
<div class="chart-card">
<h4>Custos Operacionais</h4>
<canvas #costsBreakdownChart></canvas>
</div>
<!-- Mapa de Rotas Ativas -->
<div class="chart-card map-card">
<h4>Rotas Ativas</h4>
<div class="map-container">
<google-map
[center]="mapCenter"
[zoom]="mapZoom"
[options]="mapOptions">
<map-marker
*ngFor="let route of activeRoutes"
[position]="route.currentLocation"
[options]="getMarkerOptions(route)">
</map-marker>
</google-map>
</div>
</div>
</div>
3. Tabelas de Dados
<div class="data-tables-section">
<!-- Top 5 Motoristas -->
<div class="table-card">
<h4>Top Motoristas (Produtividade)</h4>
<table class="performance-table">
<thead>
<tr>
<th>Motorista</th>
<th>Entregas</th>
<th>KM</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let driver of topDrivers">
<td>{{ driver.name }}</td>
<td>{{ driver.deliveries }}</td>
<td>{{ driver.kilometers }}</td>
<td>
<span class="score-badge" [ngClass]="getScoreClass(driver.score)">
{{ driver.score }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Rotas Críticas -->
<div class="table-card">
<h4>Rotas em Atraso</h4>
<table class="critical-routes-table">
<thead>
<tr>
<th>Rota</th>
<th>Motorista</th>
<th>Destino</th>
<th>Atraso</th>
<th>Ação</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let route of delayedRoutes" class="delayed-row">
<td>{{ route.routeNumber }}</td>
<td>{{ route.driverName }}</td>
<td>{{ route.destination }}</td>
<td class="delay-time">{{ route.delayMinutes }}min</td>
<td>
<button class="btn btn-sm btn-warning" (click)="contactDriver(route)">
<i class="fas fa-phone"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
4. Alertas e Notificações
<div class="alerts-section">
<div class="alert-card" *ngFor="let alert of criticalAlerts"
[ngClass]="'alert-' + alert.severity">
<div class="alert-icon">
<i class="fas" [ngClass]="getAlertIcon(alert.type)"></i>
</div>
<div class="alert-content">
<h5>{{ alert.title }}</h5>
<p>{{ alert.message }}</p>
<small>{{ alert.timestamp | date:'dd/MM/yyyy HH:mm' }}</small>
</div>
<div class="alert-actions">
<button class="btn btn-sm btn-primary" (click)="resolveAlert(alert)">
Resolver
</button>
</div>
</div>
</div>
🔧 Implementação Técnica
📊 Dashboard Component
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [CommonModule, GoogleMapsModule, NgChartsModule],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss'
})
export class DashboardComponent implements OnInit, OnDestroy {
// KPIs Data
fleetKPIs: FleetKPIs = {};
routesKPIs: RoutesKPIs = {};
financialKPIs: FinancialKPIs = {};
driversKPIs: DriversKPIs = {};
// Charts Data
fleetUtilizationData: ChartData = {};
routesPerformanceData: ChartData = {};
costsBreakdownData: ChartData = {};
// Tables Data
topDrivers: Driver[] = [];
delayedRoutes: Route[] = [];
criticalAlerts: Alert[] = [];
// Real-time Updates
private updateInterval: any;
private websocketConnection: WebSocketSubject<any>;
constructor(
private dashboardService: DashboardService,
private websocketService: WebSocketService,
private cdr: ChangeDetectorRef
) {}
ngOnInit() {
this.loadDashboardData();
this.setupRealTimeUpdates();
this.startPeriodicUpdates();
}
ngOnDestroy() {
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
this.websocketConnection?.complete();
}
private loadDashboardData() {
forkJoin({
fleet: this.dashboardService.getFleetKPIs(),
routes: this.dashboardService.getRoutesKPIs(),
financial: this.dashboardService.getFinancialKPIs(),
drivers: this.dashboardService.getDriversKPIs(),
alerts: this.dashboardService.getCriticalAlerts()
}).subscribe({
next: (data) => {
this.fleetKPIs = data.fleet;
this.routesKPIs = data.routes;
this.financialKPIs = data.financial;
this.driversKPIs = data.drivers;
this.criticalAlerts = data.alerts;
this.updateCharts();
this.cdr.detectChanges();
},
error: (error) => {
console.error('Erro ao carregar dashboard:', error);
this.loadFallbackData();
}
});
}
private setupRealTimeUpdates() {
this.websocketConnection = this.websocketService.connect('/dashboard-updates');
this.websocketConnection.subscribe({
next: (update) => {
this.handleRealTimeUpdate(update);
},
error: (error) => {
console.error('WebSocket error:', error);
}
});
}
private handleRealTimeUpdate(update: any) {
switch (update.type) {
case 'ROUTE_STATUS_CHANGE':
this.updateRouteStatus(update.data);
break;
case 'VEHICLE_STATUS_CHANGE':
this.updateVehicleStatus(update.data);
break;
case 'NEW_ALERT':
this.addAlert(update.data);
break;
case 'KPI_UPDATE':
this.updateKPIs(update.data);
break;
}
this.cdr.detectChanges();
}
private startPeriodicUpdates() {
// Atualizar dados a cada 5 minutos
this.updateInterval = setInterval(() => {
this.loadDashboardData();
}, 5 * 60 * 1000);
}
// Métodos de utilidade
getTrendClass(kpi: string): string {
// Lógica para determinar se trend é positivo/negativo
return 'trend-positive'; // ou 'trend-negative'
}
getScoreClass(score: number): string {
if (score >= 90) return 'score-excellent';
if (score >= 80) return 'score-good';
if (score >= 70) return 'score-average';
return 'score-poor';
}
getAlertIcon(type: string): string {
const icons = {
'MAINTENANCE': 'fa-wrench',
'DELAY': 'fa-clock',
'ACCIDENT': 'fa-exclamation-triangle',
'FUEL': 'fa-gas-pump',
'ROUTE': 'fa-route'
};
return icons[type] || 'fa-info-circle';
}
// Ações do usuário
contactDriver(route: Route) {
// Implementar contato com motorista
}
resolveAlert(alert: Alert) {
// Implementar resolução de alerta
}
refreshData() {
this.loadDashboardData();
}
}
📊 Dashboard Service
@Injectable({
providedIn: 'root'
})
export class DashboardService {
private apiUrl = `${environment.apiUrl}/dashboard`;
constructor(private http: HttpClient) {}
getFleetKPIs(): Observable<FleetKPIs> {
return this.http.get<FleetKPIs>(`${this.apiUrl}/fleet-kpis`);
}
getRoutesKPIs(): Observable<RoutesKPIs> {
return this.http.get<RoutesKPIs>(`${this.apiUrl}/routes-kpis`);
}
getFinancialKPIs(): Observable<FinancialKPIs> {
return this.http.get<FinancialKPIs>(`${this.apiUrl}/financial-kpis`);
}
getDriversKPIs(): Observable<DriversKPIs> {
return this.http.get<DriversKPIs>(`${this.apiUrl}/drivers-kpis`);
}
getCriticalAlerts(): Observable<Alert[]> {
return this.http.get<Alert[]>(`${this.apiUrl}/alerts`);
}
// Dados históricos para gráficos
getHistoricalData(period: string): Observable<any> {
return this.http.get(`${this.apiUrl}/historical/${period}`);
}
}
📊 Dados Mockados para Desenvolvimento
🎯 KPIs Mock Data
{
"fleetKPIs": {
"totalVehicles": 45,
"activeVehicles": 38,
"maintenanceVehicles": 4,
"availableVehicles": 3,
"utilizationRate": 84.4,
"averageKmPerVehicle": 287.5,
"fuelEfficiencyAverage": 8.2
},
"routesKPIs": {
"totalRoutes": 156,
"completedRoutes": 142,
"inProgressRoutes": 12,
"delayedRoutes": 2,
"completionRate": 91.0,
"onTimeDeliveryRate": 87.2,
"averageRouteTime": 4.7,
"loadUtilizationRate": 78.9
},
"financialKPIs": {
"totalRevenue": 487650.00,
"totalCosts": 312420.00,
"profitMargin": 35.9,
"costPerKm": 2.85,
"revenuePerRoute": 3126.28,
"fuelCosts": 89450.00,
"maintenanceCosts": 45780.00,
"fleetROI": 24.7
},
"driversKPIs": {
"totalDrivers": 52,
"activeDrivers": 38,
"averageProductivity": 8.7,
"accidentRate": 0.12,
"averageDrivingScore": 82.5,
"overtimeHours": 156.5,
"trainingCompliance": 94.2
}
}
🎨 Estilos CSS
.dashboard-container {
padding: 20px;
background: #f8f9fa;
min-height: 100vh;
.executive-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 16px;
margin-bottom: 24px;
.kpi-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-left: 4px solid #007bff;
&.highlight {
border-left-color: #28a745;
}
h3 {
font-size: 14px;
color: #6c757d;
margin-bottom: 8px;
text-transform: uppercase;
}
.value {
font-size: 28px;
font-weight: bold;
color: #212529;
margin-bottom: 8px;
}
.trend {
font-size: 12px;
font-weight: 500;
&.trend-positive {
color: #28a745;
}
&.trend-negative {
color: #dc3545;
}
}
.progress-bar {
width: 100%;
height: 6px;
background: #e9ecef;
border-radius: 3px;
overflow: hidden;
.progress {
height: 100%;
background: #28a745;
transition: width 0.3s ease;
}
}
}
}
.charts-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 16px;
margin-bottom: 24px;
.chart-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
h4 {
margin-bottom: 16px;
color: #212529;
}
&.map-card {
.map-container {
height: 300px;
border-radius: 4px;
overflow: hidden;
}
}
}
}
.data-tables-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 16px;
margin-bottom: 24px;
.table-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
table {
width: 100%;
border-collapse: collapse;
th, td {
padding: 12px 8px;
text-align: left;
border-bottom: 1px solid #e9ecef;
}
th {
background: #f8f9fa;
font-weight: 600;
color: #495057;
}
.score-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
&.score-excellent {
background: #d4edda;
color: #155724;
}
&.score-good {
background: #cce5ff;
color: #004085;
}
&.score-average {
background: #fff3cd;
color: #856404;
}
&.score-poor {
background: #f8d7da;
color: #721c24;
}
}
.delayed-row {
background: #fff3cd;
.delay-time {
color: #856404;
font-weight: 600;
}
}
}
}
}
.alerts-section {
.alert-card {
background: white;
border-radius: 8px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
display: flex;
align-items: center;
gap: 16px;
&.alert-critical {
border-left: 4px solid #dc3545;
}
&.alert-warning {
border-left: 4px solid #ffc107;
}
&.alert-info {
border-left: 4px solid #17a2b8;
}
.alert-icon {
font-size: 24px;
color: #6c757d;
}
.alert-content {
flex: 1;
h5 {
margin-bottom: 4px;
color: #212529;
}
p {
margin-bottom: 4px;
color: #6c757d;
}
small {
color: #adb5bd;
}
}
}
}
}
// Responsividade
@media (max-width: 768px) {
.dashboard-container {
padding: 12px;
.charts-section,
.data-tables-section {
grid-template-columns: 1fr;
}
.executive-summary {
grid-template-columns: 1fr;
}
}
}
🚀 Próximos Passos
1. Implementação por Fases
- Fase 1: KPIs básicos + gráficos simples
- Fase 2: Mapa em tempo real + alertas
- Fase 3: Machine Learning para previsões
- Fase 4: Dashboards personalizáveis por usuário
2. Integrações Necessárias
- WebSocket para dados em tempo real
- Google Maps API para visualização
- Chart.js ou D3.js para gráficos
- Push notifications para alertas críticos
3. Otimizações de Performance
- Cache de dados com TTL
- Lazy loading de componentes
- Paginação server-side
- Compressão de dados WebSocket
📱 Configuração no Sidebar
// Adicionar ao menu principal
{
id: 'dashboard',
label: 'Dashboard',
icon: 'fa-tachometer-alt',
route: '/dashboard',
order: 1, // Primeiro item do menu
permissions: ['DASHBOARD_VIEW']
}
Este dashboard fornece uma visão 360° da operação, permitindo tomada de decisões baseada em dados e monitoramento proativo da performance da frota. 📊✨