144 lines
8.3 KiB
TypeScript
144 lines
8.3 KiB
TypeScript
|
|
|
||
|
|
import React, { useState } from 'react';
|
||
|
|
import { Store, Plus, Edit2, Trash2, Truck, Phone, Star } from 'lucide-react';
|
||
|
|
import { useCRM } from '../context/CRMContext';
|
||
|
|
import { Supplier } from '../types';
|
||
|
|
|
||
|
|
const Suppliers: React.FC = () => {
|
||
|
|
const { suppliers, addSupplier, updateSupplier, deleteSupplier } = useCRM();
|
||
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||
|
|
const [editingId, setEditingId] = useState<string | null>(null);
|
||
|
|
const [formData, setFormData] = useState<Partial<Supplier>>({
|
||
|
|
name: '', contact: '', rating: 5
|
||
|
|
});
|
||
|
|
|
||
|
|
const handleSubmit = (e: React.FormEvent) => {
|
||
|
|
e.preventDefault();
|
||
|
|
if (editingId) {
|
||
|
|
updateSupplier(editingId, formData);
|
||
|
|
} else {
|
||
|
|
addSupplier(formData as any);
|
||
|
|
}
|
||
|
|
closeModal();
|
||
|
|
};
|
||
|
|
|
||
|
|
const openModal = (supplier?: Supplier) => {
|
||
|
|
if (supplier) {
|
||
|
|
setEditingId(supplier.id);
|
||
|
|
setFormData(supplier);
|
||
|
|
} else {
|
||
|
|
setEditingId(null);
|
||
|
|
setFormData({ name: '', contact: '', rating: 5 });
|
||
|
|
}
|
||
|
|
setIsModalOpen(true);
|
||
|
|
};
|
||
|
|
|
||
|
|
const closeModal = () => {
|
||
|
|
setIsModalOpen(false);
|
||
|
|
setEditingId(null);
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-8 animate-in fade-in duration-500">
|
||
|
|
{/* HEADER */}
|
||
|
|
<div className="flex justify-between items-center glass-card p-6 rounded-2xl border border-white/5 shadow-sm">
|
||
|
|
<div>
|
||
|
|
<h2 className="text-xl font-bold text-white tracking-tight">Parceiros Logísticos</h2>
|
||
|
|
<p className="text-xs text-slate-500 tracking-wide mt-1">Gestão de Transporte e Fornecedores</p>
|
||
|
|
</div>
|
||
|
|
<button
|
||
|
|
onClick={() => openModal()}
|
||
|
|
className="flex items-center gap-2 bg-indigo-600 hover:bg-indigo-500 text-white px-5 py-2.5 rounded-lg font-bold text-sm transition-all shadow-lg shadow-indigo-600/20 active:scale-95"
|
||
|
|
>
|
||
|
|
<Plus size={16} /> Novo Parceiro
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* LIST */}
|
||
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
|
|
{suppliers.map(s => (
|
||
|
|
<div key={s.id} className="glass-card p-6 rounded-2xl border border-white/5 shadow-sm flex flex-col justify-between group hover:border-indigo-500/50 transition-all min-h-[160px] relative overflow-hidden">
|
||
|
|
|
||
|
|
{/* Watermark Icon */}
|
||
|
|
<div className="absolute -bottom-4 -right-4 text-white/[0.02] transform -rotate-12 group-hover:scale-110 transition-transform duration-500">
|
||
|
|
<Truck size={100} />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-start justify-between mb-4 relative z-10">
|
||
|
|
<div className="flex items-center gap-4">
|
||
|
|
<div className="w-12 h-12 bg-indigo-500/10 text-indigo-400 rounded-xl flex items-center justify-center border border-indigo-500/20 group-hover:bg-indigo-600 group-hover:text-white transition-all shadow-lg">
|
||
|
|
<Truck size={20} />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<h4 className="text-base font-bold text-white max-w-[150px] truncate leading-tight" title={s.name}>{s.name}</h4>
|
||
|
|
<p className="text-[10px] text-slate-500 font-bold uppercase tracking-widest mt-0.5">Parceiro</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div className="flex gap-0.5">
|
||
|
|
{[1, 2, 3, 4, 5].map(star => (
|
||
|
|
<div key={star} className={`w-1 h-1 rounded-full ${star <= s.rating ? 'bg-amber-400' : 'bg-slate-700/50'}`}></div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="space-y-2 mb-4 relative z-10">
|
||
|
|
{s.contact && (
|
||
|
|
<div className="flex items-center gap-2 text-slate-400 text-xs font-mono bg-black/20 p-2 rounded-lg border border-white/5">
|
||
|
|
<Phone size={12} className="text-emerald-400" />
|
||
|
|
{s.contact}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="pt-3 border-t border-white/5 flex justify-end gap-2 relative z-10">
|
||
|
|
<button onClick={() => openModal(s)} className="p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-all"><Edit2 size={14} /></button>
|
||
|
|
<button onClick={() => deleteSupplier(s.id)} className="p-1.5 text-slate-500 hover:text-rose-400 hover:bg-rose-500/10 rounded transition-all"><Trash2 size={14} /></button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* MODAL */}
|
||
|
|
{isModalOpen && (
|
||
|
|
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||
|
|
<div className="bg-[#0F1115] border border-white/10 w-full max-w-md rounded-2xl p-6 shadow-2xl animate-in zoom-in-95 duration-200">
|
||
|
|
<h3 className="text-lg font-bold text-white mb-6 border-b border-white/5 pb-4">{editingId ? 'Editar Parceiro' : 'Novo Parceiro'}</h3>
|
||
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||
|
|
<div>
|
||
|
|
<label className="block text-[10px] font-bold text-slate-500 uppercase mb-1">Nome / Apelido</label>
|
||
|
|
<input required type="text" value={formData.name} onChange={e => setFormData({ ...formData, name: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" placeholder="Ex: João Freteiro" />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label className="block text-[10px] font-bold text-slate-500 uppercase mb-1">Contato (WhatsApp)</label>
|
||
|
|
<input type="text" value={formData.contact} onChange={e => setFormData({ ...formData, contact: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" placeholder="+55 45 9..." />
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label className="block text-[10px] font-bold text-slate-500 uppercase mb-1">Avaliação / Confiança (1-5)</label>
|
||
|
|
<div className="flex gap-4 mt-2">
|
||
|
|
{[1, 2, 3, 4, 5].map(star => (
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
key={star}
|
||
|
|
onClick={() => setFormData({ ...formData, rating: star })}
|
||
|
|
className={`p-2 rounded-lg transition-all ${star <= (formData.rating || 0) ? 'text-amber-400 bg-amber-400/10' : 'text-slate-600 bg-slate-800'}`}
|
||
|
|
>
|
||
|
|
<Star size={16} fill={star <= (formData.rating || 0) ? "currentColor" : "none"} />
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex gap-3 mt-8 pt-4 border-t border-white/5">
|
||
|
|
<button type="button" onClick={closeModal} className="flex-1 py-2 text-xs font-bold text-slate-400 hover:text-white transition-colors uppercase tracking-wide">Cancelar</button>
|
||
|
|
<button type="submit" className="flex-1 bg-indigo-600 hover:bg-indigo-500 text-white py-2 rounded-lg font-bold shadow-lg transition-all text-sm">Salvar</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default Suppliers;
|