start
This commit is contained in:
178
Chargers copy.jsx
Normal file
178
Chargers copy.jsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Plus, Loader2, X, Trash2 } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export default function ChargersPage() {
|
||||
const [chargers, setChargers] = useState([]);
|
||||
const [selectedCharger, setSelectedCharger] = useState(null);
|
||||
const [deletingId, setDeletingId] = useState(null);
|
||||
const [showAdd, setShowAdd] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Carregar os carregadores ao montar o componente
|
||||
useEffect(() => {
|
||||
async function fetchChargers() {
|
||||
try {
|
||||
const res = await fetch('/api/chargers');
|
||||
const json = await res.json();
|
||||
if (res.ok) {
|
||||
setChargers(json.data || []);
|
||||
} else {
|
||||
throw new Error('Falha ao carregar carregadores');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
}
|
||||
}
|
||||
fetchChargers();
|
||||
}, []);
|
||||
|
||||
const handleOpenAdd = () => setShowAdd(true);
|
||||
const handleCloseAdd = () => setShowAdd(false);
|
||||
|
||||
const handleSelect = (id) => {
|
||||
setSelectedCharger(id); // Atualiza o carregador selecionado
|
||||
navigate(`/charger/${id}`); // Navega para a página do carregador
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (window.confirm('Excluir este carregador? Esta ação não pode ser desfeita.')) {
|
||||
setDeletingId(id);
|
||||
try {
|
||||
const res = await fetch(`/api/chargers/${id}`, { method: 'DELETE' });
|
||||
if (res.ok) {
|
||||
setChargers((prevChargers) => prevChargers.filter(c => c.id !== id));
|
||||
} else {
|
||||
throw new Error('Erro ao excluir carregador');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Erro ao deletar carregador', err);
|
||||
} finally {
|
||||
setDeletingId(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto p-4 pb-24">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h1 className="text-2xl font-bold">Meus Carregadores</h1>
|
||||
<button
|
||||
className="flex items-center gap-2 bg-blue-600 text-white px-4 py-2 rounded-lg shadow hover:bg-blue-700 transition"
|
||||
onClick={handleOpenAdd}
|
||||
>
|
||||
<Plus size={20} /> Novo
|
||||
</button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{chargers.length > 0 ? (
|
||||
chargers.map((charger) => (
|
||||
<motion.div
|
||||
key={charger.id}
|
||||
className={`bg-white rounded-xl shadow-md border p-5 flex flex-col gap-2 cursor-pointer transition relative ${selectedCharger === charger.id ? 'border-blue-500 bg-blue-50' : ''}`}
|
||||
onClick={() => handleSelect(charger.id)}
|
||||
>
|
||||
<button
|
||||
className="absolute top-3 right-3 text-gray-400 hover:text-red-500 transition z-10"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDelete(charger.id);
|
||||
}}
|
||||
disabled={deletingId === charger.id}
|
||||
>
|
||||
{deletingId === charger.id ? <Loader2 className="animate-spin" size={18} /> : <Trash2 size={18} />}
|
||||
</button>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className={`inline-block w-2 h-2 rounded-full ${charger.status === 'online' ? 'bg-green-500' : charger.status === 'standby' ? 'bg-yellow-400' : 'bg-gray-400'}`} />
|
||||
<span className="font-semibold text-lg">{charger.nome}</span>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">Código: <span className="font-mono">{charger.codigoEmparelhamento}</span></div>
|
||||
<div className="text-xs text-gray-500">Local: {charger.local}</div>
|
||||
<div className="text-xs text-gray-400">Última atividade: {charger.ultimaAtividade}</div>
|
||||
</motion.div>
|
||||
))
|
||||
) : (
|
||||
<p className="text-gray-500">Nenhum carregador encontrado</p>
|
||||
)}
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{showAdd && <AddChargerModal onClose={handleCloseAdd} />}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AddChargerModal({ onClose }) {
|
||||
const [nome, setNome] = useState('');
|
||||
const [local, setLocal] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
setSaving(true);
|
||||
try {
|
||||
const res = await fetch('/api/chargers', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ nome, local })
|
||||
});
|
||||
const json = await res.json();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
console.error('Erro ao adicionar carregador', err);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 bg-black bg-opacity-40 z-50 flex items-center justify-center"
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.94, y: 40, opacity: 0 }}
|
||||
animate={{ scale: 1, y: 0, opacity: 1 }}
|
||||
exit={{ scale: 0.92, y: 40, opacity: 0 }}
|
||||
className="bg-white rounded-2xl shadow-xl p-8 w-full max-w-sm relative"
|
||||
>
|
||||
<button className="absolute top-3 right-3 text-gray-500 hover:text-gray-800" onClick={onClose} aria-label="Fechar">
|
||||
<X />
|
||||
</button>
|
||||
<h2 className="text-xl font-bold mb-4">Novo Carregador</h2>
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Nome</label>
|
||||
<input
|
||||
type="text"
|
||||
value={nome}
|
||||
onChange={(e) => setNome(e.target.value)}
|
||||
required
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Localização</label>
|
||||
<input
|
||||
type="text"
|
||||
value={local}
|
||||
onChange={(e) => setLocal(e.target.value)}
|
||||
required
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={saving}
|
||||
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-lg transition flex items-center justify-center gap-2"
|
||||
>
|
||||
{saving && <Loader2 className="animate-spin" size={18} />} Salvar
|
||||
</button>
|
||||
</form>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user