258 lines
15 KiB
JavaScript
258 lines
15 KiB
JavaScript
import React, { useState, useMemo } from 'react';
|
|
import { useAutoLab } from '../hooks/useAutoLab';
|
|
// Importação local do ExcelTable para garantir isolamento total conforme Regra de Ouro
|
|
import ExcelTable from '../components/ExcelTable';
|
|
import { AutoLabDetailPanel } from '../components/AutoLabDetailPanel';
|
|
import {
|
|
Plus, FileText, ShoppingCart, Clock, ClipboardCheck,
|
|
User, Package, Calendar, Info, Mail, Phone,
|
|
Settings, Image, List, Save, Trash2, Tag, Hash, File
|
|
} from 'lucide-react';
|
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
|
import { Button } from "@/components/ui/button";
|
|
import { toast } from 'sonner';
|
|
|
|
export const VendasView = () => {
|
|
const { data: vendas, loading } = useAutoLab('vendas');
|
|
const [activeTab, setActiveTab] = useState('orcamentos');
|
|
const [selectedVenda, setSelectedVenda] = useState(null);
|
|
const [isPanelOpen, setIsPanelOpen] = useState(false);
|
|
|
|
const handleRowClick = (row) => {
|
|
setSelectedVenda(row);
|
|
setIsPanelOpen(true);
|
|
};
|
|
|
|
const vendasColumns = [
|
|
{ header: 'Cliente', field: 'cliente', width: '250px', className: 'font-bold' },
|
|
{ header: 'Data', field: 'data', width: '100px' },
|
|
{ header: 'Digitador', field: 'digitador', width: '100px' },
|
|
{ header: 'Email', field: 'email', width: '200px' },
|
|
{ header: 'Equipamento', field: 'equipamento', width: '150px' },
|
|
{ header: 'Frete', field: 'frete', width: '100px' },
|
|
{ header: 'Frota', field: 'frota', width: '80px' },
|
|
{ header: 'Hora', field: 'hora', width: '100px' },
|
|
{ header: 'Cód. Solicitação', field: 'id_solicitacao', width: '120px', className: 'font-bold text-emerald-600' },
|
|
{ header: 'Modelo', field: 'modelo', width: '100px' },
|
|
{ header: 'Placa', field: 'placa', width: '100px', className: 'font-mono' },
|
|
{ header: 'Prazo Entrega', field: 'prazo_entrega', width: '120px' },
|
|
{ header: 'Resp. Orç.', field: 'responsavel_orcamento', width: '120px' },
|
|
];
|
|
|
|
return (
|
|
<div className="flex flex-col h-full bg-white dark:bg-[#1b1b1b]">
|
|
{/* Header Toolbar */}
|
|
<div className="p-4 border-b dark:border-zinc-800 bg-slate-50/50 dark:bg-zinc-900/20 flex flex-wrap items-center justify-between gap-4">
|
|
<div className="flex items-center gap-4 flex-1">
|
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="bg-white dark:bg-zinc-900 p-1 rounded-xl border dark:border-zinc-800 shadow-sm">
|
|
<TabsList className="bg-transparent border-none gap-1 h-8">
|
|
<TabsTrigger value="orcamentos" className="data-[state=active]:bg-[#1b4332] data-[state=active]:text-white rounded-lg px-4 transition-all uppercase text-[10px] font-bold">
|
|
<FileText size={16} className="mr-2" /> Orçamentos
|
|
</TabsTrigger>
|
|
<TabsTrigger value="realizadas" className="data-[state=active]:bg-[#1b4332] data-[state=active]:text-white rounded-lg px-4 transition-all uppercase text-[10px] font-bold">
|
|
<ShoppingCart size={16} className="mr-2" /> Vendas Realizadas
|
|
</TabsTrigger>
|
|
<TabsTrigger value="os" className="data-[state=active]:bg-[#1b4332] data-[state=active]:text-white rounded-lg px-4 transition-all uppercase text-[10px] font-bold">
|
|
<ClipboardCheck size={16} className="mr-2" /> Ordem de Serviço
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
</Tabs>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3">
|
|
<Button className="bg-[#1b4332] hover:bg-[#2d6a4f] text-white rounded-xl px-4 py-2 font-bold shadow-lg shadow-emerald-900/10 flex items-center gap-2 uppercase text-[10px]">
|
|
<Plus size={18} /> Novo Orçamento
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Table Content */}
|
|
<div className="flex-1 overflow-hidden">
|
|
<ExcelTable
|
|
data={vendas}
|
|
loading={loading}
|
|
columns={vendasColumns}
|
|
onRowClick={handleRowClick}
|
|
rowKey="id"
|
|
/>
|
|
</div>
|
|
|
|
{/* Slide-over Detail Panel */}
|
|
<AutoLabDetailPanel
|
|
isOpen={isPanelOpen}
|
|
onClose={() => setIsPanelOpen(false)}
|
|
title={selectedVenda?.cliente || 'Detalhes da Venda'}
|
|
subtitle={`Solicitação #${selectedVenda?.id_solicitacao || ''}`}
|
|
status={selectedVenda?.status}
|
|
statusColor="bg-orange-500/10 text-orange-600"
|
|
actions={[
|
|
{ label: 'Salvar', onClick: () => toast.success('Venda Salva!'), variant: 'default', className: 'bg-[#1b4332] hover:bg-[#2d6a4f] text-white' },
|
|
{ label: 'Excluir', onClick: () => toast.error('Solicitação excluída'), isDestructive: true }
|
|
]}
|
|
tabs={[
|
|
{
|
|
id: 'detalhes',
|
|
label: 'Detalhes',
|
|
content: (
|
|
<div className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<DetailItem label="Aplicação:" value={selectedVenda?.aplicacao} />
|
|
<DetailItem label="Data:" value={selectedVenda?.data} />
|
|
<DetailItem label="Desconto:" value={selectedVenda?.desconto} />
|
|
<DetailItem label="Digitador:" value={selectedVenda?.digitador} />
|
|
<DetailItem label="Email:" value={selectedVenda?.email} />
|
|
<DetailItem label="Equipamento:" value={selectedVenda?.equipamento} />
|
|
<DetailItem label="Frete:" value={selectedVenda?.frete} />
|
|
<DetailItem label="Frota:" value={selectedVenda?.frota} />
|
|
<DetailItem label="Garantia:" value={selectedVenda?.garantia} />
|
|
<DetailItem label="Hora:" value={selectedVenda?.hora} />
|
|
<DetailItem label="Cod. Solicitação:" value={selectedVenda?.id_solicitacao} />
|
|
<DetailItem label="Cod. Cliente:" value={selectedVenda?.cod_cliente} />
|
|
<DetailItem label="Info:" value={selectedVenda?.info_cliente} />
|
|
<DetailItem label="Modelo:" value={selectedVenda?.modelo} />
|
|
<DetailItem label="Placa:" value={selectedVenda?.placa} />
|
|
<DetailItem label="Prazo Entrega:" value={selectedVenda?.prazo_entrega} />
|
|
<DetailItem label="Resp. Orç.:" value={selectedVenda?.responsavel_orcamento} />
|
|
</div>
|
|
<div className="mt-4 p-4 bg-slate-50 dark:bg-zinc-900 rounded-xl border dark:border-zinc-800">
|
|
<span className="text-[10px] font-bold uppercase text-slate-400">Descrição:</span>
|
|
<p className="text-sm mt-1">{selectedVenda?.descricao}</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
},
|
|
{
|
|
id: 'editar',
|
|
label: 'Editar',
|
|
content: (
|
|
<div className="space-y-6">
|
|
<Tabs defaultValue="base" className="w-full">
|
|
<TabsList className="bg-slate-100 dark:bg-zinc-800 p-1 rounded-lg w-full justify-start gap-1">
|
|
<TabsTrigger value="base" className="h-8 w-8 p-0 rounded-md data-[state=active]:bg-white dark:data-[state=active]:bg-zinc-700 shadow-sm"><User size={16} /></TabsTrigger>
|
|
<TabsTrigger value="config" className="h-8 w-8 p-0 rounded-md data-[state=active]:bg-white dark:data-[state=active]:bg-zinc-700 shadow-sm"><Settings size={16} /></TabsTrigger>
|
|
<TabsTrigger value="images" className="h-8 w-8 p-0 rounded-md data-[state=active]:bg-white dark:data-[state=active]:bg-zinc-700 shadow-sm"><Image size={16} /></TabsTrigger>
|
|
<TabsTrigger value="items" className="h-8 w-8 p-0 rounded-md data-[state=active]:bg-white dark:data-[state=active]:bg-zinc-700 shadow-sm"><List size={16} /></TabsTrigger>
|
|
</TabsList>
|
|
|
|
<div className="mt-6">
|
|
<TabsContent value="base" className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<EditField label="Data:" value={selectedVenda?.data} />
|
|
<EditField label="Hora:" value={selectedVenda?.hora} />
|
|
<EditField label="Digitador:" value={selectedVenda?.digitador} />
|
|
<EditField label="Resp. Orçamento:" value={selectedVenda?.responsavel_orcamento} />
|
|
<EditField label="Placa:" value={selectedVenda?.placa} />
|
|
<EditField label="Modelo:" value={selectedVenda?.modelo} />
|
|
<EditField label="Frota:" value={selectedVenda?.frota} />
|
|
<EditField label="Equipamento:" value={selectedVenda?.equipamento} />
|
|
<EditField label="Aplicação:" value={selectedVenda?.aplicacao} />
|
|
<div className="col-span-2">
|
|
<EditField label="Cliente:" value={selectedVenda?.cliente} />
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="config" className="space-y-4">
|
|
<div className="space-y-4">
|
|
<div className="space-y-1.5">
|
|
<label className="text-[10px] font-bold uppercase text-slate-400 ml-1">info_cliente:</label>
|
|
<textarea className="w-full bg-slate-50 dark:bg-zinc-950 border border-slate-200 dark:border-zinc-800 rounded-lg p-3 text-sm min-h-[100px]" defaultValue={selectedVenda?.info_cliente} />
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<label className="text-[10px] font-bold uppercase text-slate-400 ml-1">relatorio_oficina:</label>
|
|
<textarea className="w-full bg-slate-50 dark:bg-zinc-950 border border-slate-200 dark:border-zinc-800 rounded-lg p-3 text-sm min-h-[100px]" defaultValue={selectedVenda?.relatorio_oficina} />
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<label className="text-[10px] font-bold uppercase text-slate-400 ml-1">obs:</label>
|
|
<textarea className="w-full bg-slate-50 dark:bg-zinc-950 border border-slate-200 dark:border-zinc-800 rounded-lg p-3 text-sm min-h-[100px]" defaultValue={selectedVenda?.obs} />
|
|
</div>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="images" className="space-y-4">
|
|
<div className="border-2 border-dashed border-slate-200 dark:border-zinc-800 rounded-2xl p-8 text-center space-y-3">
|
|
<label className="cursor-pointer block">
|
|
<div className="bg-slate-100 dark:bg-zinc-800 w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-2 text-slate-400">
|
|
<Plus size={24} />
|
|
</div>
|
|
<span className="text-sm font-bold block">Anexar imagens</span>
|
|
<span className="text-xs text-slate-400">Arraste ou clique para selecionar</span>
|
|
<input type="file" className="hidden" />
|
|
</label>
|
|
</div>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="items" className="space-y-6">
|
|
<div className="bg-slate-50 dark:bg-zinc-900/50 p-4 rounded-xl border dark:border-zinc-800 space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<EditField label="Preço:" value="-- Escolha uma opção --" select />
|
|
<EditField label="Item:" placeholder="Pesquisar Item..." />
|
|
<EditField label="Cod Peças:" />
|
|
<EditField label="Qtd Peças:" />
|
|
<EditField label="Unid Peças:" value="-- Escolha uma opção --" select />
|
|
<EditField label="Modelo:" />
|
|
<EditField label="Marca:" />
|
|
<EditField label="Fornecedor:" />
|
|
<EditField label="Preço Unitário:" />
|
|
</div>
|
|
<Button className="w-full bg-emerald-600 hover:bg-emerald-700 text-white font-bold h-10 mt-2">
|
|
Inserir
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<h4 className="text-[10px] font-black uppercase text-slate-400 tracking-widest pl-1">Itens Adicionados</h4>
|
|
{selectedVenda?.items?.map(item => (
|
|
<div key={item.id} className="p-4 bg-emerald-500/5 border border-emerald-500/10 rounded-xl relative group">
|
|
<p className="text-xs font-bold text-emerald-800 dark:text-emerald-400 pr-8">{item.item}</p>
|
|
<div className="grid grid-cols-3 gap-2 mt-2 text-[10px] text-slate-500">
|
|
<span>Qtd: {item.qtd}</span>
|
|
<span>Preço: {item.preco}</span>
|
|
<span>Marca: {item.marca}</span>
|
|
</div>
|
|
<button className="absolute top-4 right-4 text-rose-500 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
<Trash2 size={16} />
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</TabsContent>
|
|
</div>
|
|
</Tabs>
|
|
</div>
|
|
)
|
|
}
|
|
]}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// Helper components
|
|
const DetailItem = ({ label, value }) => (
|
|
<div className="flex flex-col gap-0.5">
|
|
<span className="text-[10px] font-black uppercase text-slate-400 tracking-wider leading-none">{label}</span>
|
|
<span className="text-sm font-bold text-slate-800 dark:text-slate-100">{value || '---'}</span>
|
|
</div>
|
|
);
|
|
|
|
const EditField = ({ label, value, placeholder, select }) => (
|
|
<div className="space-y-1.5">
|
|
<label className="text-[10px] font-black uppercase text-slate-400 tracking-wider ml-1 leading-none">{label}</label>
|
|
{select ? (
|
|
<select className="w-full bg-slate-50 dark:bg-zinc-950 border border-slate-200 dark:border-zinc-800 rounded-lg px-3 h-10 text-xs font-semibold focus:ring-2 focus:ring-emerald-500/20 outline-none transition-all">
|
|
<option>{value}</option>
|
|
</select>
|
|
) : (
|
|
<input
|
|
type="text"
|
|
className="w-full bg-slate-50 dark:bg-zinc-950 border border-slate-200 dark:border-zinc-800 rounded-lg px-3 h-10 text-xs font-semibold placeholder:text-slate-400 focus:ring-2 focus:ring-emerald-500/20 outline-none transition-all"
|
|
defaultValue={value}
|
|
placeholder={placeholder}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
const Separator = () => <div className="h-px w-full bg-slate-100 dark:bg-zinc-800" />;
|