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

284 lines
15 KiB
JavaScript

import React, { useEffect, useState } from 'react';
import { useStatusFrota } from '../hooks/useStatusFrota';
import { useVehicles } from '../hooks/useVehicles';
import {
Activity, Search, Filter, Plus, Pencil,
MapPin, User, Truck, Calendar
} from 'lucide-react';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from '@/lib/utils';
import { useFleetOptions } from '../hooks/useFleetOptions';
export const StatusView = () => {
const { statusList, loading, fetchStatus, createStatus, updateStatus } = useStatusFrota();
const { getSelectOptions } = useFleetOptions();
const statusFrotaOptions = getSelectOptions('status_frota');
const { vehicles, fetchVehicles } = useVehicles();
const [searchTerm, setSearchTerm] = useState('');
// Modal State
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingStatus, setEditingStatus] = useState(null);
// Form State
const [formData, setFormData] = useState({
placa: '',
placa_reserva: '',
status_frota: 'Operacional',
manutencao: '',
obs: '',
// Contexto
id_rota: '',
motorista: '',
base: '',
uf: '',
tipo_placa: '',
categoria: '',
modelo: '',
proprietario: '',
atuacao: '',
vecfleet: ''
});
useEffect(() => {
fetchStatus();
fetchVehicles();
}, [fetchStatus, fetchVehicles]);
const handleOpenModal = (item = null) => {
if (item) {
setEditingStatus(item);
setFormData({
placa: item.placa || '',
placa_reserva: item.placa_reserva || '',
status_frota: item.status_frota || 'Operacional',
manutencao: item.manutencao || '',
obs: item.obs || '',
id_rota: item.id_rota || '',
motorista: item.motorista || '',
base: item.base || '',
uf: item.uf || '',
tipo_placa: item.tipo_placa || '',
categoria: item.categoria || '',
modelo: item.modelo || '',
proprietario: item.proprietario || '',
atuacao: item.atuacao || '',
vecfleet: item.vecfleet || ''
});
} else {
setEditingStatus(null);
setFormData({
placa: '', placa_reserva: '', status_frota: 'Operacional', manutencao: '', obs: '',
id_rota: '', motorista: '', base: '', uf: '', tipo_placa: '',
categoria: '', modelo: '', proprietario: '', atuacao: '', vecfleet: ''
});
}
setIsModalOpen(true);
};
const handleVehicleSelect = (placa) => {
const vehicle = vehicles.find(v => v.placa === placa);
if (vehicle) {
setFormData(prev => ({
...prev,
placa: vehicle.placa,
modelo: vehicle.modelo || prev.modelo,
categoria: vehicle.categoria || prev.categoria,
base: vehicle.base || prev.base,
uf: vehicle.uf || prev.uf,
proprietario: vehicle.proprietario || prev.proprietario,
atuacao: vehicle.atuacao || prev.atuacao,
tipo_placa: vehicle.tipo_de_placa || prev.tipo_placa
}));
} else {
setFormData(prev => ({ ...prev, placa }));
}
};
const handleSubmit = async (e) => {
e.preventDefault();
let success;
if (editingStatus) {
success = await updateStatus(editingStatus.idstatus_frota, formData);
} else {
success = await createStatus(formData);
}
if (success) setIsModalOpen(false);
};
const filteredList = statusList.filter(s =>
(s.placa || '').toLowerCase().includes(searchTerm.toLowerCase()) ||
(s.status_frota || '').toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="space-y-6 pb-20">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="space-y-1">
<h2 className="text-2xl font-bold tracking-tight text-slate-900">Status da Frota</h2>
<p className="text-sm text-slate-500 font-medium">Gestão de disponibilidade e situações operacionais.</p>
</div>
<div className="flex items-center gap-3">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" size={14} />
<Input
placeholder="Buscar..."
className="pl-9 h-9 w-[200px] text-xs font-medium bg-white"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
</div>
<Button
className="gap-2 h-9 font-bold bg-indigo-600 hover:bg-indigo-700 text-white"
onClick={() => handleOpenModal()}
>
<Plus size={16} /> Novo Status
</Button>
</div>
</div>
<Card className="border border-slate-200 shadow-sm overflow-hidden bg-white">
<div className="overflow-x-auto">
{loading ? <div className="p-8 text-center text-slate-500">Carregando...</div> : (
<table className="w-full text-left border-collapse min-w-[800px] text-sm">
<thead className="bg-slate-50 font-bold text-xs uppercase text-slate-500">
<tr>
<th className="px-6 py-3 border-b border-slate-100">Veículo</th>
<th className="px-6 py-3 border-b border-slate-100">Status</th>
<th className="px-6 py-3 border-b border-slate-100">Operacional</th>
<th className="px-6 py-3 border-b border-slate-100">Observações</th>
<th className="px-6 py-3 border-b border-slate-100 text-right">Ação</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{filteredList.map((s) => (
<tr key={s.idstatus_frota || Math.random()} className="hover:bg-slate-50/50">
<td className="px-6 py-3">
<div className="flex flex-col">
<span className="font-bold text-slate-800">{s.placa}</span>
<span className="text-[10px] text-slate-400">{s.modelo}</span>
</div>
</td>
<td className="px-6 py-3">
<Badge variant="outline" className={cn("text-[10px] font-bold uppercase",
s.status_frota === 'Operacional' ? 'bg-emerald-50 text-emerald-600 border-emerald-200' :
s.status_frota === 'Em Manutenção' ? 'bg-rose-50 text-rose-600 border-rose-200' :
'bg-slate-100 text-slate-600 border-slate-200'
)}>
{s.status_frota}
</Badge>
</td>
<td className="px-6 py-3 text-xs text-slate-500">
<div><MapPin size={10} className="inline mr-1"/>{s.base}</div>
<div><User size={10} className="inline mr-1"/>{s.motorista || '-'}</div>
</td>
<td className="px-6 py-3 text-xs text-slate-500 truncate max-w-[200px]">
{s.obs || '-'}
</td>
<td className="px-6 py-3 text-right">
<Button variant="ghost" size="icon" className="h-8 w-8 text-slate-400 hover:text-indigo-600" onClick={() => handleOpenModal(s)}>
<Pencil size={14} />
</Button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</Card>
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
<DialogContent className="sm:max-w-[700px] bg-white text-slate-900">
<DialogHeader>
<DialogTitle>{editingStatus ? 'Editar Status' : 'Atualizar Status'}</DialogTitle>
<DialogDescription>Atualize a situação atual do veículo.</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="grid grid-cols-2 gap-4 py-4 max-h-[75vh] overflow-y-auto custom-scrollbar px-6">
<div className="space-y-1.5">
<Label className="text-xs font-bold text-slate-500 uppercase">Placa</Label>
<div className="flex gap-2">
<Input value={formData.placa} onChange={e => setFormData({...formData, placa: e.target.value})} className="font-mono" placeholder="ABC-1234"/>
<Button type="button" size="icon" variant="outline" onClick={() => handleVehicleSelect(formData.placa)}><Search size={14}/></Button>
</div>
</div>
<div className="space-y-1.5">
<Label className="text-xs font-bold text-slate-500 uppercase">Status Frota</Label>
<Select value={formData.status_frota} onValueChange={v => setFormData(p => ({...p, status_frota: v}))}>
<SelectTrigger><SelectValue/></SelectTrigger>
<SelectContent>
{statusFrotaOptions.length > 0 ? (
statusFrotaOptions.map(opt => (
<SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
))
) : (
<SelectItem value="Operacional">Operacional</SelectItem>
)}
</SelectContent>
</Select>
</div>
<div className="col-span-2 grid grid-cols-3 gap-4 bg-slate-50 p-3 rounded-lg border border-slate-100">
<div className="space-y-1.5">
<Label className="text-[10px] font-bold text-slate-400 uppercase">Motorista</Label>
<Input className="h-8 text-xs bg-white" value={formData.motorista} onChange={e => setFormData({...formData, motorista: e.target.value})} />
</div>
<div className="space-y-1.5">
<Label className="text-[10px] font-bold text-slate-400 uppercase">Base</Label>
<Input className="h-8 text-xs bg-white" value={formData.base} onChange={e => setFormData({...formData, base: e.target.value})} />
</div>
<div className="space-y-1.5">
<Label className="text-[10px] font-bold text-slate-400 uppercase">ID Rota</Label>
<Input className="h-8 text-xs bg-white" value={formData.id_rota} onChange={e => setFormData({...formData, id_rota: e.target.value})} />
</div>
<div className="space-y-1.5">
<Label className="text-[10px] font-bold text-slate-400 uppercase">Placa Reserva</Label>
<Input className="h-8 text-xs bg-white" value={formData.placa_reserva} onChange={e => setFormData({...formData, placa_reserva: e.target.value})} />
</div>
<div className="space-y-1.5">
<Label className="text-[10px] font-bold text-slate-400 uppercase">Modelo</Label>
<Input className="h-8 text-xs bg-white" value={formData.modelo} onChange={e => setFormData({...formData, modelo: e.target.value})} />
</div>
<div className="space-y-1.5">
<Label className="text-[10px] font-bold text-slate-400 uppercase">UF</Label>
<Input className="h-8 text-xs bg-white" value={formData.uf} onChange={e => setFormData({...formData, uf: e.target.value})} />
</div>
</div>
<div className="col-span-2 space-y-1.5">
<Label className="text-xs font-bold text-slate-500 uppercase">Observações</Label>
<Textarea value={formData.obs} onChange={e => setFormData({...formData, obs: e.target.value})} />
</div>
<DialogFooter className="col-span-2 mt-2">
<Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>Cancelar</Button>
<Button type="submit" className="bg-indigo-600 hover:bg-indigo-700 text-white">Salvar</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
);
};