234 lines
16 KiB
TypeScript
234 lines
16 KiB
TypeScript
|
|
import React from 'react';
|
|
import { Search, Package, Plus, Calculator, Trash2, Minus, ShoppingCart, CheckCircle2, Facebook, Tag, ToggleLeft, ToggleRight } from 'lucide-react';
|
|
import { useCRM } from '../context/CRMContext';
|
|
import MarketplaceAnalytic from '../components/MarketplaceAnalytic';
|
|
|
|
const Sourcing: React.FC = () => {
|
|
const {
|
|
searchTerm, setSearchTerm, handleSearch, handleOpportunitySearch, products,
|
|
selectedProduct, setSelectedProduct, addToShoppingList,
|
|
shoppingList, removeFromShoppingList,
|
|
updateShoppingItemQuantity, saveOrderAsQuotation,
|
|
calculateShoppingTotals, overheadPercent,
|
|
useOverhead, setUseOverhead,
|
|
searchLoading, searchError, searchType, setSearchType
|
|
} = useCRM();
|
|
|
|
const onSearchSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (searchType === 'specific') {
|
|
handleSearch(e);
|
|
} else {
|
|
handleOpportunitySearch(searchTerm);
|
|
}
|
|
};
|
|
|
|
const handleUpdateQuantity = (item: any, delta: number) => {
|
|
updateShoppingItemQuantity(item.id, delta);
|
|
};
|
|
|
|
const handleRemoveItem = (id: string) => {
|
|
removeFromShoppingList(id);
|
|
};
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 xl:grid-cols-12 gap-8 items-start animate-in fade-in duration-500">
|
|
{/* COLUNA 1: BUSCA */}
|
|
<div className="xl:col-span-3 space-y-6">
|
|
<div className="bg-white/5 rounded-[32px] p-6 border border-white/5 shadow-sm backdrop-blur-sm">
|
|
{/* TABS */}
|
|
<div className="flex bg-slate-900/50 rounded-2xl p-1 mb-6 border border-white/5">
|
|
<button
|
|
onClick={() => setSearchType('specific')}
|
|
className={`flex-1 py-2 text-[10px] font-bold uppercase tracking-wider rounded-xl transition-all ${searchType === 'specific' ? 'bg-indigo-600 text-white shadow-lg' : 'text-slate-500 hover:text-slate-300'}`}
|
|
>
|
|
Busca Específica
|
|
</button>
|
|
<button
|
|
onClick={() => setSearchType('opportunity')}
|
|
className={`flex-1 py-2 text-[10px] font-bold uppercase tracking-wider rounded-xl transition-all ${searchType === 'opportunity' ? 'bg-emerald-600 text-white shadow-lg' : 'text-slate-500 hover:text-slate-300'}`}
|
|
>
|
|
Oportunidades
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2 mb-6">
|
|
{searchType === 'specific' ? <Search size={16} className="text-indigo-400" /> : <Tag size={16} className="text-emerald-400" />}
|
|
<h2 className="text-[11px] font-bold text-slate-500 uppercase tracking-widest">
|
|
{searchType === 'specific' ? 'Sourcing Real-Time' : 'Caçador de Margem (>25%)'}
|
|
</h2>
|
|
</div>
|
|
|
|
<form onSubmit={onSearchSubmit} className="relative group mb-8">
|
|
<input
|
|
type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)}
|
|
placeholder={searchType === 'specific' ? "Ex: iPhone 16 Pro Max" : "Ex: Celulares, Drones, Games"}
|
|
className={`w-full bg-slate-900/50 border border-white/10 rounded-2xl py-4 pl-12 pr-4 text-sm font-semibold outline-none transition-all text-white placeholder-slate-600 shadow-inner ${searchType === 'specific' ? 'focus:border-indigo-500' : 'focus:border-emerald-500'}`}
|
|
/>
|
|
<Search className={`absolute left-4 top-1/2 -translate-y-1/2 transition-colors ${searchType === 'specific' ? 'text-slate-500 group-focus-within:text-indigo-500' : 'text-slate-500 group-focus-within:text-emerald-500'}`} size={20} />
|
|
</form>
|
|
|
|
{searchError && (
|
|
<div className="mb-6 p-4 bg-rose-500/10 border border-rose-500/20 rounded-2xl flex items-center gap-3 text-rose-400">
|
|
<div className="w-2 h-2 bg-rose-500 rounded-full animate-pulse"></div>
|
|
<p className="text-xs font-bold uppercase tracking-wide">{searchError}</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="space-y-4 max-h-[600px] overflow-y-auto pr-2 custom-scrollbar">
|
|
{searchLoading ? (
|
|
<div className="py-20 text-center animate-pulse">
|
|
<div className="w-10 h-10 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
|
<p className="text-[10px] font-bold text-slate-500 uppercase">Consultando Compras Paraguai...</p>
|
|
</div>
|
|
) : products.map((p, idx) => (
|
|
<div
|
|
key={idx}
|
|
onClick={() => setSelectedProduct(p)}
|
|
className={`p-5 rounded-3xl border-2 cursor-pointer transition-all hover:scale-[1.02] active:scale-95 ${selectedProduct?.name === p.name
|
|
? 'border-indigo-500 bg-indigo-500/10 shadow-md'
|
|
: 'border-white/5 bg-white/5 hover:border-white/10 hover:bg-white/10'
|
|
}`}
|
|
>
|
|
<div className="flex justify-between items-start">
|
|
<span className="text-[8px] font-bold uppercase bg-slate-800 border border-slate-700 px-2 py-0.5 rounded-lg text-slate-400">{p.store}</span>
|
|
</div>
|
|
<h4 className="text-sm font-bold text-slate-200 leading-tight mt-3 mb-4 line-clamp-2">{p.name}</h4>
|
|
<div className="flex items-end justify-between">
|
|
<div>
|
|
<p className="text-xl font-bold text-white tracking-tight">US$ {p.priceUSD.toFixed(2)}</p>
|
|
<p className="text-[10px] font-bold text-slate-500 uppercase">R$ {p.priceBRL.toLocaleString('pt-BR')}</p>
|
|
</div>
|
|
<div className="flex flex-col items-end gap-2">
|
|
{p.salesVolume && (
|
|
<span className="text-[9px] font-bold uppercase tracking-widest bg-emerald-500/10 text-emerald-400 px-2 py-1 rounded-lg border border-emerald-500/20 shadow-[0_0_10px_rgba(16,185,129,0.1)]">
|
|
{p.salesVolume}
|
|
</span>
|
|
)}
|
|
<button
|
|
onClick={(e) => { e.stopPropagation(); addToShoppingList(p); }}
|
|
className="p-3 bg-indigo-600 text-white rounded-2xl hover:bg-indigo-500 transition-all shadow-lg active:scale-90"
|
|
>
|
|
<Plus size={18} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{!searchLoading && products.length === 0 && (
|
|
<div className="py-24 text-center opacity-20">
|
|
<Package size={48} strokeWidth={1.5} className="mx-auto mb-4 text-slate-400" />
|
|
<p className="text-[10px] font-bold text-slate-500 uppercase tracking-widest">Aguardando seu input</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* COLUNA 2: ANÁLISE DETALHADA */}
|
|
<div className="xl:col-span-6">
|
|
{selectedProduct ? (
|
|
<MarketplaceAnalytic product={selectedProduct} overheadPercent={overheadPercent} useOverhead={useOverhead} />
|
|
) : (
|
|
<div className="bg-white/5 rounded-[40px] border border-white/5 shadow-sm min-h-[600px] flex flex-col items-center justify-center p-20 text-center backdrop-blur-sm">
|
|
<div className="w-24 h-24 bg-slate-800/50 rounded-[32px] flex items-center justify-center mb-8 border border-white/5 shadow-inner">
|
|
<Calculator size={40} className="text-slate-600" />
|
|
</div>
|
|
<h3 className="text-2xl font-bold text-white mb-4 tracking-tight">Analítica de Arbitragem</h3>
|
|
<p className="text-slate-500 max-w-sm text-sm font-medium leading-relaxed">Selecione um produto para ver a comparação de margem real entre os marketplaces do Brasil e sua venda direta no Facebook.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* COLUNA 3: COTAÇÃO / CRM CHECKOUT */}
|
|
<div className="xl:col-span-3">
|
|
<div className="bg-white/5 rounded-[40px] border border-white/5 shadow-sm flex flex-col min-h-[700px] sticky top-[120px] overflow-hidden backdrop-blur-sm">
|
|
<div className="p-8 border-b border-white/5 flex justify-between items-center bg-white/5">
|
|
<div>
|
|
<h2 className="text-sm font-bold text-white uppercase tracking-widest leading-none">Minha Cotação</h2>
|
|
<p className="text-[9px] font-bold text-indigo-400 uppercase mt-1">Sourcing Ativo</p>
|
|
</div>
|
|
<span className="w-9 h-9 bg-indigo-600 text-white text-xs font-bold rounded-xl flex items-center justify-center shadow-lg shadow-indigo-500/30">{shoppingList.length}</span>
|
|
</div>
|
|
|
|
<div className="flex-grow p-6 space-y-4 overflow-y-auto max-h-[450px] custom-scrollbar">
|
|
{shoppingList.length > 0 ? (
|
|
shoppingList.map(item => (
|
|
<div key={item.id} className="p-5 bg-slate-900/50 rounded-3xl border border-white/5 group transition-all hover:border-indigo-500/30 hover:shadow-md">
|
|
<div className="flex justify-between items-start mb-4">
|
|
<div className="flex-grow pr-4">
|
|
<span className="text-[9px] font-bold text-slate-500 uppercase tracking-wider">{item.store}</span>
|
|
<h4 className="text-xs font-bold text-slate-200 leading-tight mt-1">{item.name}</h4>
|
|
</div>
|
|
<button onClick={() => handleRemoveItem(item.id)} className="text-slate-600 hover:text-rose-500 transition-colors">
|
|
<Trash2 size={16} />
|
|
</button>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-sm font-bold text-white">US$ {item.priceUSD.toFixed(2)}</p>
|
|
<div className="flex items-center gap-3 bg-black/20 rounded-xl p-1 border border-white/5">
|
|
<button onClick={() => handleUpdateQuantity(item, -1)} className="p-1.5 hover:bg-white/10 rounded-lg text-slate-400"><Minus size={12} /></button>
|
|
<span className="text-xs font-bold text-slate-300 w-5 text-center">{item.quantity}</span>
|
|
<button onClick={() => handleUpdateQuantity(item, 1)} className="p-1.5 hover:bg-white/10 rounded-lg text-slate-400"><Plus size={12} /></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="py-24 text-center opacity-10">
|
|
<ShoppingCart size={64} strokeWidth={1.5} className="mx-auto text-white" />
|
|
<p className="text-[10px] font-bold text-white uppercase mt-4 tracking-widest">Carrinho Vazio</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="p-8 bg-slate-900 text-white rounded-t-[40px] space-y-5 border-t border-white/5">
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between items-center opacity-80 hover:opacity-100 transition-opacity">
|
|
<button
|
|
onClick={() => setUseOverhead(!useOverhead)}
|
|
className="flex items-center gap-2 cursor-pointer group"
|
|
title={useOverhead ? "Desativar Taxas (20%)" : "Ativar Taxas (20%)"}
|
|
>
|
|
{useOverhead ? (
|
|
<ToggleRight size={20} className="text-indigo-400 group-hover:text-indigo-300 transition-colors" />
|
|
) : (
|
|
<ToggleLeft size={20} className="text-slate-500 group-hover:text-slate-400 transition-colors" />
|
|
)}
|
|
<span className={`text-[10px] font-bold uppercase tracking-widest ${useOverhead ? 'text-indigo-300' : 'text-slate-500'} transition-colors`}>
|
|
Custo Final Est. ({useOverhead ? 'c/ Taxas' : 's/ Taxas'})
|
|
</span>
|
|
</button>
|
|
<span className={`text-sm font-bold ${useOverhead ? 'text-indigo-100' : 'text-slate-500'} transition-colors`}>
|
|
R$ {calculateShoppingTotals().totalCostWithOverhead.toLocaleString('pt-BR')}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between items-center opacity-60">
|
|
<span className="text-[10px] font-bold uppercase tracking-widest">Subtotal Paraguai</span>
|
|
<span className="text-sm font-bold">US$ {calculateShoppingTotals().totalUSD.toLocaleString('en-US')}</span>
|
|
</div>
|
|
<div className="pt-4 border-t border-white/10 flex justify-between items-center">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-[11px] font-bold text-emerald-400 uppercase tracking-widest">Lucro Est. Final</span>
|
|
<Facebook size={12} className="text-emerald-400" />
|
|
</div>
|
|
<span className="text-2xl font-bold text-emerald-400">R$ {calculateShoppingTotals().totalApproxProfit.toLocaleString('pt-BR')}</span>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={saveOrderAsQuotation}
|
|
disabled={shoppingList.length === 0}
|
|
className="w-full bg-indigo-600 text-white font-bold py-5 rounded-[24px] hover:bg-indigo-500 transition-all flex items-center justify-center gap-2 disabled:opacity-20 active:scale-95 shadow-xl shadow-indigo-900/40"
|
|
>
|
|
<CheckCircle2 size={20} />
|
|
SALVAR NO CRM
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Sourcing;
|