testes/src_2/features/fleet-v2/views/QualityControlView.jsx

279 lines
17 KiB
JavaScript

import React, { useEffect, useState } from 'react';
import { useMonitoring } from '../hooks/useMonitoring';
import { useVehicles } from '../hooks/useVehicles';
import {
ClipboardCheck,
Satellite,
CheckCircle2,
XCircle,
Info,
Calendar,
User,
Activity,
Zap,
Signal,
SignalLow,
Wifi,
WifiOff,
Search,
ArrowRight,
ShieldAlert
} from 'lucide-react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { cn } from '@/lib/utils';
import { motion, AnimatePresence } from 'framer-motion';
/**
* QualityControlView V2.
* Separates historical audit (Checklists) from active/real-time tracking (Monitoramento).
*/
export const QualityControlView = ({ initialTab = 'checklists' }) => {
const { mokiData, fetchMokiData, loading } = useMonitoring();
const { vehicles, fetchVehicles } = useVehicles();
const [activeTab, setActiveTab] = useState(initialTab);
// Sync activeTab with initialTab when it changes from outside
useEffect(() => {
setActiveTab(initialTab);
}, [initialTab]);
useEffect(() => {
fetchMokiData();
fetchVehicles();
}, [fetchMokiData, fetchVehicles]);
const stats = {
online: vehicles.filter(v => v.statusTel === 'Ativo').length,
total: vehicles.length,
offline: vehicles.filter(v => v.statusTel !== 'Ativo').length
};
const onlinePercentage = stats.total > 0 ? Math.round((stats.online / stats.total) * 100) : 0;
return (
<div className="space-y-6 pb-10 animate-in fade-in duration-500">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="space-y-1">
<div className="flex items-center gap-2 text-primary">
<ShieldAlert size={16} />
<span className="text-[10px] font-black uppercase tracking-[0.2em]">Conformidade & Rastreio</span>
</div>
<h2 className="text-3xl font-black tracking-tighter text-slate-900 dark:text-white">Qualidade & Monitoramento</h2>
<p className="text-sm text-slate-500 font-medium font-inter">Gestão de inspeções técnicas e telemetria avançada</p>
</div>
</div>
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="space-y-8"
>
<div className="bg-slate-100 dark:bg-white/5 p-1 rounded-2xl w-fit border border-slate-200 dark:border-white/10">
<TabsList className="bg-transparent h-12">
<TabsTrigger
value="checklists"
className="rounded-xl font-black text-[10px] uppercase tracking-widest gap-2 px-8 data-[state=active]:bg-white dark:data-[state=active]:bg-primary data-[state=active]:text-primary dark:data-[state=active]:text-white data-[state=active]:shadow-lg active:scale-95 transition-all"
>
<ClipboardCheck size={16} /> Histórico de Inspeções
</TabsTrigger>
<TabsTrigger
value="monitoramento"
className="rounded-xl font-black text-[10px] uppercase tracking-widest gap-2 px-8 data-[state=active]:bg-white dark:data-[state=active]:bg-primary data-[state=active]:text-primary dark:data-[state=active]:text-white data-[state=active]:shadow-lg active:scale-95 transition-all"
>
<Satellite size={16} /> Monitoramento em Tempo Real
</TabsTrigger>
</TabsList>
</div>
{/* --- TAB: CHECKLISTS (Historical Log) --- */}
<TabsContent value="checklists" className="space-y-6 outline-none">
<Card className="border-none shadow-2xl shadow-slate-200/50 dark:shadow-none overflow-hidden ring-1 ring-slate-200 dark:ring-white/5 bg-white dark:bg-slate-900/50 backdrop-blur-xl rounded-[2rem]">
<CardHeader className="bg-slate-50/50 dark:bg-white/[0.02] border-b border-slate-100 dark:border-white/5 px-8 py-6">
<div className="flex items-center justify-between">
<CardTitle className="text-sm font-black text-slate-800 dark:text-white uppercase tracking-wider">Log de Auditoria</CardTitle>
<div className="relative w-64">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" size={14} />
<input
placeholder="Buscar registro..."
className="w-full pl-9 pr-4 py-2 bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10 rounded-xl text-xs font-bold focus:ring-2 focus:ring-primary/20 outline-none transition-all"
/>
</div>
</div>
</CardHeader>
<div className="overflow-x-auto">
{loading ? <div className="p-8 text-center text-slate-500">Carregando checklists...</div> : (
<table className="w-full text-left border-collapse min-w-[900px]">
<thead>
<tr className="bg-slate-50/20 dark:bg-white/[0.01]">
<th className="px-8 py-5 text-[0.65rem] uppercase font-black text-slate-400 tracking-[0.15em] border-b border-slate-100 dark:border-white/5">Data / Unidade</th>
<th className="px-8 py-5 text-[0.65rem] uppercase font-black text-slate-400 tracking-[0.15em] border-b border-slate-100 dark:border-white/5">Tipo de Inspeção</th>
<th className="px-8 py-5 text-[0.65rem] uppercase font-black text-slate-400 tracking-[0.15em] border-b border-slate-100 dark:border-white/5">Autor / Inspetor</th>
<th className="px-8 py-5 text-[0.65rem] uppercase font-black text-slate-400 tracking-[0.15em] border-b border-slate-100 dark:border-white/5 text-center">Aprovação %</th>
<th className="px-8 py-5 text-[0.65rem] uppercase font-black text-slate-400 tracking-[0.15em] border-b border-slate-100 dark:border-white/5 text-right">Resultado</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100 dark:divide-white/5">
{mokiData.length === 0 ? <tr><td colSpan="5" className="p-6 text-center text-xs text-slate-500">Nenhum checklist encontrado.</td></tr> : mokiData.map((c, index) => (
<tr key={c.id || index} className="hover:bg-slate-50/50 dark:hover:bg-white/[0.02] transition-colors group">
<td className="px-8 py-6">
<div className="flex flex-col">
<span className="text-sm font-black text-slate-800 dark:text-white leading-tight">{c.unidade || c.nome_unidade}</span>
<span className="text-[10px] font-bold text-slate-400 mt-1 uppercase leading-none tracking-widest">{c.data}</span>
</div>
</td>
<td className="px-8 py-6">
<Badge variant="outline" className="bg-slate-100 dark:bg-white/5 border-none font-black text-[0.65rem] uppercase px-3 py-1 rounded-lg text-slate-600 dark:text-slate-400 tracking-tight">
{c.checklist}
</Badge>
</td>
<td className="px-8 py-6">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-[0.65rem] font-black text-primary border border-primary/20 uppercase">
{(c.autor || 'U').substring(0, 2)}
</div>
<span className="text-sm font-bold text-slate-700 dark:text-slate-300">{c.autor}</span>
</div>
</td>
<td className="px-8 py-6 text-center">
<div className="inline-flex flex-col items-center gap-1.5">
<span className={cn("text-sm font-black", (c.aprovacao || 0) >= 80 ? "text-emerald-500" : "text-rose-500")}>
{c.aprovacao || 0}%
</span>
<div className="h-1.5 w-16 bg-slate-100 dark:bg-white/5 rounded-full overflow-hidden shadow-inner">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${c.aprovacao || 0}%` }}
className={cn("h-full", (c.aprovacao || 0) >= 80 ? "bg-emerald-500" : "bg-rose-500")}
/>
</div>
</div>
</td>
<td className="px-8 py-6 text-right">
<Badge className={cn(
"border-none font-black text-[0.65rem] uppercase tracking-widest px-4 py-1.5 rounded-xl shadow-sm",
c.status === 'Aprovado'
? "bg-emerald-500/10 text-emerald-600 dark:bg-emerald-500/20"
: "bg-rose-500/10 text-rose-600 dark:bg-rose-500/20"
)}>
{c.status}
</Badge>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</Card>
</TabsContent>
{/* --- TAB: MONITORING (Real-time Status Dashboard) --- */}
<TabsContent value="monitoramento" className="space-y-10 outline-none">
{/* Signal & Health Overview */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<Card className="border-none shadow-xl shadow-slate-200/50 dark:shadow-none bg-white dark:bg-slate-900 ring-1 ring-slate-200 dark:ring-white/5 rounded-3xl overflow-hidden p-6 group hover:ring-2 hover:ring-primary/20 transition-all">
<div className="flex items-center justify-between mb-4">
<div className="p-3 rounded-2xl group-hover:scale-110 transition-transform duration-300 bg-emerald-500/10 text-emerald-500">
<Signal size={22} />
</div>
<Activity size={18} className="text-slate-200 dark:text-slate-800" />
</div>
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-1">Sinal Online</p>
<h4 className="text-3xl font-black text-slate-900 dark:text-white tracking-tighter">{onlinePercentage}%</h4>
</Card>
<Card className="border-none shadow-xl shadow-slate-200/50 dark:shadow-none bg-white dark:bg-slate-900 ring-1 ring-slate-200 dark:ring-white/5 rounded-3xl overflow-hidden p-6 group hover:ring-2 hover:ring-primary/20 transition-all">
<div className="flex items-center justify-between mb-4">
<div className="p-3 rounded-2xl group-hover:scale-110 transition-transform duration-300 bg-primary/10 text-primary">
<Wifi size={22} />
</div>
<Activity size={18} className="text-slate-200 dark:text-slate-800" />
</div>
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-1">Telemetria OK</p>
<h4 className="text-3xl font-black text-slate-900 dark:text-white tracking-tighter">{stats.online}</h4>
</Card>
<Card className="border-none shadow-xl shadow-slate-200/50 dark:shadow-none bg-white dark:bg-slate-900 ring-1 ring-slate-200 dark:ring-white/5 rounded-3xl overflow-hidden p-6 group hover:ring-2 hover:ring-primary/20 transition-all">
<div className="flex items-center justify-between mb-4">
<div className="p-3 rounded-2xl group-hover:scale-110 transition-transform duration-300 bg-rose-500/10 text-rose-500">
<WifiOff size={22} />
</div>
<Activity size={18} className="text-slate-200 dark:text-slate-800" />
</div>
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-1">Sem Comunicação</p>
<h4 className="text-3xl font-black text-slate-900 dark:text-white tracking-tighter">{stats.offline}</h4>
</Card>
<Card className="border-none shadow-xl shadow-slate-200/50 dark:shadow-none bg-white dark:bg-slate-900 ring-1 ring-slate-200 dark:ring-white/5 rounded-3xl overflow-hidden p-6 group hover:ring-2 hover:ring-primary/20 transition-all">
<div className="flex items-center justify-between mb-4">
<div className="p-3 rounded-2xl group-hover:scale-110 transition-transform duration-300 bg-amber-500/10 text-amber-500">
<Zap size={22} />
</div>
<Activity size={18} className="text-slate-200 dark:text-slate-800" />
</div>
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-1">Alertas Ativos</p>
<h4 className="text-3xl font-black text-slate-900 dark:text-white tracking-tighter">0</h4>
</Card>
</div>
{/* Real-time Status Grid */}
<div className="space-y-6">
<div className="flex items-center justify-between px-2">
<div>
<h3 className="text-xl font-black text-slate-900 dark:text-white tracking-tight">Status da Transmissão</h3>
<p className="text-xs text-slate-500 font-bold uppercase tracking-widest mt-1 opacity-70">Sincronização Sascar / Geotab / T4S</p>
</div>
<Badge className="bg-emerald-500/10 text-emerald-600 border-none font-black text-[10px] uppercase tracking-widest gap-2 py-1.5 px-4 rounded-full">
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" /> Gateway em Operação
</Badge>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{vehicles.map((v) => (
<Card key={v.id} className="border-none bg-slate-50/50 dark:bg-white/[0.02] ring-1 ring-slate-200 dark:ring-white/5 rounded-[2rem] p-5 group hover:bg-white dark:hover:bg-white/5 hover:shadow-2xl transition-all duration-500">
<div className="flex items-start justify-between mb-4">
<div>
<span className="text-xs font-black text-slate-900 dark:text-white font-mono tracking-tighter block mb-1 underline decoration-primary/20 underline-offset-4">{v.placa}</span>
<span className="text-[9px] font-black text-slate-400 uppercase tracking-widest">{v.modelo}</span>
</div>
<div className={cn(
"p-2 rounded-full",
v.statusTel === 'Ativo' ? "bg-emerald-500/10 text-emerald-500" : "bg-rose-500/10 text-rose-500"
)}>
{v.statusTel === 'Ativo' ? <Wifi size={14} /> : <WifiOff size={14} />}
</div>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between text-[10px]">
<span className="font-bold text-slate-400 uppercase tracking-widest">Sinal</span>
<span className={cn("font-black uppercase", v.statusTel === 'Ativo' ? "text-emerald-500" : "text-rose-500")}>
{v.statusTel === 'Ativo' ? 'Excelente' : 'Desconectado'}
</span>
</div>
<div className="h-1.5 w-full bg-slate-200 dark:bg-white/5 rounded-full overflow-hidden">
<div className={cn("h-full", v.statusTel === 'Ativo' ? "bg-emerald-500 w-[95%]" : "bg-rose-500 w-[10%]")} />
</div>
<div className="pt-2 flex items-center justify-between">
<div className="flex gap-1.5">
<Signal size={12} className={v.statusTel === 'Ativo' ? "text-primary" : "text-slate-300"} />
<Satellite size={12} className={v.statusTel === 'Ativo' ? "text-primary" : "text-slate-300"} />
</div>
<button className="text-[9px] font-black uppercase text-primary hover:underline hover:underline-offset-4 flex items-center gap-1 group/btn">
Abrir Mapa <ArrowRight size={10} className="group-hover/btn:translate-x-1 transition-transform" />
</button>
</div>
</div>
</Card>
))}
</div>
</div>
</TabsContent>
</Tabs>
</div>
);
};